Files
OrcaSlicer/src/slic3r/Utils/NetworkAgentFactory.cpp
T
HanifKoh 4895bc03b4 Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced (#16099)
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced

Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes.

clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer.

* Remove Unused Project Includes From Files With Platform-Specific Code

A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block.

* Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass

The platform-file pass treated pchheader.hpp as an ordinary header and
emptied it, and left GUI_Preview.hpp and 14 other files relying on
headers they no longer reached directly.

* Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call

* Include Headers That Files Reached Through Ones the Cleanup Removed

* Drop Includes Duplicated by the Cleanup or by Main's Own Additions

* Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
2026-10-05 16:47:17 +08:00

505 lines
22 KiB
C++

#include "NetworkAgentFactory.hpp"
#include "CloudProvider.hpp"
#include "IPrinterAgent.hpp"
#include "ICloudServiceAgent.hpp"
#include "BBLPrinterAgent.hpp"
#include "OrcaPrinterAgent.hpp"
#include "QidiPrinterAgent.hpp"
#include "SnapmakerPrinterAgent.hpp"
#include "MoonrakerPrinterAgent.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "slic3r/plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.hpp"
#include "CrealityPrintAgent.hpp"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <chrono>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include <slic3r/GUI/GUI_App.hpp>
#include <slic3r/GUI/I18N.hpp>
#include <slic3r/plugin/PluginDescriptor.hpp>
#include <slic3r/plugin/PythonPluginInterface.hpp>
#include <vector>
#include <wx/app.h>
#include <wx/msgdlg.h>
#include <wx/string.h>
#include "libslic3r/AppConfig.hpp"
namespace Slic3r {
namespace {
static std::mutex s_registry_mutex;
struct PythonPrinterAgentRegistrationToken {};
static std::map<std::pair<std::string, std::string>, std::shared_ptr<PythonPrinterAgentRegistrationToken>>
s_python_printer_agent_registration_tokens;
std::map<std::string, PrinterAgentInfo>& get_printer_agents()
{
static std::map<std::string, PrinterAgentInfo> agents;
return agents;
}
std::map<std::string, std::shared_ptr<IPrinterAgent>>& get_printer_agent_cache()
{
static std::map<std::string, std::shared_ptr<IPrinterAgent>> cache;
return cache;
}
std::map<std::pair<std::string, std::string>, std::string>& get_python_printer_agent_ids()
{
static std::map<std::pair<std::string, std::string>, std::string> capability_to_agent_id;
return capability_to_agent_id;
}
std::string plugin_printer_agent_full_ref(const PluginDescriptor& descriptor, const std::string& capability_name)
{
const std::string identity = descriptor.is_cloud_plugin() ? descriptor.name : descriptor.plugin_key;
return identity + ';' + descriptor.cloud_uuid() + ';' + capability_name;
}
// Helper to register a printer agent type with the standard factory pattern.
// AgentTypes that take a log_dir constructor arg use the default; BBLPrinterAgent
// (no log_dir) is registered separately.
template<typename T> void register_agent()
{
auto info = T::get_agent_info_static();
NetworkAgentFactory::register_printer_agent(info.id, info.name,
[](std::shared_ptr<ICloudServiceAgent> cloud_agent,
const std::string& log_dir) -> std::shared_ptr<IPrinterAgent> {
auto agent = std::make_shared<T>(log_dir);
if (cloud_agent)
agent->set_cloud_agent(cloud_agent);
return agent;
});
}
} // anonymous namespace
bool NetworkAgentFactory::register_printer_agent(const std::string& id, const std::string& display_name, PrinterAgentFactory factory)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
return agents.emplace(id, PrinterAgentInfo(id, display_name, std::move(factory))).second;
}
bool NetworkAgentFactory::is_printer_agent_registered(const std::string& id)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
return agents.find(id) != agents.end();
}
const PrinterAgentInfo* NetworkAgentFactory::get_printer_agent_info(const std::string& id)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
auto it = agents.find(id);
return (it != agents.end()) ? &it->second : nullptr;
}
std::string NetworkAgentFactory::get_printer_agent_plugin_identifier(const std::string& id)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
auto it = agents.find(id);
return (it != agents.end() && it->second.is_plugin()) ? it->second.plugin_identifier : std::string();
}
std::vector<PrinterAgentInfo> NetworkAgentFactory::get_registered_printer_agents()
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
std::vector<PrinterAgentInfo> result;
result.reserve(agents.size());
for (const auto& pair : agents) {
if (!pair.second.is_plugin())
result.push_back(pair.second);
}
for (const auto& pair : agents) {
if (pair.second.is_plugin())
result.push_back(pair.second);
}
return result;
}
std::shared_ptr<IPrinterAgent> NetworkAgentFactory::create_printer_agent_by_id(const std::string& id,
std::shared_ptr<ICloudServiceAgent> cloud_agent,
const std::string& log_dir)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
// Check cache first
auto& cache = get_printer_agent_cache();
auto cache_it = cache.find(id);
if (cache_it != cache.end()) {
BOOST_LOG_TRIVIAL(info) << "Reusing cached printer agent: " << id;
if (cloud_agent)
cache_it->second->set_cloud_agent(cloud_agent);
return cache_it->second;
}
// Not cached — create via factory
auto& agents = get_printer_agents();
auto it = agents.find(id);
if (it == agents.end()) {
BOOST_LOG_TRIVIAL(warning) << "Unknown printer agent ID: " << id;
return nullptr;
}
auto agent = it->second.factory(cloud_agent, log_dir);
if (agent) {
BOOST_LOG_TRIVIAL(info) << "Created and cached printer agent: " << id;
cache[id] = agent;
}
return agent;
}
void NetworkAgentFactory::clear_printer_agent_cache()
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& cache = get_printer_agent_cache();
for (auto& pair : cache) {
if (pair.second)
pair.second->disconnect_printer();
}
cache.clear();
BOOST_LOG_TRIVIAL(info) << "Printer agent cache cleared";
}
void NetworkAgentFactory::register_all_agents()
{
register_agent<OrcaPrinterAgent>();
register_agent<QidiPrinterAgent>();
register_agent<SnapmakerPrinterAgent>();
register_agent<CrealityPrintAgent>(); // Must come BEFORE MoonrakerPrinterAgent —
// CrealityPrintAgent extends Moonraker behaviour
// for K-series boards with CFS support.
register_agent<MoonrakerPrinterAgent>();
// Keep BBL as a built-in option. Python printer-agent plugins with the
// same AgentInfo ID are listed separately under the plugin registry key.
{
auto info = BBLPrinterAgent::get_agent_info_static();
register_printer_agent(info.id, info.name,
[](std::shared_ptr<ICloudServiceAgent> cloud_agent,
const std::string& /*log_dir*/) -> std::shared_ptr<IPrinterAgent> {
auto agent = std::make_shared<BBLPrinterAgent>();
if (cloud_agent)
agent->set_cloud_agent(cloud_agent);
return agent;
});
}
}
std::unique_ptr<NetworkAgent> create_agent_from_config(const std::string& log_dir, AppConfig* app_config)
{
if (!app_config)
return std::make_unique<NetworkAgent>(nullptr, nullptr);
// Always create Orca cloud agent as the primary provider
auto cloud_agent = NetworkAgentFactory::create_cloud_agent(ORCA_CLOUD_PROVIDER, log_dir);
if (!cloud_agent) {
BOOST_LOG_TRIVIAL(error) << "Failed to create cloud agent";
}
auto agent = std::make_unique<NetworkAgent>(std::move(cloud_agent), nullptr);
if (agent) {
// create orca cloud agent first
auto* orca_cloud = dynamic_cast<OrcaCloudServiceAgent*>(agent->get_cloud_agent().get());
if (orca_cloud) {
orca_cloud->configure_urls(app_config);
}
// Initialize third-party cloud agents from config
auto providers = app_config->get_cloud_providers();
for (const auto& provider : providers) {
if (provider == ORCA_CLOUD_PROVIDER)
continue; // Primary agent already created above
auto third_party_agent = NetworkAgentFactory::create_cloud_agent(provider, log_dir);
if (third_party_agent) {
agent->add_cloud_agent(provider, std::move(third_party_agent));
BOOST_LOG_TRIVIAL(info) << "Initialized third-party cloud agent: " << provider;
}
}
}
return agent;
}
void NetworkAgentFactory::register_python_plugin(const std::string& plugin_key)
{
for (const auto& capability : PluginManager::instance().get_plugin_capabilities(plugin_key, PluginCapabilityType::PrinterConnection,
/*only_enabled=*/true))
register_python_printer_agent(plugin_key, capability->name());
}
void NetworkAgentFactory::register_python_printer_agent(const std::string& plugin_key, const std::string& capability_name)
{
PluginManager& plugin_manager = PluginManager::instance();
auto cap = plugin_manager.get_plugin_capability({PluginCapabilityType::PrinterConnection, capability_name, plugin_key},
/*only_enabled=*/false);
if (!cap) {
BOOST_LOG_TRIVIAL(warning) << "Printer-agent capability '" << capability_name << "' not found for plugin '" << plugin_key << "'";
return;
}
PluginDescriptor descriptor;
if (!plugin_manager.try_get_plugin_descriptor(plugin_key, descriptor)) {
BOOST_LOG_TRIVIAL(warning) << "Could not find descriptor for plugin key: '" << plugin_key;
return;
}
if (!cap->is_enabled()) {
BOOST_LOG_TRIVIAL(warning) << "Printer-agent capability '" << capability_name << "' is disabled for plugin '" << plugin_key << "'";
return;
}
auto plugin = std::dynamic_pointer_cast<PrinterAgentPluginCapability>(cap);
if (!plugin) {
BOOST_LOG_TRIVIAL(warning) << "Loaded plugin capability '" << capability_name << "' is not a printer-agent plugin";
return;
}
auto info = plugin->get_agent_info();
if (info.id.empty()) {
BOOST_LOG_TRIVIAL(warning) << "Printer-agent plugin '" << capability_name << "' returned an empty agent ID";
return;
}
const auto capability_key = std::make_pair(plugin_key, capability_name);
std::shared_ptr<PythonPrinterAgentRegistrationToken> registration_token;
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto token_it = std::find_if(s_python_printer_agent_registration_tokens.begin(), s_python_printer_agent_registration_tokens.end(),
[&plugin_key, &capability_name](const auto& entry) {
return entry.first.first == plugin_key && entry.first.second == capability_name;
});
if (token_it == s_python_printer_agent_registration_tokens.end()) {
registration_token = std::make_shared<PythonPrinterAgentRegistrationToken>();
s_python_printer_agent_registration_tokens.emplace(capability_key, registration_token);
} else {
registration_token = token_it->second;
}
}
const std::string plugin_full_ref = plugin_printer_agent_full_ref(descriptor, capability_name);
std::weak_ptr<PrinterAgentPluginCapability> weak_plugin = plugin;
PrinterAgentFactory factory = [weak_plugin](std::shared_ptr<ICloudServiceAgent> cloud_agent,
const std::string& /*log_dir*/) -> std::shared_ptr<IPrinterAgent> {
// The capability implements IPrinterAgent directly, so it is handed out as the
// live agent (no adapter); the Python plugin services the calls.
auto plugin = weak_plugin.lock();
if (!plugin)
return nullptr;
if (cloud_agent)
plugin->set_cloud_agent(cloud_agent);
return plugin;
};
// Python work above may allow disable/unload/reload to replace this capability.
// Re-resolve without holding the registry mutex to avoid lock inversion and reentrancy.
// only_enabled defaults to true here, so a capability that changed identity (reload) or was
// merely disabled both surface the same way: current_cap no longer equals cap.
auto current_cap = plugin_manager.get_plugin_capability({PluginCapabilityType::PrinterConnection, capability_name, plugin_key});
if (current_cap != cap) {
BOOST_LOG_TRIVIAL(debug) << "Printer-agent capability '" << capability_name << "' from plugin '" << plugin_key
<< "' changed or was disabled during registration";
return;
}
std::shared_ptr<IPrinterAgent> cached_agent;
auto reject_conflicting_capability = [plugin_key, capability_name](const std::string& error_message) {
if (!wxTheApp || GUI::wxGetApp().is_closing())
return;
GUI::wxGetApp().CallAfter([plugin_key, capability_name, error_message]() {
if (GUI::wxGetApp().is_closing())
return;
PluginManager& manager = PluginManager::instance();
manager.set_plugin_error(plugin_key, error_message);
// note: the unload callback triggered by disabling will call deregister,
// which will be a no-op since the printer agent is never registered
manager.set_capability_enabled({PluginCapabilityType::PrinterConnection, capability_name, plugin_key}, false);
wxMessageBox(wxString::FromUTF8(error_message.c_str()), _L("Plugins"), wxOK | wxICON_WARNING, GUI::wxGetApp().GetTopWindow());
});
};
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto token_it = std::find_if(s_python_printer_agent_registration_tokens.begin(), s_python_printer_agent_registration_tokens.end(),
[&plugin_key, &capability_name](const auto& entry) {
return entry.first.first == plugin_key && entry.first.second == capability_name;
});
const bool cap_still_enabled = cap->is_enabled();
if (token_it == s_python_printer_agent_registration_tokens.end() || token_it->second != registration_token ||
!cap_still_enabled) {
BOOST_LOG_TRIVIAL(debug) << "Printer-agent capability '" << capability_name << "' from plugin '" << plugin_key
<< "' was disabled before registry commit";
return;
}
auto& python_agent_ids = get_python_printer_agent_ids();
for (const auto& pair : python_agent_ids) {
if (pair.first != capability_key && pair.second == info.id) {
const std::string error_message = "Printer-agent '" + info.name + "' could not be enabled: agent ID '" + info.id +
"' is already registered by capability '" + pair.first.second + "' from plugin '" + pair.first.first + "'.";
BOOST_LOG_TRIVIAL(warning) << error_message;
reject_conflicting_capability(error_message);
return;
}
}
auto plugin_it = python_agent_ids.find(capability_key);
if (plugin_it != python_agent_ids.end() && plugin_it->second != info.id) {
const std::string old_agent_id = plugin_it->second;
get_printer_agents().erase(old_agent_id);
auto& cache = get_printer_agent_cache();
auto cache_it = cache.find(old_agent_id);
if (cache_it != cache.end()) {
cached_agent = std::move(cache_it->second);
cache.erase(cache_it);
}
}
auto& agents = get_printer_agents();
auto agent_it = agents.find(info.id);
// why: reject only when the ID is owned by SOMEONE ELSE - a built-in has an empty
// plugin_identifier, another plugin/capability has a different plugin_full_ref. When it
// IS the same plugin_full_ref, this capability is just re-registering itself, so fall
// through and refresh.
if (agent_it != agents.end() && agent_it->second.plugin_identifier != plugin_full_ref) {
const std::string error_message = "Printer-agent '" + info.name + "' could not be enabled: agent ID '" + info.id +
"' is already registered by '" + agent_it->second.display_name + "'.";
BOOST_LOG_TRIVIAL(warning) << error_message;
reject_conflicting_capability(error_message);
return;
}
// why: insert_or_assign, not emplace - reaching here means the ID is new, or the same
// capability is re-registering (same plugin_full_ref). In the re-register case we WANT to
// overwrite so the factory closure points at the current live capability instance; emplace
// would silently keep the stale entry.
agents.insert_or_assign(info.id, PrinterAgentInfo(info.id, info.name, plugin_full_ref, std::move(factory)));
python_agent_ids[capability_key] = info.id;
auto& cache = get_printer_agent_cache();
auto cache_it = cache.find(info.id);
if (cache_it != cache.end()) {
cached_agent = std::move(cache_it->second);
cache.erase(cache_it);
}
}
if (cached_agent)
cached_agent->disconnect_printer();
BOOST_LOG_TRIVIAL(info) << "Registered printer-agent plugin '" << capability_name << "' with agent ID '" << info.id
<< "' and plugin ref '" << plugin_full_ref << "'";
}
void NetworkAgentFactory::deregister_python_plugin(const std::string& plugin_key)
{
std::vector<std::string> capability_names;
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
for (const auto& [capability_key, token] : s_python_printer_agent_registration_tokens) {
(void) token;
if (capability_key.first == plugin_key)
capability_names.push_back(capability_key.second);
}
for (const auto& [capability_key, agent_id] : get_python_printer_agent_ids()) {
(void) agent_id;
if (capability_key.first == plugin_key &&
std::find(capability_names.begin(), capability_names.end(), capability_key.second) == capability_names.end())
capability_names.push_back(capability_key.second);
}
}
for (const std::string& capability_name : capability_names)
deregister_python_printer_agent(plugin_key, capability_name);
if (capability_names.empty())
BOOST_LOG_TRIVIAL(debug) << "Python printer-agent plugin '" << plugin_key << "' has no registered capabilities";
}
void NetworkAgentFactory::deregister_python_printer_agent(const std::string& plugin_key, const std::string& capability_name)
{
std::string agent_id;
std::shared_ptr<IPrinterAgent> cached_agent;
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto token_it = std::find_if(s_python_printer_agent_registration_tokens.begin(),
s_python_printer_agent_registration_tokens.end(),
[&plugin_key, &capability_name](const auto& entry) {
return entry.first.first == plugin_key &&
entry.first.second == capability_name;
});
if (token_it != s_python_printer_agent_registration_tokens.end())
s_python_printer_agent_registration_tokens.erase(token_it);
auto& python_agent_ids = get_python_printer_agent_ids();
auto id_it = std::find_if(python_agent_ids.begin(), python_agent_ids.end(),
[&plugin_key, &capability_name](const auto& entry) {
return entry.first.first == plugin_key &&
entry.first.second == capability_name;
});
if (id_it == python_agent_ids.end()) {
BOOST_LOG_TRIVIAL(debug) << "Python printer-agent capability '" << capability_name << "' from plugin '"
<< plugin_key << "' is not registered";
return;
}
agent_id = id_it->second;
python_agent_ids.erase(id_it);
auto& cache = get_printer_agent_cache();
auto cache_it = cache.find(agent_id);
if (cache_it != cache.end()) {
cached_agent = std::move(cache_it->second);
cache.erase(cache_it);
}
get_printer_agents().erase(agent_id);
}
if (cached_agent)
cached_agent->disconnect_printer();
// The GUI may still own the active capability separately from the factory cache. Clear that
// handle before the plugin module is torn down, otherwise NetworkAgent can retain a Python
// object whose implementation is about to be unloaded.
if (!agent_id.empty() && wxTheApp) {
NetworkAgent* network_agent = GUI::wxGetApp().getAgent();
if (network_agent) {
const auto active_agent = network_agent->get_printer_agent();
if (active_agent && active_agent->get_agent_info().id == agent_id)
network_agent->set_printer_agent(nullptr);
}
}
BOOST_LOG_TRIVIAL(info) << "Deregistered Python printer-agent capability '" << capability_name << "' from plugin '"
<< plugin_key << "' with agent ID '" << agent_id << "'";
}
} // namespace Slic3r