mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-15 23:12:08 +00:00
GUI_App::on_init_inner() carried the plugin dispatch policy inline (the
capability resolver and the slicing-pipeline dispatcher) and would grow
with every capability that fires from inside libslic3r.
plugin/PluginHooks.{hpp,cpp} now owns one file-local installer per hook,
aggregated by plugin_hooks::install() -- called from
PluginManager::initialize(), reset in shutdown() so no hook can enter
Python after the interpreter finalizes. The wx-side loader subscriptions
move into GUI_App::init_plugin_gui_wiring().
No behavior change; dispatch bodies moved verbatim.
130 lines
6.9 KiB
C++
130 lines
6.9 KiB
C++
#include "PluginHooks.hpp"
|
|
|
|
#include "PluginManager.hpp"
|
|
#include "PythonInterpreter.hpp"
|
|
#include "PythonPluginInterface.hpp"
|
|
#include "pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.hpp"
|
|
|
|
#include "libslic3r/Config.hpp"
|
|
#include "libslic3r/Exception.hpp"
|
|
#include "libslic3r/Print.hpp"
|
|
#include "libslic3r_version.h"
|
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
namespace Slic3r::plugin_hooks {
|
|
namespace {
|
|
|
|
// Manifest resolver: turns the bare capability name a preset stores into the full
|
|
// "name;uuid;capability" reference the dispatchers consume (see
|
|
// ConfigBase::collect_plugin_manifest / update_plugin_manifest).
|
|
void install_capability_resolver()
|
|
{
|
|
ConfigBase::set_resolve_capability_fn([](const std::string& cap_name, const std::string& cap_type) {
|
|
PluginManager& plugin_mgr = PluginManager::instance();
|
|
auto plugin_cap = plugin_mgr.get_loader().try_get_plugin_capability_by_name_and_type(cap_name, plugin_capability_type_from_string(cap_type));
|
|
if (!plugin_cap)
|
|
return std::string();
|
|
|
|
PluginDescriptor descriptor;
|
|
if (!plugin_mgr.get_catalog().try_get_plugin_descriptor(plugin_cap->plugin_key, descriptor))
|
|
return std::string();
|
|
|
|
// Cloud plugins are resolved at runtime via the UUID in the middle field, so the first
|
|
// field keeps the friendly display name. Local plugins are looked up by plugin_key (the
|
|
// first field, with an empty UUID), so emit the plugin_key to keep them resolvable.
|
|
const std::string identity = descriptor.is_cloud_plugin() ? descriptor.name : descriptor.plugin_key;
|
|
return identity + ';' + descriptor.cloud_uuid() + ';' + cap_name;
|
|
});
|
|
}
|
|
|
|
// Print::process() fires this hook at each pipeline seam on the slicing worker
|
|
// thread; here we run the picker-selected SlicingPipeline capabilities. Per
|
|
// capability we acquire the GIL, honor cancellation, and convert a plugin
|
|
// failure into a (non-critical) SlicingError so it surfaces as a slicing-error
|
|
// notification rather than the fatal-crash dialog.
|
|
void install_slicing_pipeline_hook()
|
|
{
|
|
Print::set_slicing_pipeline_hook_fn(
|
|
[](Print& print, const PrintObject* object, SlicingPipelineStepPlugin step) {
|
|
const auto* caps = print.config().option<ConfigOptionStrings>("slicing_pipeline_plugin");
|
|
// `plugins` is a dynamic-only manifest key (not a static PrintConfig member), so it
|
|
// must be read from the full/dynamic config -- reading it off print.config() (the
|
|
// static PrintConfig) always yields nullptr and skips every capability. Mirrors the
|
|
// post-process path (PostProcessor.cpp, via BackgroundSlicingProcess::full_print_config()).
|
|
const auto* plugs = print.full_print_config().option<ConfigOptionStrings>("plugins");
|
|
if (caps == nullptr || caps->values.empty())
|
|
return;
|
|
|
|
execute_capabilities_from_refs<SlicingPipelinePluginCapability>(
|
|
*caps, plugs, PluginCapabilityType::SlicingPipeline,
|
|
[&](std::shared_ptr<SlicingPipelinePluginCapability> cap, const PluginCapabilityRef& ref) {
|
|
ExecutionResult r;
|
|
try {
|
|
// GIL is acquired per capability (not once for the whole dispatch) so it
|
|
// is released between capabilities.
|
|
PythonGILState gil;
|
|
// throw_if_canceled() is protected on PrintBase; canceled() is the public
|
|
// equivalent check (same cancel flag), so honor cancellation via it.
|
|
if (print.canceled())
|
|
throw CanceledException();
|
|
SlicingPipelineContext ctx;
|
|
ctx.orca_version = SoftFever_VERSION;
|
|
ctx.step = step;
|
|
ctx.print = &print;
|
|
ctx.object = object;
|
|
// hand the plugin its own [tool.orcaslicer.plugin.settings] as ctx.params
|
|
// (same plugin_key the capability was resolved by, so it always matches).
|
|
const std::string plugin_key = ref.uuid.empty() ? ref.name : ref.uuid;
|
|
ctx.params = PluginManager::instance().get_loader().get_plugin_settings(plugin_key);
|
|
r = cap->execute(ctx);
|
|
} catch (const CanceledException&) {
|
|
throw; // cancellation must reach process(), never become a slicing error
|
|
} catch (const std::exception& ex) {
|
|
// A Python raise reaches here as pybind11::error_already_set; surface it as a
|
|
// (non-critical) slicing error instead of a crash.
|
|
throw SlicingError(std::string("Slicing pipeline plugin '") +
|
|
ref.capability_name + "' error: " + ex.what());
|
|
}
|
|
if (r.status == PluginResult::FatalError)
|
|
throw SlicingError(std::string("Slicing pipeline plugin '") +
|
|
ref.capability_name + "' error: " + r.message);
|
|
// log a non-empty success/skipped message instead of dropping it. This is
|
|
// log-only by design: every pipeline hook fires AFTER set_done() (see Print.cpp),
|
|
// so the Print-level m_step_active is -1 here. Calling active_step_add_warning()
|
|
// would then index m_state[-1] (out-of-bounds; the guarding assert is compiled
|
|
// out in Release), so it must NOT be called from a pipeline hook.
|
|
if (!r.message.empty()) {
|
|
static const char* const kStepNames[] = {
|
|
"posSlice", "posPerimeters", "posEstimateCurledExtrusions", "posPrepareInfill", "posInfill",
|
|
"posIroning", "posContouring", "posSupportMaterial", "posDetectOverhangsForLift",
|
|
"posSimplifyPath", "psWipeTower", "psSkirtBrim", "psGCodePostProcess"
|
|
}; // order must match SlicingPipelineStepPlugin
|
|
const char* step_name = static_cast<size_t>(step) < sizeof(kStepNames) / sizeof(kStepNames[0])
|
|
? kStepNames[static_cast<int>(step)] : "Unknown";
|
|
BOOST_LOG_TRIVIAL(info) << "Slicing pipeline plugin '" << ref.capability_name
|
|
<< "' [" << step_name << "]: " << r.message;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void install()
|
|
{
|
|
install_capability_resolver();
|
|
install_slicing_pipeline_hook();
|
|
}
|
|
|
|
void uninstall()
|
|
{
|
|
ConfigBase::set_resolve_capability_fn(nullptr);
|
|
Print::set_slicing_pipeline_hook_fn(nullptr);
|
|
}
|
|
|
|
} // namespace Slic3r::plugin_hooks
|