This commit is contained in:
SoftFever
2026-01-23 17:05:56 +08:00
parent 01eec2f098
commit 3b85cd4d5d
60 changed files with 13396 additions and 1785 deletions

View File

@@ -300,6 +300,13 @@ void AppConfig::set_defaults()
if (get("allow_abnormal_storage").empty()) {
set_bool("allow_abnormal_storage", false);
}
#ifdef __linux__
if (get(SETTING_USE_ENCRYPTED_TOKEN_FILE).empty())
set_bool(SETTING_USE_ENCRYPTED_TOKEN_FILE, true);
#else
if (get(SETTING_USE_ENCRYPTED_TOKEN_FILE).empty())
set_bool(SETTING_USE_ENCRYPTED_TOKEN_FILE, false);
#endif
if(get("check_stable_update_only").empty()) {
set_bool("check_stable_update_only", false);

View File

@@ -28,6 +28,7 @@ using namespace nlohmann;
#define SETTING_NETWORK_PLUGIN_SKIPPED_VERSIONS "network_plugin_skipped_versions"
#define SETTING_NETWORK_PLUGIN_UPDATE_DISABLED "network_plugin_update_prompts_disabled"
#define SETTING_NETWORK_PLUGIN_REMIND_LATER "network_plugin_remind_later"
#define SETTING_USE_ENCRYPTED_TOKEN_FILE "use_encrypted_token_file"
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.01"
#define SUPPORT_DARK_MODE

View File

@@ -1017,7 +1017,7 @@ static std::vector<std::string> s_Preset_printer_options {
"scan_first_layer", "enable_power_loss_recovery", "wrapping_detection_layers", "wrapping_exclude_area", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "time_cost", "machine_pause_gcode", "template_custom_gcode",
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","printer_structure",
"best_object_pos", "head_wrap_detect_zone",
"host_type", "print_host", "printhost_apikey", "bbl_use_printhost",
"host_type", "print_host", "printhost_apikey", "bbl_use_printhost", "printer_agent",
"print_host_webui",
"printhost_cafile","printhost_port","printhost_authorization_type",
"printhost_user", "printhost_password", "printhost_ssl_ignore_revoke", "thumbnails", "thumbnails_format",
@@ -1502,7 +1502,7 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " uploading user preset name is: " << preset.name << "and create filament_id is: " << preset.filament_id
<< " and base_id is: " << preset.base_id;
key_values[BBL_JSON_KEY_UPDATE_TIME] = std::to_string(preset.updated_time);
key_values[ORCA_JSON_KEY_UPDATE_TIME] = std::to_string(preset.updated_time);
key_values[BBL_JSON_KEY_TYPE] = Preset::get_iot_type_string(preset.type);
return 0;
}
@@ -1802,8 +1802,8 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
//update_time
long long cloud_update_time = 0;
if (preset_values.find(BBL_JSON_KEY_UPDATE_TIME) != preset_values.end()) {
cloud_update_time = std::atoll(preset_values[BBL_JSON_KEY_UPDATE_TIME].c_str());
if (preset_values.find(ORCA_JSON_KEY_UPDATE_TIME) != preset_values.end()) {
cloud_update_time = std::atoll(preset_values[ORCA_JSON_KEY_UPDATE_TIME].c_str());
}
//user_id
@@ -3395,6 +3395,7 @@ static std::vector<std::string> s_PhysicalPrinter_opts {
"printer_technology",
"bbl_use_printhost",
"host_type",
"printer_agent",
"print_host",
"print_host_webui",
"printhost_apikey",

View File

@@ -52,7 +52,8 @@
#define BBL_JSON_KEY_BASE_ID "base_id"
#define BBL_JSON_KEY_USER_ID "user_id"
#define BBL_JSON_KEY_FILAMENT_ID "filament_id"
#define BBL_JSON_KEY_UPDATE_TIME "updated_time"
#define ORCA_JSON_KEY_UPDATE_TIME "updated_time"
#define ORCA_JSON_KEY_CREATED_TIME "created_time"
#define BBL_JSON_KEY_INHERITS "inherits"
#define BBL_JSON_KEY_INSTANTIATION "instantiation"
#define BBL_JSON_KEY_NOZZLE_DIAMETER "nozzle_diameter"

View File

@@ -740,6 +740,13 @@ void PrintConfigDef::init_common_params()
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("printer_agent", coString);
def->label = L("Printer Agent");
def->tooltip = L("Select the network agent implementation for printer communication.");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field should contain "

View File

@@ -6,6 +6,7 @@
#include <cassert>
#include <ctime>
#include <cstdio>
#include <cctype>
#ifdef _MSC_VER
#include <map>
@@ -230,5 +231,76 @@ time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt)
return str2time(ss, zone, fmtstr.c_str());
}
// /////////////////////////////////////////////////////////////////////////////
// Millisecond timestamps for cloud sync protocol
std::string millis_to_iso8601(long long unix_millis)
{
time_t seconds = static_cast<time_t>(unix_millis / 1000);
int millis = static_cast<int>(unix_millis % 1000);
std::tm tms = {};
_gmtime_r(&seconds, &tms);
char buf[32];
std::snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
tms.tm_year + 1900,
tms.tm_mon + 1,
tms.tm_mday,
tms.tm_hour,
tms.tm_min,
tms.tm_sec,
millis);
return std::string(buf);
}
long long iso8601_to_millis(const std::string& iso_time)
{
if (iso_time.empty()) return -1;
int y, M, d, h, m, s, ms = 0;
// Try parsing with milliseconds: "2025-11-28T14:30:00.123Z"
int parsed = sscanf(iso_time.c_str(), "%d-%d-%dT%d:%d:%d.%dZ",
&y, &M, &d, &h, &m, &s, &ms);
if (parsed < 6) {
// Try without milliseconds: "2025-11-28T14:30:00Z"
parsed = sscanf(iso_time.c_str(), "%d-%d-%dT%d:%d:%dZ",
&y, &M, &d, &h, &m, &s);
ms = 0;
}
if (parsed < 6) return -1;
// Normalize milliseconds (handle .1, .12, .123, .1234, etc.)
if (parsed >= 7) {
// Count digits in the fractional part to normalize
const char* dot = strchr(iso_time.c_str(), '.');
if (dot) {
int digits = 0;
for (const char* p = dot + 1; *p && *p != 'Z' && std::isdigit(*p); ++p)
digits++;
// Normalize to 3 digits (milliseconds)
while (digits < 3) { ms *= 10; digits++; }
while (digits > 3) { ms /= 10; digits--; }
}
}
std::tm tms = {};
tms.tm_year = y - 1900;
tms.tm_mon = M - 1;
tms.tm_mday = d;
tms.tm_hour = h;
tms.tm_min = m;
tms.tm_sec = s;
time_t seconds = _timegm(&tms);
if (seconds == time_t(-1)) return -1;
return static_cast<long long>(seconds) * 1000 + ms;
}
}; // namespace Utils
}; // namespace Slic3r

View File

@@ -63,6 +63,15 @@ inline time_t parse_iso_utc_timestamp(const std::string &str)
return str2time(str, TimeZone::utc, TimeFormat::iso8601Z);
}
// /////////////////////////////////////////////////////////////////////////////
// Millisecond timestamps for cloud sync protocol
// Format: "2025-11-28T14:30:00.123Z" (ISO 8601 with milliseconds)
// Lossless conversion: Unix milliseconds <-> ISO 8601
// Format: "YYYY-MM-DDTHH:MM:SS.sssZ" (always 3 decimal places for milliseconds)
std::string millis_to_iso8601(long long unix_millis);
long long iso8601_to_millis(const std::string& iso_time); // Returns -1 on parse error
// /////////////////////////////////////////////////////////////////////////////
} // namespace Utils

View File

@@ -598,6 +598,24 @@ set(SLIC3R_GUI_SOURCES
Utils/MKS.hpp
Utils/NetworkAgent.cpp
Utils/NetworkAgent.hpp
Utils/NetworkAgentFactory.hpp
Utils/NetworkAgentFactory.cpp
Utils/ICloudServiceAgent.hpp
Utils/IPrinterAgent.hpp
Utils/OrcaCloudServiceAgent.cpp
Utils/OrcaCloudServiceAgent.hpp
Utils/OrcaPrinterAgent.cpp
Utils/OrcaPrinterAgent.hpp
Utils/QidiPrinterAgent.cpp
Utils/QidiPrinterAgent.hpp
Utils/MoonrakerPrinterAgent.cpp
Utils/MoonrakerPrinterAgent.hpp
Utils/BBLCloudServiceAgent.cpp
Utils/BBLCloudServiceAgent.hpp
Utils/BBLPrinterAgent.cpp
Utils/BBLPrinterAgent.hpp
Utils/BBLNetworkPlugin.cpp
Utils/BBLNetworkPlugin.hpp
Utils/Obico.cpp
Utils/Obico.hpp
Utils/OctoPrint.cpp

View File

@@ -869,7 +869,7 @@ void BindMachineDialog::on_show(wxShowEvent &event)
m_printer_name->SetLabelText(from_u8(m_machine_info->get_dev_name()));
if (wxGetApp().is_user_login()) {
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_nickanme());
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_nickname());
m_user_name->SetLabelText(username_text);
std::string avatar_url = wxGetApp().getAgent()->get_user_avatar();

View File

@@ -255,6 +255,8 @@ public:
long tray_read_done_bits = 0;
long tray_reading_bits = 0;
bool ams_air_print_status { false };
/** Whether this printer supports virtual trays (external/manual filament loading).
* When true, vt_slot data is used by build_filament_ams_list() to include external filaments. */
bool ams_support_virtual_tray { true };
time_t ams_user_setting_start = 0;
time_t ams_switch_filament_start = 0;
@@ -856,7 +858,16 @@ public:
bool is_enable_np{ false };
bool is_enable_ams_np{ false };
/*vi slot data*/
/**
* Virtual Tray (vt_slot) - External/manual filament loading slots.
*
* Data Flow: Populated from printer JSON via parse_vt_tray() during MachineObject::parse_json().
* Used by: Sidebar::build_filament_ams_list() when ams_support_virtual_tray is true.
*
* Virtual trays represent filament that is manually loaded into the extruder
* rather than fed through an AMS unit. This supports printers without AMS
* or scenarios where users want to bypass the AMS.
*/
std::vector<DevAmsTray> vt_slot;
DevAmsTray parse_vt_tray(json vtray);

View File

@@ -119,6 +119,10 @@
#include "ModelMall.hpp"
#include "HintNotification.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
#include "slic3r/Utils/bambu_networking.hpp"
//#ifdef WIN32
//#include "BaseException.h"
//#endif
@@ -1176,9 +1180,9 @@ std::string GUI_App::get_plugin_url(std::string name, std::string country_code)
curr_version = BAMBU_NETWORK_AGENT_VERSION_LEGACY;
} else if (name == "plugins" && app_config) {
std::string user_version = app_config->get_network_plugin_version();
curr_version = user_version.empty() ? BBL::get_latest_network_version() : user_version;
curr_version = user_version.empty() ? get_latest_network_version() : user_version;
} else {
curr_version = BBL::get_latest_network_version();
curr_version = get_latest_network_version();
}
std::string using_version = curr_version.substr(0, 9) + "00";
@@ -1519,7 +1523,7 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP
if (name == "plugins") {
std::string config_version = app_config->get_network_plugin_version();
if (config_version.empty()) {
config_version = BBL::get_latest_network_version();
config_version = get_latest_network_version();
BOOST_LOG_TRIVIAL(info) << "[install_plugin] config_version was empty, using latest: " << config_version;
app_config->set_network_plugin_version(config_version);
GUI::wxGetApp().CallAfter([this] {
@@ -1814,7 +1818,7 @@ bool GUI_App::hot_reload_network_plugin()
std::string GUI_App::get_latest_network_version() const
{
return BBL::get_latest_network_version();
return Slic3r::get_latest_network_version();
}
bool GUI_App::has_network_update_available() const
@@ -1919,9 +1923,9 @@ bool GUI_App::check_networking_version()
studio_ver = BAMBU_NETWORK_AGENT_VERSION_LEGACY;
} else if (app_config) {
std::string user_version = app_config->get_network_plugin_version();
studio_ver = user_version.empty() ? BBL::get_latest_network_version() : user_version;
studio_ver = user_version.empty() ? get_latest_network_version() : user_version;
} else {
studio_ver = BBL::get_latest_network_version();
studio_ver = get_latest_network_version();
}
BOOST_LOG_TRIVIAL(info) << "check_networking_version: network_ver=" << network_ver << ", expected=" << studio_ver;
@@ -2233,6 +2237,8 @@ GUI_App::~GUI_App()
}
StaticBambuLib::release();
BBLNetworkPlugin::shutdown();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": exit");
}
@@ -3323,7 +3329,7 @@ __retry:
std::string loaded_version = Slic3r::NetworkAgent::get_version();
if (app_config && !loaded_version.empty() && loaded_version != "00.00.00.00") {
std::string config_version = app_config->get_network_plugin_version();
std::string config_base = BBL::extract_base_version(config_version);
std::string config_base = extract_base_version(config_version);
if (config_base != loaded_version) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": syncing config version from " << config_version << " to loaded " << loaded_version;
app_config->set(SETTING_NETWORK_PLUGIN_VERSION, loaded_version);
@@ -3370,7 +3376,12 @@ __retry:
//std::string data_dir = wxStandardPaths::Get().GetUserDataDir().ToUTF8().data();
std::string data_directory = data_dir();
m_agent = new Slic3r::NetworkAgent(data_directory);
// Register all printer agents before creating the network agent
Slic3r::NetworkAgentFactory::register_all_agents();
// m_agent = new Slic3r::NetworkAgent(data_directory);
std::unique_ptr<Slic3r::NetworkAgent> agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config);
m_agent = agent_ptr.release();
if (!m_device_manager)
m_device_manager = new Slic3r::DeviceManager(m_agent);
@@ -3460,6 +3471,82 @@ unsigned GUI_App::get_colour_approx_luma(const wxColour &colour)
));
}
void GUI_App::switch_printer_agent(const std::string& agent_id)
{
if (!m_agent) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": no agent exists";
return;
}
// Use registry to validate and create agent
// If empty, use default
std::string effective_agent_id = agent_id.empty() ? NetworkAgentFactory::get_default_printer_agent_id() : agent_id;
// Check if agent is registered
if (!NetworkAgentFactory::is_printer_agent_registered(effective_agent_id)) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": unregistered agent ID '" << effective_agent_id
<< "', keeping current agent";
// Keep current agent, don't switch
return;
}
std::string current_agent_id;
if (m_agent && m_agent->get_printer_agent())
current_agent_id = m_agent->get_printer_agent()->get_agent_info().id;
if (!current_agent_id.empty() && current_agent_id == effective_agent_id) {
return;
}
std::string log_dir = data_dir();
std::shared_ptr<ICloudServiceAgent> cloud_agent = m_agent->get_cloud_agent();
if (!cloud_agent) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no cloud agent available";
return;
}
// Create new printer agent via registry
std::shared_ptr<IPrinterAgent> new_printer_agent =
NetworkAgentFactory::create_printer_agent_by_id(effective_agent_id, cloud_agent, log_dir);
if (!new_printer_agent) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": failed to create agent '" << effective_agent_id
<< "', keeping current agent";
return;
}
// Swap the agent
m_agent->set_printer_agent(new_printer_agent);
// Update dependent managers
if (m_device_manager) {
m_device_manager->set_agent(m_agent);
// If there's a selected machine that was deferred due to no printer agent,
// trigger a connection now that the agent is ready
MachineObject* selected = m_device_manager->get_selected_machine();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": checking for deferred connection - selected="
<< (selected ? selected->get_dev_id() : "null");
if (selected) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": selected machine - is_lan_mode=" << selected->is_lan_mode_printer()
<< " is_connected=" << selected->is_connected();
}
if (selected && selected->is_lan_mode_printer() && !selected->is_connected()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": connecting deferred LAN machine dev_id=" << selected->get_dev_id();
#if !BBL_RELEASE_TO_PUBLIC
selected->connect(app_config->get("enable_ssl_for_mqtt") == "true" ? true : false);
#else
selected->connect(selected->local_use_ssl_for_mqtt);
#endif
selected->set_lan_mode_connection_state(true);
}
} else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": m_device_manager is null, cannot check for deferred connection";
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": printer agent switched to " << effective_agent_id;
}
bool GUI_App::dark_mode()
{
#ifdef SUPPORT_DARK_MODE
@@ -4283,10 +4370,14 @@ void GUI_App::get_login_info()
GUI::wxGetApp().run_script(strJS);
}
else {
m_agent->user_logout();
std::string logout_cmd = m_agent->build_logout_cmd();
wxString strJS = wxString::Format("window.postMessage(%s)", logout_cmd);
GUI::wxGetApp().run_script(strJS);
// OrcaNetwork performs async refresh on startup; avoid clearing
// persisted tokens when the UI polls before refresh completes.
if (m_agent->get_version() != "orca_network") {
m_agent->user_logout();
std::string logout_cmd = m_agent->build_logout_cmd();
wxString strJS = wxString::Format("window.postMessage(%s)", logout_cmd);
GUI::wxGetApp().run_script(strJS);
}
}
mainframe->m_webview->SetLoginPanelVisibility(true);
}
@@ -5635,7 +5726,7 @@ void GUI_App::sync_preset(Preset* preset)
if (!new_setting_id.empty()) {
setting_id = new_setting_id;
result = 0;
auto update_time_str = values_map[BBL_JSON_KEY_UPDATE_TIME];
auto update_time_str = values_map[ORCA_JSON_KEY_UPDATE_TIME];
if (!update_time_str.empty())
update_time = std::atoll(update_time_str.c_str());
}
@@ -5664,7 +5755,7 @@ void GUI_App::sync_preset(Preset* preset)
if (!new_setting_id.empty()) {
setting_id = new_setting_id;
result = 0;
auto update_time_str = values_map[BBL_JSON_KEY_UPDATE_TIME];
auto update_time_str = values_map[ORCA_JSON_KEY_UPDATE_TIME];
if (!update_time_str.empty())
update_time = std::atoll(update_time_str.c_str());
} else {
@@ -5690,16 +5781,16 @@ void GUI_App::sync_preset(Preset* preset)
result = 0;
}
else {
result = m_agent->put_setting(setting_id, preset->name, &values_map, &http_code);
if (http_code >= 400) {
result = 0;
updated_info = "hold";
BOOST_LOG_TRIVIAL(error) << "[sync_preset] put setting_id = " << setting_id << " failed, http_code = " << http_code;
} else {
auto update_time_str = values_map[BBL_JSON_KEY_UPDATE_TIME];
result = m_agent->put_setting(setting_id, preset->name, &values_map, &http_code);
if (http_code >= 400) {
result = 0;
updated_info = "hold";
BOOST_LOG_TRIVIAL(error) << "[sync_preset] put setting_id = " << setting_id << " failed, http_code = " << http_code;
} else {
auto update_time_str = values_map[ORCA_JSON_KEY_UPDATE_TIME];
if (!update_time_str.empty())
update_time = std::atoll(update_time_str.c_str());
}
}
}
}
@@ -5752,6 +5843,8 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
return;
if (!m_agent || !m_agent->is_user_login()) return;
if(!m_agent->get_cloud_agent())
return;
// has already start sync
if (m_user_sync_token) return;
@@ -5801,7 +5894,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
auto type = info[BBL_JSON_KEY_TYPE];
auto name = info[BBL_JSON_KEY_NAME];
auto setting_id = info[BBL_JSON_KEY_SETTING_ID];
auto update_time_str = info[BBL_JSON_KEY_UPDATE_TIME];
auto update_time_str = info[ORCA_JSON_KEY_UPDATE_TIME];
long long update_time = 0;
if (!update_time_str.empty())
update_time = std::atoll(update_time_str.c_str());
@@ -5911,6 +6004,25 @@ void GUI_App::start_http_server()
if (!m_http_server.is_started())
m_http_server.start();
}
void GUI_App::start_http_server(int port)
{
if (port <= 0) {
start_http_server();
return;
}
if (m_http_server.is_started()) {
if (m_http_server.get_port() == static_cast<boost::asio::ip::port_type>(port)) {
return;
}
m_http_server.stop();
}
m_http_server.set_port(static_cast<boost::asio::ip::port_type>(port));
m_http_server.start();
}
void GUI_App::stop_http_server()
{
m_http_server.stop();

View File

@@ -340,6 +340,10 @@ public:
Slic3r::TaskManager* getTaskManager() { return m_task_manager; }
HMSQuery* get_hms_query() { return hms_query; }
NetworkAgent* getAgent() { return m_agent; }
// Dynamic printer agent switching
void switch_printer_agent(const std::string& agent_id);
FilamentColorCodeQuery* get_filament_color_code_query();
bool is_editor() const { return m_app_mode == EAppMode::Editor; }
bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; }
@@ -501,6 +505,7 @@ public:
void start_sync_user_preset(bool with_progress_dlg = false);
void stop_sync_user_preset();
void start_http_server();
void start_http_server(int port);
void stop_http_server();
void switch_staff_pick(bool on);

View File

@@ -182,6 +182,42 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
{
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: get_response";
const std::string auth_code = url_get_param(url, "code");
if (!auth_code.empty()) {
std::string state = url_get_param(url, "state");
NetworkAgent* agent = wxGetApp().getAgent();
if (!agent) {
return std::make_shared<ResponseNotFound>();
}
json payload;
payload["command"] = "user_login";
payload["data"]["code"] = auth_code;
payload["data"]["state"] = state;
agent->change_user(payload.dump());
const bool login_ok = agent->is_user_login();
if (login_ok) {
wxGetApp().request_user_login(1);
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
}
const std::string title = login_ok ? "Authentication complete" : "Authentication failed";
const std::string message = login_ok
? "You can return to OrcaSlicer. This window will close automatically."
: "Something went wrong. Please return to OrcaSlicer and try again.";
const std::string html =
"<html><head><meta charset=\"utf-8\">"
"<style>body{font-family:Arial,sans-serif;background:#f7f7f7;color:#222;margin:32px;}"
"a.button{display:inline-block;padding:10px 16px;margin-top:12px;background:#0f8bff;color:#fff;text-decoration:none;border-radius:6px;}"
"</style></head><body><div class=\"container\">"
"<h2>" + title + "</h2>"
"<p>" + message + "</p>"
"<script>setTimeout(function(){try{window.close();}catch(e){}},1500);</script>"
"</div></body></html>";
return std::make_shared<ResponseHtml>(html);
}
if (boost::contains(url, "access_token")) {
std::string redirect_url = url_get_param(url, "redirect_url");
std::string access_token = url_get_param(url, "access_token");
@@ -249,7 +285,17 @@ void HttpServer::ResponseNotFound::write_response(std::stringstream& ssOut)
void HttpServer::ResponseRedirect::write_response(std::stringstream& ssOut)
{
const std::string sHTML = "<html><body><p>redirect to url </p></body></html>";
const std::string sHTML =
"<html><head><meta charset=\"utf-8\">"
"<meta http-equiv=\"refresh\" content=\"0;url=" + location_str + "\">"
"<style>body{font-family:Arial,sans-serif;background:#f7f7f7;color:#222;margin:32px;}"
"a.button{display:inline-block;padding:10px 16px;margin-top:12px;background:#0f8bff;color:#fff;text-decoration:none;border-radius:6px;}"
"</style></head><body><div class=\"container\">"
"<h2>Authentication complete</h2>"
"<p>You can return to OrcaSlicer. If your browser does not redirect automatically, use the button below.</p>"
"<a class=\"button\" href=\"" + location_str + "\">Continue</a>"
"<script>setTimeout(function(){try{window.close();}catch(e){}},1500);</script>"
"</div></body></html>";
ssOut << "HTTP/1.1 302 Found" << std::endl;
ssOut << "Location: " << location_str << std::endl;
ssOut << "content-type: text/html" << std::endl;
@@ -258,5 +304,14 @@ void HttpServer::ResponseRedirect::write_response(std::stringstream& ssOut)
ssOut << sHTML;
}
void HttpServer::ResponseHtml::write_response(std::stringstream& ssOut)
{
ssOut << "HTTP/1.1 200 OK" << std::endl;
ssOut << "content-type: text/html" << std::endl;
ssOut << "content-length: " << html.length() << std::endl;
ssOut << std::endl;
ssOut << html;
}
} // GUI
} //Slic3r

View File

@@ -13,6 +13,7 @@
#include <string>
#include <set>
#include <memory>
#include <utility>
#define LOCALHOST_PORT 13618
#define LOCALHOST_URL "http://localhost:"
@@ -98,6 +99,16 @@ public:
void write_response(std::stringstream& ssOut) override;
};
class ResponseHtml : public Response
{
const std::string html;
public:
explicit ResponseHtml(std::string html) : html(std::move(html)) {}
~ResponseHtml() override = default;
void write_response(std::stringstream& ssOut) override;
};
HttpServer(boost::asio::ip::port_type port = LOCALHOST_PORT);
boost::thread m_http_server_thread;
@@ -106,6 +117,8 @@ public:
bool is_started() { return start_http_server; }
void start();
void stop();
void set_port(boost::asio::ip::port_type new_port) { port = new_port; }
boost::asio::ip::port_type get_port() const { return port; }
void set_request_handler(const std::function<std::shared_ptr<Response>(const std::string&)>& m_request_handler);
static std::shared_ptr<Response> bbl_auth_handle_request(const std::string& url);

View File

@@ -66,22 +66,22 @@ void BindJob::process(Ctl &ctl)
result_code = code;
result_info = info;
if (stage == BBL::BindJobStage::LoginStageConnect) {
if (stage == BindJobStage::LoginStageConnect) {
curr_percent = 15;
msg = _u8L("Logging in");
} else if (stage == BBL::BindJobStage::LoginStageLogin) {
} else if (stage == BindJobStage::LoginStageLogin) {
curr_percent = 30;
msg = _u8L("Logging in");
} else if (stage == BBL::BindJobStage::LoginStageWaitForLogin) {
} else if (stage == BindJobStage::LoginStageWaitForLogin) {
curr_percent = 45;
msg = _u8L("Logging in");
} else if (stage == BBL::BindJobStage::LoginStageGetIdentify) {
} else if (stage == BindJobStage::LoginStageGetIdentify) {
curr_percent = 60;
msg = _u8L("Logging in");
} else if (stage == BBL::BindJobStage::LoginStageWaitAuth) {
} else if (stage == BindJobStage::LoginStageWaitAuth) {
curr_percent = 80;
msg = _u8L("Logging in");
} else if (stage == BBL::BindJobStage::LoginStageFinished) {
} else if (stage == BindJobStage::LoginStageFinished) {
curr_percent = 100;
msg = _u8L("Logging in");
} else {

View File

@@ -197,7 +197,7 @@ void PrintJob::process(Ctl &ctl)
this->task_bed_type = bed_type_to_gcode_string(plate_data.is_valid ? plate_data.bed_type : curr_plate->get_bed_type(true));
}
BBL::PrintParams params;
PrintParams params;
// local print access
params.dev_ip = m_dev_ip;
@@ -382,14 +382,14 @@ void PrintJob::process(Ctl &ctl)
StagePercentPoint
](int stage, int code, std::string info) {
if (stage == BBL::SendingPrintJobStage::PrintingStageCreate && !is_try_lan_mode_failed) {
if (stage == SendingPrintJobStage::PrintingStageCreate && !is_try_lan_mode_failed) {
if (this->connection_type == "lan") {
msg = _u8L("Sending print job over LAN");
} else {
msg = _u8L("Sending print job through cloud service");
}
}
else if (stage == BBL::SendingPrintJobStage::PrintingStageUpload && !is_try_lan_mode_failed) {
else if (stage == SendingPrintJobStage::PrintingStageUpload && !is_try_lan_mode_failed) {
if (code >= 0 && code <= 100 && !info.empty()) {
if (this->connection_type == "lan") {
msg = _u8L("Sending print job over LAN");
@@ -399,24 +399,24 @@ void PrintJob::process(Ctl &ctl)
msg += format("(%s)", info);
}
}
else if (stage == BBL::SendingPrintJobStage::PrintingStageWaiting) {
else if (stage == SendingPrintJobStage::PrintingStageWaiting) {
if (this->connection_type == "lan") {
msg = _u8L("Sending print job over LAN");
} else {
msg = _u8L("Sending print job through cloud service");
}
}
else if (stage == BBL::SendingPrintJobStage::PrintingStageRecord && !is_try_lan_mode) {
else if (stage == SendingPrintJobStage::PrintingStageRecord && !is_try_lan_mode) {
msg = _u8L("Sending print configuration");
}
else if (stage == BBL::SendingPrintJobStage::PrintingStageSending && !is_try_lan_mode) {
else if (stage == SendingPrintJobStage::PrintingStageSending && !is_try_lan_mode) {
if (this->connection_type == "lan") {
msg = _u8L("Sending print job over LAN");
} else {
msg = _u8L("Sending print job through cloud service");
}
}
else if (stage == BBL::SendingPrintJobStage::PrintingStageFinished) {
else if (stage == SendingPrintJobStage::PrintingStageFinished) {
msg = format(_u8L("Successfully sent. Will automatically jump to the device page in %ss"), info);
if (m_print_job_completed_id == wxGetApp().plater()->get_send_calibration_finished_event()) {
msg = format(_u8L("Successfully sent. Will automatically jump to the next page in %ss"), info);
@@ -433,15 +433,15 @@ void PrintJob::process(Ctl &ctl)
// update current percnet
if (stage >= 0 && stage <= (int) PrintingStageFinished) {
curr_percent = StagePercentPoint[stage];
if ((stage == BBL::SendingPrintJobStage::PrintingStageUpload
|| stage == BBL::SendingPrintJobStage::PrintingStageRecord)
if ((stage == SendingPrintJobStage::PrintingStageUpload
|| stage == SendingPrintJobStage::PrintingStageRecord)
&& (code > 0 && code <= 100)) {
curr_percent = (StagePercentPoint[stage + 1] - StagePercentPoint[stage]) * code / 100 + StagePercentPoint[stage];
}
}
//get errors
if (code > 100 || code < 0 || stage == BBL::SendingPrintJobStage::PrintingStageERROR) {
if (code > 100 || code < 0 || stage == SendingPrintJobStage::PrintingStageERROR) {
if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_OVER_SIZE || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_OVER_SIZE) {
m_plater->update_print_error_info(code, desc_file_too_large, info);
}else if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST){

View File

@@ -100,10 +100,10 @@ inline std::string get_transform_string(int bytes)
void SendJob::process(Ctl &ctl)
{
BBL::PrintParams params;
PrintParams params;
std::string msg;
int curr_percent = 10;
NetworkAgent* m_agent = wxGetApp().getAgent();
NetworkAgent* agent = wxGetApp().getAgent();
AppConfig* config = wxGetApp().app_config;
int result = -1;
std::string http_body;
@@ -234,14 +234,14 @@ void SendJob::process(Ctl &ctl)
// update current percnet
if (stage >= 0 && stage <= (int) PrintingStageFinished) {
curr_percent = StagePercentPoint[stage];
if ((stage == BBL::SendingPrintJobStage::PrintingStageUpload) &&
if ((stage == SendingPrintJobStage::PrintingStageUpload) &&
(code > 0 && code <= 100)) {
curr_percent = (StagePercentPoint[stage + 1] - StagePercentPoint[stage]) * code / 100 + StagePercentPoint[stage];
}
}
//get errors
if (code > 100 || code < 0 || stage == BBL::SendingPrintJobStage::PrintingStageERROR) {
if (code > 100 || code < 0 || stage == SendingPrintJobStage::PrintingStageERROR) {
if (code == BAMBU_NETWORK_ERR_PRINT_WR_FILE_OVER_SIZE || code == BAMBU_NETWORK_ERR_PRINT_SP_FILE_OVER_SIZE) {
m_plater->update_print_error_info(code, desc_file_too_large, info);
}
@@ -281,7 +281,7 @@ void SendJob::process(Ctl &ctl)
// try to send local with record
BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode to printer";
ctl.update_status(curr_percent, _u8L("Sending G-code file over LAN"));
result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
result = agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) {
params.comments = "upload_failed";
} else {
@@ -304,8 +304,8 @@ void SendJob::process(Ctl &ctl)
case DevStorage::SdcardState::HAS_SDCARD_ABNORMAL:
if(this->has_sdcard) {
// means the sdcard is abnormal but can be used option is enabled
ctl.update_status(curr_percent, _u8L("Sending G-code file over LAN, but the Storage in the printer is abnormal and print-issues may be caused by this."));
result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
ctl.update_status(curr_percent, _u8L("Sending G-code file over LAN, but the Storage in the printer is abnormal and print-issues may be caused by this."));
result = agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
break;
}
ctl.update_status(curr_percent, _u8L("The Storage in the printer is abnormal. Please replace it with a normal Storage before sending to printer."));
@@ -315,7 +315,7 @@ void SendJob::process(Ctl &ctl)
return;
case DevStorage::SdcardState::HAS_SDCARD_NORMAL:
ctl.update_status(curr_percent, _u8L("Sending G-code file over LAN"));
result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
result = agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn, nullptr);
break;
default:
ctl.update_status(curr_percent, _u8L("Encountered an unknown error with the Storage status. Please try again."));

View File

@@ -7,6 +7,8 @@
#include "I18N.hpp"
#include "MsgDialog.hpp"
#include "DownloadProgressDialog.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>

View File

@@ -3,6 +3,7 @@
#include "libslic3r/Model.hpp"
#include "libslic3r/AppConfig.hpp"
#include "slic3r/Utils/bambu_networking.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include <wx/app.h>
#include <wx/button.h>
@@ -532,8 +533,8 @@ void MonitorPanel::update_network_version_footer()
return;
std::string configured_version = wxGetApp().app_config->get_network_plugin_version();
std::string suffix = BBL::extract_suffix(configured_version);
std::string configured_base = BBL::extract_base_version(configured_version);
std::string suffix = extract_suffix(configured_version);
std::string configured_base = extract_base_version(configured_version);
wxString footer_text;
if (!suffix.empty() && configured_base == binary_version) {

View File

@@ -211,7 +211,7 @@ void NetworkPluginDownloadDialog::setup_version_selector()
wxDefaultPosition, wxSize(FromDIP(380), FromDIP(28)), 0, nullptr, wxCB_READONLY);
m_version_combo->SetFont(::Label::Body_13);
m_available_versions = BBL::get_all_available_versions();
m_available_versions = get_all_available_versions();
for (size_t i = 0; i < m_available_versions.size(); ++i) {
const auto& ver = m_available_versions[i];
wxString label;

View File

@@ -53,7 +53,7 @@ private:
wxCollapsiblePane* m_details_pane{nullptr};
std::string m_error_message;
std::string m_error_details;
std::vector<BBL::NetworkLibraryVersionInfo> m_available_versions;
std::vector<NetworkLibraryVersionInfo> m_available_versions;
};
class NetworkPluginRestartDialog : public DPIDialog

View File

@@ -24,6 +24,7 @@
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "format.hpp"
#include "Tab.hpp"
#include "wxExtensions.hpp"
@@ -124,8 +125,22 @@ PhysicalPrinterDialog::~PhysicalPrinterDialog()
void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup)
{
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (opt_key == "host_type" || opt_key == "printhost_authorization_type")
// Special handling for printer_agent: convert fake enum index to string agent ID
if (opt_key == "printer_agent") {
try {
int selected_idx = boost::any_cast<int>(value);
auto agents = NetworkAgentFactory::get_registered_printer_agents();
if (selected_idx >= 0 && selected_idx < static_cast<int>(agents.size())) {
m_config->set_key_value("printer_agent",
new ConfigOptionString(agents[selected_idx].id));
}
} catch (const boost::bad_any_cast&) {
// If value is not an int, ignore
}
this->update();
} else if (opt_key == "host_type" || opt_key == "printhost_authorization_type") {
this->update();
}
if (opt_key == "print_host")
this->update_printhost_buttons();
if (opt_key == "printhost_port")
@@ -136,6 +151,55 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
m_optgroup->append_single_option_line("host_type");
// Build printer agent dropdown from registry (only if network agent is available)
if (wxGetApp().getAgent() != nullptr) {
auto agents = NetworkAgentFactory::get_registered_printer_agents();
if (!agents.empty()) {
// Create a fake enum option to force a Choice widget instead of TextCtrl
// (printer_agent is coString in config, but we need a dropdown)
ConfigOptionDef def;
def.type = coEnum;
def.width = Field::def_width();
def.label = L("Printer Agent");
def.tooltip = L("Select the network agent implementation for printer communication. "
"Available agents are registered at startup.");
def.mode = comAdvanced;
// Populate enum values and labels from registered agents
for (const auto& agent : agents) {
def.enum_values.push_back(agent.id);
def.enum_labels.push_back(agent.display_name);
}
// Set initial selection based on current config value or default
const std::string current_agent = m_config->opt_string("printer_agent");
std::string selected_agent = current_agent;
if (selected_agent.empty()) {
selected_agent = NetworkAgentFactory::get_default_printer_agent_id();
}
// Verify selected agent is valid
auto it = std::find_if(agents.begin(), agents.end(), [&selected_agent](const auto& a) { return a.id == selected_agent; });
if (it == agents.end()) {
selected_agent = NetworkAgentFactory::get_default_printer_agent_id();
}
// Set default value for the enum (using the index)
auto def_it = std::find_if(agents.begin(), agents.end(), [&selected_agent](const auto& a) { return a.id == selected_agent; });
if (def_it != agents.end()) {
size_t default_idx = std::distance(agents.begin(), def_it);
def.set_default_value(new ConfigOptionInt(static_cast<int>(default_idx)));
}
// Create and append the option line
auto agent_option = Option(def, "printer_agent");
Line agent_line = m_optgroup->create_single_option_line(agent_option);
m_optgroup->append_line(agent_line);
}
}
auto create_sizer_with_btn = [](wxWindow* parent, Button** btn, const std::string& icon_name, const wxString& label) {
*btn = new Button(parent, label);
(*btn)->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
@@ -688,6 +752,31 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
}
}
void PhysicalPrinterDialog::update_printer_agent_type()
{
if (m_config == nullptr)
return;
Field* agent_field = m_optgroup->get_field("printer_agent");
if (!agent_field)
return;
Choice* agent_choice = dynamic_cast<Choice*>(agent_field);
if (!agent_choice)
return;
// Sync selection with current config value
const std::string current_agent = m_config->opt_string("printer_agent");
auto agents = NetworkAgentFactory::get_registered_printer_agents();
for (size_t i = 0; i < agents.size(); ++i) {
if (agents[i].id == current_agent) {
agent_choice->set_value(i);
return;
}
}
}
void PhysicalPrinterDialog::update_printers()
{
wxBusyCursor wait;

View File

@@ -60,6 +60,7 @@ public:
void update(bool printer_change = false);
void update_host_type(bool printer_change);
void update_printer_agent_type();
void update_preset_input();
void update_printhost_buttons();
void update_printers();

View File

@@ -2371,7 +2371,14 @@ void Sidebar::update_all_preset_comboboxes()
} else {
//p->btn_connect_printer->Show();
p->m_printer_connect->Show();
p->m_bpButton_ams_filament->Hide();
// ORCA: show/hide sync-ams button based on filament sync mode
auto agent = wxGetApp().getAgent();
if (agent && agent->get_filament_sync_mode() != FilamentSyncMode::none)
p->m_bpButton_ams_filament->Show();
else
p->m_bpButton_ams_filament->Hide();
auto print_btn_type = MainFrame::PrintSelectType::eExportGcode;
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
wxString apikey;
@@ -3161,11 +3168,49 @@ void Sidebar::on_bed_type_change(BedType bed_type)
p->combo_printer_bed->SetSelection(0);
}
/**
* Build a map of filament configurations from the connected printer's AMS (Automatic Material System).
*
* Data Flow Architecture:
* =======================
* This function reads pre-populated state from MachineObject - it does NOT directly call
* NetworkAgent APIs. The data pipeline is:
*
* Printer Device (MQTT/LAN messages)
* ↓
* NetworkAgent (receives JSON, triggers OnMessageFn callbacks)
* ↓
* MachineObject::parse_json() (updates device state)
* ├── vt_slot (std::vector<DevAmsTray>) - virtual tray data for external filament
* └── DevFilaSystem → DevAms → DevAmsTray - AMS unit hierarchy
* ↓
* build_filament_ams_list() [THIS FUNCTION] - aggregates into DynamicPrintConfig maps
*
* Data Sources:
* - obj->vt_slot: Virtual trays for external/manual filament loading (when ams_support_virtual_tray is true)
* - obj->GetFilaSystem()->GetAmsList(): Map of AMS units, each containing multiple DevAmsTray slots
*
* Return Value:
* - Map key encoding:
* - Virtual trays: 0x10000 + vt_tray.id (first/main extruder), or just vt_tray.id (secondary)
* - AMS trays: 0x10000 + (ams_id * 4 + slot_id) (main extruder), or (ams_id * 4 + slot_id) (secondary)
* - The 0x10000 flag indicates the main/right extruder
* - Map value: DynamicPrintConfig with filament properties (id, type, color, etc.)
*
* @param obj The MachineObject representing the connected printer (nullable)
* @return Map of tray indices to filament configurations
*/
std::map<int, DynamicPrintConfig> Sidebar::build_filament_ams_list(MachineObject* obj)
{
std::map<int, DynamicPrintConfig> filament_ams_list;
if (!obj) return filament_ams_list;
// For pull-mode agents (e.g., HTTP REST API), refresh DevFilaSystem first
auto* agent = wxGetApp().getDeviceManager()->get_agent();
if (agent && agent->get_filament_sync_mode() == FilamentSyncMode::pull) {
agent->fetch_filament_info(obj->get_dev_id());
}
auto build_tray_config = [](DevAmsTray const &tray, std::string const &name, std::string ams_id, std::string slot_id) {
BOOST_LOG_TRIVIAL(info) << boost::format("build_filament_ams_list: name %1% setting_id %2% type %3% color %4%")
% name % tray.setting_id % tray.m_fila_type % tray.color;
@@ -3285,7 +3330,14 @@ void Sidebar::get_small_btn_sync_pos_size(wxPoint &pt, wxSize &size) {
void Sidebar::load_ams_list(MachineObject* obj)
{
std::map<int, DynamicPrintConfig> filament_ams_list = build_filament_ams_list(obj);
std::map<int, DynamicPrintConfig> filament_ams_list;
// build_filament_ams_list handles both subscription-based and non-subscription-based agents:
// - For non-subscription agents, it calls fetch_filament_info() first to populate DevFilaSystem
// - Then it always reads from DevFilaSystem to build the filament list
if (obj) {
filament_ams_list = build_filament_ams_list(obj);
}
bool device_change = false;
const std::string& device = obj ? obj->get_dev_id() : "";
@@ -3315,8 +3367,9 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
wxBusyCursor cursor;
// Force load ams list
auto obj = wxGetApp().getDeviceManager()->get_selected_machine();
if (obj)
GUI::wxGetApp().sidebar().load_ams_list(obj);
if (!obj)
return;
GUI::wxGetApp().sidebar().load_ams_list(obj);
auto & list = wxGetApp().preset_bundle->filament_ams_list;
if (list.empty()) {
@@ -15134,7 +15187,7 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy
// set designInfo before export and reset after export
if (wxGetApp().is_user_login()) {
p->model.design_info = std::make_shared<ModelDesignInfo>();
//p->model.design_info->Designer = wxGetApp().getAgent()->get_user_nickanme();
//p->model.design_info->Designer = wxGetApp().getAgent()->get_user_nickname();
p->model.design_info->Designer = "";
p->model.design_info->DesignerUserId = wxGetApp().getAgent()->get_user_id();
BOOST_LOG_TRIVIAL(trace) << "design_info prepare, designer = "<< "";

View File

@@ -15,6 +15,7 @@
#include "Widgets/StaticLine.hpp"
#include "Widgets/RadioGroup.hpp"
#include "slic3r/Utils/bambu_networking.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "DownloadProgressDialog.hpp"
#ifdef __WINDOWS__
@@ -1411,6 +1412,11 @@ void PreferencesDialog::create_items()
auto item_system_sync = create_item_checkbox(_L("Update built-in Presets automatically."), "", "sync_system_preset");
g_sizer->Add(item_system_sync);
auto item_token_storage = create_item_checkbox(_L("Use encrypted file for token storage"),
_L("Store authentication tokens in an encrypted file instead of the system keychain. (Requires restart)"),
SETTING_USE_ENCRYPTED_TOKEN_FILE);
g_sizer->Add(item_token_storage);
//// ONLINE > Network plugin
g_sizer->Add(create_item_title(_L("Network plugin")), 1, wxEXPAND);
@@ -1433,11 +1439,11 @@ void PreferencesDialog::create_items()
std::string current_version = app_config->get_network_plugin_version();
if (current_version.empty()) {
current_version = BBL::get_latest_network_version();
current_version = get_latest_network_version();
}
int current_selection = 0;
m_available_versions = BBL::get_all_available_versions();
m_available_versions = get_all_available_versions();
for (size_t i = 0; i < m_available_versions.size(); i++) {
const auto& ver = m_available_versions[i];
@@ -1468,7 +1474,7 @@ void PreferencesDialog::create_items()
std::string new_version = selected_ver.version;
std::string old_version = app_config->get_network_plugin_version();
if (old_version.empty()) {
old_version = BBL::get_latest_network_version();
old_version = get_latest_network_version();
}
app_config->set(SETTING_NETWORK_PLUGIN_VERSION, new_version);

View File

@@ -70,7 +70,7 @@ public:
::TextInput *m_backup_interval_textinput = {nullptr};
::ComboBox * m_network_version_combo = {nullptr};
wxBoxSizer * m_network_version_sizer = {nullptr};
std::vector<BBL::NetworkLibraryVersionInfo> m_available_versions;
std::vector<NetworkLibraryVersionInfo> m_available_versions;
wxString m_developer_mode_def;
wxString m_internal_developer_mode_def;

View File

@@ -439,9 +439,9 @@ void SendMultiMachinePage::refresh_user_device()
Fit();
}
BBL::PrintParams SendMultiMachinePage::request_params(MachineObject* obj)
PrintParams SendMultiMachinePage::request_params(MachineObject* obj)
{
BBL::PrintParams params;
PrintParams params;
//get all setting
bool bed_leveling = app_config->get("print", "bed_leveling") == "1" ? true : false;
@@ -734,7 +734,7 @@ void SendMultiMachinePage::on_send(wxCommandEvent& event)
}
std::vector<BBL::PrintParams> print_params;
std::vector<PrintParams> print_params;
for (auto it = m_device_items.begin(); it != m_device_items.end(); ++it) {
auto obj = it->second->get_obj();
@@ -742,7 +742,7 @@ void SendMultiMachinePage::on_send(wxCommandEvent& event)
if (obj && obj->is_online() && !obj->can_abort() && !obj->is_in_upgrading() && it->second->get_state_selected() == 1 && it->second->state_printable <= 2) {
if (!it->second->is_blocking_printing(obj)) {
BBL::PrintParams params = request_params(obj);
PrintParams params = request_params(obj);
print_params.push_back(params);
}
}

View File

@@ -170,7 +170,7 @@ public:
void on_send(wxCommandEvent& event);
bool Show(bool show);
BBL::PrintParams request_params(MachineObject* obj);
PrintParams request_params(MachineObject* obj);
bool get_ams_mapping_result(std::string &mapping_array_str, std::string &mapping_array_str2, std::string &ams_mapping_info);
wxBoxSizer* create_item_title(wxString title, wxWindow* parent, wxString tooltip);

View File

@@ -2099,6 +2099,11 @@ void Tab::on_presets_changed()
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
wxGetApp().plater()->sidebar().update_presets(m_type);
// Check if printer agent needs switching
if (m_type == Preset::TYPE_PRINTER) {
update_printer_agent_if_needed();
}
bool is_bbl_vendor_preset = m_preset_bundle->is_bbl_vendor();
if (is_bbl_vendor_preset) {
wxGetApp().plater()->get_partplate_list().set_render_option(true, true);
@@ -2129,6 +2134,28 @@ void Tab::on_presets_changed()
wxGetApp().plater()->update_project_dirty_from_presets();
}
void Tab::update_printer_agent_if_needed()
{
std::string agent_id = "orca";
if (m_preset_bundle) {
if (wxGetApp().preset_bundle->is_bbl_vendor()) {
agent_id = "bbl";
} else if (wxGetApp().preset_bundle->is_qidi_vendor()) {
agent_id = "qidi";
}
}
const DynamicPrintConfig& config = m_preset_bundle->printers.get_edited_preset().config;
if (config.has("printer_agent")) {
std::string value = config.option<ConfigOptionString>("printer_agent")->value;
if (!value.empty()) {
agent_id = value;
}
}
// Switch agent in GUI_App
wxGetApp().switch_printer_agent(agent_id);
}
void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup)
{
auto description_line = [this](wxWindow* parent) {

View File

@@ -437,6 +437,7 @@ protected:
// return true if cancelled
bool tree_sel_change_delayed(wxCommandEvent& event);
void on_presets_changed();
void update_printer_agent_if_needed();
void build_preset_description_line(ConfigOptionsGroup* optgroup);
void update_preset_description_line();
void update_frequently_changed_parameters();

View File

@@ -62,7 +62,7 @@ TaskState parse_task_status(int status)
int TaskStateInfo::g_task_info_id = 0;
TaskStateInfo::TaskStateInfo(BBL::PrintParams param)
TaskStateInfo::TaskStateInfo(PrintParams param)
: m_state(TaskState::TS_PENDING)
, m_params(param)
, m_sending_percent(0)
@@ -103,8 +103,8 @@ TaskStateInfo::TaskStateInfo(BBL::PrintParams param)
int curr_percent = 0;
if (stage >= 0 && stage <= (int)PrintingStageFinished) {
curr_percent = StagePercentPoint[stage];
if ((stage == BBL::SendingPrintJobStage::PrintingStageUpload
|| stage == BBL::SendingPrintJobStage::PrintingStageRecord)
if ((stage == SendingPrintJobStage::PrintingStageUpload
|| stage == SendingPrintJobStage::PrintingStageRecord)
&& (code > 0 && code <= 100)) {
curr_percent = (StagePercentPoint[stage + 1] - StagePercentPoint[stage]) * code / 100 + StagePercentPoint[stage];
BOOST_LOG_TRIVIAL(trace) << "task_manager: percent = " << curr_percent;
@@ -156,7 +156,7 @@ TaskManager::TaskManager(NetworkAgent* agent)
}
int TaskManager::start_print(const std::vector<BBL::PrintParams>& params, TaskSettings* settings)
int TaskManager::start_print(const std::vector<PrintParams>& params, TaskSettings* settings)
{
BOOST_LOG_TRIVIAL(info) << "task_manager: start_print size = " << params.size();
TaskManager::MaxSendingAtSameTime = settings->max_sending_at_same_time;
@@ -173,7 +173,7 @@ int TaskManager::start_print(const std::vector<BBL::PrintParams>& params, TaskSe
return 0;
}
static int start_print_test(BBL::PrintParams& params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
static int start_print_test(PrintParams& params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
{
int tick = 2;
for (int i = 0; i < 100 * tick; i++) {
@@ -321,7 +321,7 @@ std::map<std::string, TaskStateInfo> TaskManager::get_task_list(int curr_page, i
{
std::map<std::string, TaskStateInfo> out;
if (m_agent) {
BBL::TaskQueryParams task_query_params;
TaskQueryParams task_query_params;
task_query_params.limit = page_count;
task_query_params.offset = curr_page * page_count;
std::string task_info;

View File

@@ -33,7 +33,7 @@ public:
static int g_task_info_id;
typedef std::function<void(TaskState state, int percent)> StateChangedFn;
TaskStateInfo(const BBL::PrintParams param);
TaskStateInfo(const PrintParams param);
TaskStateInfo() {
task_info_id = ++TaskStateInfo::g_task_info_id;
@@ -47,9 +47,9 @@ public:
m_state_changed_fn(m_state, m_sending_percent);
}
}
BBL::PrintParams get_params() { return m_params; }
PrintParams get_params() { return m_params; }
BBL::PrintParams& params() { return m_params; }
PrintParams& params() { return m_params; }
std::string get_job_id(){return profile_id;}
@@ -109,7 +109,7 @@ private:
TaskState m_state;
std::string m_task_name;
std::string m_device_name;
BBL::PrintParams m_params;
PrintParams m_params;
int m_sending_percent;
std::string m_job_id;
StateChangedFn m_state_changed_fn;
@@ -147,7 +147,7 @@ public:
static int SendingInterval;
TaskManager(NetworkAgent* agent);
int start_print(const std::vector<BBL::PrintParams>& params, TaskSettings* settings = nullptr);
int start_print(const std::vector<PrintParams>& params, TaskSettings* settings = nullptr);
static void set_max_send_at_same_time(int count);

View File

@@ -75,15 +75,11 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
CentreOnParent();
}
else {
std::string host_url = agent->get_bambulab_host();
TargetUrl = host_url + "/sign-in";
m_networkOk = false;
// Get the login URL from the cloud service agent
wxString strlang = wxGetApp().current_language_code_safe();
if (strlang != "") {
strlang.Replace("_", "-");
TargetUrl = host_url + "/" + strlang + "/sign-in";
}
strlang.Replace("_", "-");
TargetUrl = wxString::FromUTF8(agent->get_cloud_login_url(strlang.ToStdString()));
m_networkOk = TargetUrl.StartsWith("file://");
BOOST_LOG_TRIVIAL(info) << "login url = " << TargetUrl.ToStdString();
@@ -225,9 +221,9 @@ void ZUserLogin::OnDocumentLoaded(wxWebViewEvent &evt)
// Only notify if the document is the main frame, not a subframe
wxString tmpUrl = evt.GetURL();
NetworkAgent* agent = wxGetApp().getAgent();
std::string strHost = agent->get_bambulab_host();
std::string strHost = agent->get_cloud_service_host();
if ( tmpUrl.Contains(strHost) ) {
if (tmpUrl.StartsWith("file://") || tmpUrl.Contains(strHost)) {
m_networkOk = true;
// wxLogMessage("%s", "Document loaded; url='" + evt.GetURL() + "'");
}
@@ -268,28 +264,97 @@ void ZUserLogin::OnFullScreenChanged(wxWebViewEvent &evt)
void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
{
wxString str_input = evt.GetString();
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] OnScriptMessage received: " << str_input.ToStdString();
try {
json j = json::parse(into_u8(str_input));
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Parsed JSON successfully";
wxString strCmd = j["command"];
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Command: " << strCmd.ToStdString();
NetworkAgent* agent = wxGetApp().getAgent();
if (agent && strCmd == "get_login_cmd" && agent->get_cloud_agent()) {
// Return login config (backend_url, apikey, pkce)
// WebView handles provider selection internally
std::string login_cmd = agent->build_login_cmd();
m_loopback_port = 0;
try {
json cfg = json::parse(login_cmd);
if (cfg.contains("pkce")) {
const auto& pkce = cfg["pkce"];
if (pkce.contains("loopback_port")) {
if (pkce["loopback_port"].is_number_integer()) {
m_loopback_port = pkce["loopback_port"].get<int>();
} else if (pkce["loopback_port"].is_string()) {
m_loopback_port = std::stoi(pkce["loopback_port"].get<std::string>());
}
}
if (m_loopback_port <= 0 && pkce.contains("redirect_uri") && pkce["redirect_uri"].is_string()) {
const std::string redirect_uri = pkce["redirect_uri"].get<std::string>();
const char* prefixes[] = {"localhost:", "127.0.0.1:"};
for (const char* prefix : prefixes) {
auto start = redirect_uri.find(prefix);
if (start == std::string::npos)
continue;
start += strlen(prefix);
auto end = redirect_uri.find('/', start);
std::string port_str = redirect_uri.substr(start, end - start);
try {
m_loopback_port = std::stoi(port_str);
} catch (...) {
m_loopback_port = 0;
}
break;
}
}
}
} catch (...) {
m_loopback_port = 0;
}
wxString str_js = wxString::FromUTF8("window.postMessage(") + wxString::FromUTF8(login_cmd.c_str()) +
wxString::FromUTF8(", '*')");
this->RunScript(str_js);
return;
}
if (strCmd == "autotest_token")
{
m_AutotestToken = j["data"]["token"];
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Stored autotest_token";
}
if (strCmd == "user_login") {
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Processing user_login command";
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] User data: " << j["data"].dump();
j["data"]["autotest_token"] = m_AutotestToken;
wxGetApp().handle_script_message(j.dump());
Close();
std::string message_json = j.dump();
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Calling handle_script_message with: " << message_json;
// End modal dialog first to unblock event loop before processing callbacks
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Ending modal dialog";
EndModal(wxID_OK);
// Handle message after modal dialog ends to avoid deadlock
// Use wxTheApp->CallAfter to ensure it runs after modal loop exits
wxTheApp->CallAfter([message_json]() {
BOOST_LOG_TRIVIAL(debug) << "[WebUserLoginDialog] Processing login message after modal ended";
wxGetApp().handle_script_message(message_json);
});
}
else if (strCmd == "get_localhost_url") {
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: get_localhost_url";
wxGetApp().start_http_server();
BOOST_LOG_TRIVIAL(debug) << "thirdparty_login: get_localhost_url";
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
wxGetApp().start_http_server(loopback_port);
std::string sequence_id = j["sequence_id"].get<std::string>();
CallAfter([this, sequence_id] {
json ack_j;
ack_j["command"] = "get_localhost_url";
ack_j["response"]["base_url"] = std::string(LOCALHOST_URL) + std::to_string(LOCALHOST_PORT);
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
ack_j["response"]["base_url"] = std::string(LOCALHOST_URL) + std::to_string(loopback_port);
ack_j["response"]["result"] = "success";
ack_j["sequence_id"] = sequence_id;
wxString str_js = wxString::Format("window.postMessage(%s)", ack_j.dump());
@@ -300,6 +365,8 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: thirdparty_login";
if (j["data"].contains("url")) {
std::string jump_url = j["data"]["url"].get<std::string>();
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
wxGetApp().start_http_server(loopback_port);
CallAfter([this, jump_url] {
wxString url = wxString::FromUTF8(jump_url);
wxLaunchDefaultBrowser(url);

View File

@@ -71,6 +71,7 @@ private:
wxWebView *m_browser;
std::string m_AutotestToken;
int m_loopback_port { 0 };
#if wxUSE_WEBVIEW_IE
wxMenuItem *m_script_object_el;

View File

@@ -0,0 +1,864 @@
#include "BBLCloudServiceAgent.hpp"
#include "BBLNetworkPlugin.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
BBLCloudServiceAgent::BBLCloudServiceAgent()
{
BOOST_LOG_TRIVIAL(info) << "BBLCloudServiceAgent: Constructor - using BBLNetworkPlugin singleton";
}
BBLCloudServiceAgent::~BBLCloudServiceAgent() = default;
// ============================================================================
// Lifecycle (merged from BBLAuthAgent)
// ============================================================================
int BBLCloudServiceAgent::init_log()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_init_log();
if (func && agent) {
return func(agent);
}
return -1;
}
int BBLCloudServiceAgent::set_config_dir(std::string config_dir)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_config_dir();
if (func && agent) {
return func(agent, config_dir);
}
return -1;
}
int BBLCloudServiceAgent::set_cert_file(std::string folder, std::string filename)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_cert_file();
if (func && agent) {
return func(agent, folder, filename);
}
return -1;
}
int BBLCloudServiceAgent::set_country_code(std::string country_code)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_country_code();
if (func && agent) {
return func(agent, country_code);
}
return -1;
}
int BBLCloudServiceAgent::start()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start();
if (func && agent) {
return func(agent);
}
return -1;
}
// ============================================================================
// User Session Management (merged from BBLAuthAgent)
// ============================================================================
int BBLCloudServiceAgent::change_user(std::string user_info)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_change_user();
if (func && agent) {
return func(agent, user_info);
}
return -1;
}
bool BBLCloudServiceAgent::is_user_login()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_is_user_login();
if (func && agent) {
return func(agent);
}
return false;
}
int BBLCloudServiceAgent::user_logout(bool request)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_user_logout();
if (func && agent) {
return func(agent, request);
}
return -1;
}
std::string BBLCloudServiceAgent::get_user_id()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_id();
if (func && agent) {
return func(agent);
}
return "";
}
std::string BBLCloudServiceAgent::get_user_name()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_name();
if (func && agent) {
return func(agent);
}
return "";
}
std::string BBLCloudServiceAgent::get_user_avatar()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_avatar();
if (func && agent) {
return func(agent);
}
return "";
}
std::string BBLCloudServiceAgent::get_user_nickname()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_nickanme();
if (func && agent) {
return func(agent);
}
return "";
}
// ============================================================================
// Login UI Support (merged from BBLAuthAgent)
// ============================================================================
std::string BBLCloudServiceAgent::build_login_cmd()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_build_login_cmd();
if (func && agent) {
return func(agent);
}
return "";
}
std::string BBLCloudServiceAgent::build_logout_cmd()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_build_logout_cmd();
if (func && agent) {
return func(agent);
}
return "";
}
std::string BBLCloudServiceAgent::build_login_info()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_build_login_info();
if (func && agent) {
return func(agent);
}
return "";
}
// ============================================================================
// Token Access (merged from BBLAuthAgent)
// ============================================================================
std::string BBLCloudServiceAgent::get_access_token() const
{
// BBL DLL manages tokens internally, not exposed via function pointer
// Return empty string - BBL agents inject tokens automatically
return "";
}
std::string BBLCloudServiceAgent::get_refresh_token() const
{
// BBL DLL manages tokens internally, not exposed via function pointer
return "";
}
bool BBLCloudServiceAgent::ensure_token_fresh(const std::string& reason)
{
// BBL DLL handles token refresh internally
// Always return true assuming the DLL manages this
(void)reason;
return true;
}
// ============================================================================
// Auth Callbacks (merged from BBLAuthAgent)
// ============================================================================
int BBLCloudServiceAgent::set_on_user_login_fn(OnUserLoginFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_user_login_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
// ============================================================================
// Server Connectivity
// ============================================================================
std::string BBLCloudServiceAgent::get_cloud_service_host()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_bambulab_host();
if (func && agent) {
return func(agent);
}
return "";
}
std::string BBLCloudServiceAgent::get_cloud_login_url(const std::string& language)
{
std::string host_url = get_cloud_service_host();
if (host_url.empty()) {
return "";
}
if (language.empty()) {
return host_url + "/sign-in";
}
return host_url + "/" + language + "/sign-in";
}
int BBLCloudServiceAgent::connect_server()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_connect_server();
if (func && agent) {
return func(agent);
}
return -1;
}
bool BBLCloudServiceAgent::is_server_connected()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_is_server_connected();
if (func && agent) {
return func(agent);
}
return false;
}
int BBLCloudServiceAgent::refresh_connection()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_refresh_connection();
if (func && agent) {
return func(agent);
}
return -1;
}
int BBLCloudServiceAgent::start_subscribe(std::string module)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_subscribe();
if (func && agent) {
return func(agent, module);
}
return -1;
}
int BBLCloudServiceAgent::stop_subscribe(std::string module)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_stop_subscribe();
if (func && agent) {
return func(agent, module);
}
return -1;
}
int BBLCloudServiceAgent::add_subscribe(std::vector<std::string> dev_list)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_add_subscribe();
if (func && agent) {
return func(agent, dev_list);
}
return -1;
}
int BBLCloudServiceAgent::del_subscribe(std::vector<std::string> dev_list)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_del_subscribe();
if (func && agent) {
return func(agent, dev_list);
}
return -1;
}
void BBLCloudServiceAgent::enable_multi_machine(bool enable)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_enable_multi_machine();
if (func && agent) {
func(agent, enable);
}
}
// ============================================================================
// Settings Synchronization
// ============================================================================
int BBLCloudServiceAgent::get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_presets();
if (func && agent) {
return func(agent, user_presets);
}
return -1;
}
std::string BBLCloudServiceAgent::request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_request_setting_id();
if (func && agent) {
return func(agent, name, values_map, http_code);
}
return "";
}
int BBLCloudServiceAgent::put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_put_setting();
if (func && agent) {
return func(agent, setting_id, name, values_map, http_code);
}
return -1;
}
int BBLCloudServiceAgent::get_setting_list(std::string bundle_version, ProgressFn pro_fn, WasCancelledFn cancel_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_setting_list();
if (func && agent) {
return func(agent, bundle_version, pro_fn, cancel_fn);
}
return -1;
}
int BBLCloudServiceAgent::get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn, WasCancelledFn cancel_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_setting_list2();
if (func && agent) {
return func(agent, bundle_version, chk_fn, pro_fn, cancel_fn);
}
return -1;
}
int BBLCloudServiceAgent::delete_setting(std::string setting_id)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_delete_setting();
if (func && agent) {
return func(agent, setting_id);
}
return -1;
}
// ============================================================================
// Cloud User Services
// ============================================================================
int BBLCloudServiceAgent::get_my_message(int type, int after, int limit, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_my_message();
if (func && agent) {
return func(agent, type, after, limit, http_code, http_body);
}
return -1;
}
int BBLCloudServiceAgent::check_user_task_report(int* task_id, bool* printable)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_check_user_task_report();
if (func && agent) {
return func(agent, task_id, printable);
}
return -1;
}
int BBLCloudServiceAgent::get_user_print_info(unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_print_info();
if (func && agent) {
return func(agent, http_code, http_body);
}
return -1;
}
int BBLCloudServiceAgent::get_user_tasks(TaskQueryParams params, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_tasks();
if (func && agent) {
return func(agent, params, http_body);
}
return -1;
}
int BBLCloudServiceAgent::get_printer_firmware(std::string dev_id, unsigned* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_printer_firmware();
if (func && agent) {
return func(agent, dev_id, http_code, http_body);
}
return -1;
}
int BBLCloudServiceAgent::get_task_plate_index(std::string task_id, int* plate_index)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_task_plate_index();
if (func && agent) {
return func(agent, task_id, plate_index);
}
return -1;
}
int BBLCloudServiceAgent::get_user_info(int* identifier)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_info();
if (func && agent) {
return func(agent, identifier);
}
return -1;
}
int BBLCloudServiceAgent::get_subtask_info(std::string subtask_id, std::string* task_json, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_subtask_info();
if (func && agent) {
return func(agent, subtask_id, task_json, http_code, http_body);
}
return -1;
}
int BBLCloudServiceAgent::get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_slice_info();
if (func && agent) {
return func(agent, project_id, profile_id, plate_index, slice_json);
}
return -1;
}
int BBLCloudServiceAgent::query_bind_status(std::vector<std::string> query_list, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_query_bind_status();
if (func && agent) {
return func(agent, query_list, http_code, http_body);
}
return -1;
}
int BBLCloudServiceAgent::modify_printer_name(std::string dev_id, std::string dev_name)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_modify_printer_name();
if (func && agent) {
return func(agent, dev_id, dev_name);
}
return -1;
}
// ============================================================================
// Model Mall & Publishing
// ============================================================================
int BBLCloudServiceAgent::get_camera_url(std::string dev_id, std::function<void(std::string)> callback)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_camera_url();
if (func && agent) {
return func(agent, dev_id, callback);
}
return -1;
}
int BBLCloudServiceAgent::get_design_staffpick(int offset, int limit, std::function<void(std::string)> callback)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_design_staffpick();
if (func && agent) {
return func(agent, offset, limit, callback);
}
return -1;
}
int BBLCloudServiceAgent::start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_publish();
if (func && agent) {
return func(agent, params, update_fn, cancel_fn, out);
}
return -1;
}
int BBLCloudServiceAgent::get_model_publish_url(std::string* url)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_model_publish_url();
if (func && agent) {
return func(agent, url);
}
return -1;
}
int BBLCloudServiceAgent::get_subtask(BBLModelTask* task, OnGetSubTaskFn getsub_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_subtask();
if (func && agent) {
return func(agent, task, getsub_fn);
}
return -1;
}
int BBLCloudServiceAgent::get_model_mall_home_url(std::string* url)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_model_mall_home_url();
if (func && agent) {
return func(agent, url);
}
return -1;
}
int BBLCloudServiceAgent::get_model_mall_detail_url(std::string* url, std::string id)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_model_mall_detail_url();
if (func && agent) {
return func(agent, url, id);
}
return -1;
}
int BBLCloudServiceAgent::get_my_profile(std::string token, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_my_profile();
if (func && agent) {
return func(agent, token, http_code, http_body);
}
return -1;
}
// ============================================================================
// Analytics & Tracking
// ============================================================================
int BBLCloudServiceAgent::track_enable(bool enable)
{
m_enable_track = enable;
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_track_enable();
if (func && agent) {
return func(agent, enable);
}
return -1;
}
int BBLCloudServiceAgent::track_remove_files()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_track_remove_files();
if (func && agent) {
return func(agent);
}
return -1;
}
int BBLCloudServiceAgent::track_event(std::string evt_key, std::string content)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_track_event();
if (func && agent) {
return func(agent, evt_key, content);
}
return -1;
}
int BBLCloudServiceAgent::track_header(std::string header)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_track_header();
if (func && agent) {
return func(agent, header);
}
return -1;
}
int BBLCloudServiceAgent::track_update_property(std::string name, std::string value, std::string type)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_track_update_property();
if (func && agent) {
return func(agent, name, value, type);
}
return -1;
}
int BBLCloudServiceAgent::track_get_property(std::string name, std::string& value, std::string type)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_track_get_property();
if (func && agent) {
return func(agent, name, value, type);
}
return -1;
}
bool BBLCloudServiceAgent::get_track_enable()
{
return m_enable_track;
}
// ============================================================================
// Ratings & Reviews
// ============================================================================
int BBLCloudServiceAgent::put_model_mall_rating(int design_id, int score, std::string content, std::vector<std::string> images, unsigned int& http_code, std::string& http_error)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_put_model_mall_rating();
if (func && agent) {
return func(agent, design_id, score, content, images, http_code, http_error);
}
return -1;
}
int BBLCloudServiceAgent::get_oss_config(std::string& config, std::string country_code, unsigned int& http_code, std::string& http_error)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_oss_config();
if (func && agent) {
return func(agent, config, country_code, http_code, http_error);
}
return -1;
}
int BBLCloudServiceAgent::put_rating_picture_oss(std::string& config, std::string& pic_oss_path, std::string model_id, int profile_id, unsigned int& http_code, std::string& http_error)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_put_rating_picture_oss();
if (func && agent) {
return func(agent, config, pic_oss_path, model_id, profile_id, http_code, http_error);
}
return -1;
}
int BBLCloudServiceAgent::get_model_mall_rating_result(int job_id, std::string& rating_result, unsigned int& http_code, std::string& http_error)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_model_mall_rating_result();
if (func && agent) {
return func(agent, job_id, rating_result, http_code, http_error);
}
return -1;
}
// ============================================================================
// Extra Features
// ============================================================================
int BBLCloudServiceAgent::set_extra_http_header(std::map<std::string, std::string> extra_headers)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_extra_http_header();
if (func && agent) {
return func(agent, extra_headers);
}
return -1;
}
std::string BBLCloudServiceAgent::get_studio_info_url()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_studio_info_url();
if (func && agent) {
return func(agent);
}
return "";
}
int BBLCloudServiceAgent::get_mw_user_preference(std::function<void(std::string)> callback)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_mw_user_preference();
if (func && agent) {
return func(agent, callback);
}
return -1;
}
int BBLCloudServiceAgent::get_mw_user_4ulist(int seed, int limit, std::function<void(std::string)> callback)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_mw_user_4ulist();
if (func && agent) {
return func(agent, seed, limit, callback);
}
return -1;
}
std::string BBLCloudServiceAgent::get_version()
{
auto& plugin = BBLNetworkPlugin::instance();
auto func = plugin.get_get_version();
if (func) {
return func();
}
return "";
}
// ============================================================================
// Cloud Callbacks
// ============================================================================
int BBLCloudServiceAgent::set_on_server_connected_fn(OnServerConnectedFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_server_connected_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLCloudServiceAgent::set_on_http_error_fn(OnHttpErrorFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_http_error_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLCloudServiceAgent::set_get_country_code_fn(GetCountryCodeFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_get_country_code_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLCloudServiceAgent::set_queue_on_main_fn(QueueOnMainFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_queue_on_main_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
} // namespace Slic3r

View File

@@ -0,0 +1,136 @@
#ifndef __BBL_CLOUD_SERVICE_AGENT_HPP__
#define __BBL_CLOUD_SERVICE_AGENT_HPP__
#include "ICloudServiceAgent.hpp"
#include <string>
#include <memory>
namespace Slic3r {
/**
* BBLCloudServiceAgent - BBL DLL wrapper implementation of ICloudServiceAgent.
*
* Delegates all cloud service and authentication operations to the proprietary
* BBL network DLL through function pointers obtained from BBLNetworkPlugin singleton.
* This class combines the functionality of the former BBLAuthAgent and BBLCloudServiceAgent.
*/
class BBLCloudServiceAgent : public ICloudServiceAgent {
public:
BBLCloudServiceAgent();
~BBLCloudServiceAgent() override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Auth Methods
// ========================================================================
// Lifecycle
int init_log() override;
int set_config_dir(std::string config_dir) override;
int set_cert_file(std::string folder, std::string filename) override;
int set_country_code(std::string country_code) override;
int start() override;
// User Session Management
int change_user(std::string user_info) override;
bool is_user_login() override;
int user_logout(bool request = false) override;
std::string get_user_id() override;
std::string get_user_name() override;
std::string get_user_avatar() override;
std::string get_user_nickname() override;
// Login UI Support
std::string build_login_cmd() override;
std::string build_logout_cmd() override;
std::string build_login_info() override;
// Token Access (BBL manages tokens internally)
std::string get_access_token() const override;
std::string get_refresh_token() const override;
bool ensure_token_fresh(const std::string& reason) override;
// Auth Callbacks
int set_on_user_login_fn(OnUserLoginFn fn) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Cloud Methods
// ========================================================================
// Server Connectivity
std::string get_cloud_service_host() override;
std::string get_cloud_login_url(const std::string& language = "") override;
int connect_server() override;
bool is_server_connected() override;
int refresh_connection() override;
int start_subscribe(std::string module) override;
int stop_subscribe(std::string module) override;
int add_subscribe(std::vector<std::string> dev_list) override;
int del_subscribe(std::vector<std::string> dev_list) override;
void enable_multi_machine(bool enable) override;
// Settings Synchronization
int get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets) override;
std::string request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code) override;
int put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code) override;
int get_setting_list(std::string bundle_version, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int delete_setting(std::string setting_id) override;
// Cloud User Services
int get_my_message(int type, int after, int limit, unsigned int* http_code, std::string* http_body) override;
int check_user_task_report(int* task_id, bool* printable) override;
int get_user_print_info(unsigned int* http_code, std::string* http_body) override;
int get_user_tasks(TaskQueryParams params, std::string* http_body) override;
int get_printer_firmware(std::string dev_id, unsigned* http_code, std::string* http_body) override;
int get_task_plate_index(std::string task_id, int* plate_index) override;
int get_user_info(int* identifier) override;
int get_subtask_info(std::string subtask_id, std::string* task_json, unsigned int* http_code, std::string* http_body) override;
int get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json) override;
int query_bind_status(std::vector<std::string> query_list, unsigned int* http_code, std::string* http_body) override;
int modify_printer_name(std::string dev_id, std::string dev_name) override;
// Model Mall & Publishing
int get_camera_url(std::string dev_id, std::function<void(std::string)> callback) override;
int get_design_staffpick(int offset, int limit, std::function<void(std::string)> callback) override;
int start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out) override;
int get_model_publish_url(std::string* url) override;
int get_subtask(BBLModelTask* task, OnGetSubTaskFn getsub_fn) override;
int get_model_mall_home_url(std::string* url) override;
int get_model_mall_detail_url(std::string* url, std::string id) override;
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) override;
// Analytics & Tracking
int track_enable(bool enable) override;
int track_remove_files() override;
int track_event(std::string evt_key, std::string content) override;
int track_header(std::string header) override;
int track_update_property(std::string name, std::string value, std::string type = "string") override;
int track_get_property(std::string name, std::string& value, std::string type = "string") override;
bool get_track_enable() override;
// Ratings & Reviews
int put_model_mall_rating(int design_id, int score, std::string content, std::vector<std::string> images, unsigned int& http_code, std::string& http_error) override;
int get_oss_config(std::string& config, std::string country_code, unsigned int& http_code, std::string& http_error) override;
int put_rating_picture_oss(std::string& config, std::string& pic_oss_path, std::string model_id, int profile_id, unsigned int& http_code, std::string& http_error) override;
int get_model_mall_rating_result(int job_id, std::string& rating_result, unsigned int& http_code, std::string& http_error) override;
// Extra Features
int set_extra_http_header(std::map<std::string, std::string> extra_headers) override;
std::string get_studio_info_url() override;
int get_mw_user_preference(std::function<void(std::string)> callback) override;
int get_mw_user_4ulist(int seed, int limit, std::function<void(std::string)> callback) override;
std::string get_version() override;
// Cloud Callbacks
int set_on_server_connected_fn(OnServerConnectedFn fn) override;
int set_on_http_error_fn(OnHttpErrorFn fn) override;
int set_get_country_code_fn(GetCountryCodeFn fn) override;
int set_queue_on_main_fn(QueueOnMainFn fn) override;
private:
bool m_enable_track{false};
};
} // namespace Slic3r
#endif // __BBL_CLOUD_SERVICE_AGENT_HPP__

View File

@@ -0,0 +1,823 @@
#include "BBLNetworkPlugin.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <boost/log/trivial.hpp>
#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#include "libslic3r/Utils.hpp"
#include "slic3r/Utils/FileTransferUtils.hpp"
#if !defined(_MSC_VER) && !defined(_WIN32)
#include <dlfcn.h>
#endif
namespace Slic3r {
#define BAMBU_SOURCE_LIBRARY "BambuSource"
// ============================================================================
// Singleton Implementation
// ============================================================================
// Static pointer initialization (null by default, created on first access)
BBLNetworkPlugin* BBLNetworkPlugin::s_instance = nullptr;
BBLNetworkPlugin& BBLNetworkPlugin::instance()
{
static std::once_flag flag;
std::call_once(flag, [] {
s_instance = new BBLNetworkPlugin();
});
return *s_instance;
}
void BBLNetworkPlugin::shutdown()
{
// Note: Do not call instance() after shutdown() - the singleton is destroyed.
if (s_instance) {
delete s_instance;
s_instance = nullptr;
}
}
BBLNetworkPlugin::BBLNetworkPlugin() = default;
BBLNetworkPlugin::~BBLNetworkPlugin()
{
destroy_agent();
unload();
}
// ============================================================================
// Module Lifecycle
// ============================================================================
int BBLNetworkPlugin::initialize(bool using_backup, const std::string& version)
{
clear_load_error();
std::string library;
std::string data_dir_str = data_dir();
boost::filesystem::path data_dir_path(data_dir_str);
auto plugin_folder = data_dir_path / "plugins";
if (using_backup) {
plugin_folder = plugin_folder / "backup";
}
if (version.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": version is required but not provided";
set_load_error(
"Network library version not specified",
"A version must be specified to load the network library",
""
);
return -1;
}
// Auto-migration: If loading legacy version and versioned library doesn't exist,
// but unversioned legacy library does exist, rename it to versioned format
if (version == BAMBU_NETWORK_AGENT_VERSION_LEGACY) {
boost::filesystem::path versioned_path;
boost::filesystem::path legacy_path;
#if defined(_MSC_VER) || defined(_WIN32)
versioned_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dll");
legacy_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + ".dll");
#elif defined(__WXMAC__)
versioned_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dylib");
legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".dylib");
#else
versioned_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".so");
legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".so");
#endif
if (!boost::filesystem::exists(versioned_path) && boost::filesystem::exists(legacy_path)) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": auto-migrating unversioned legacy library to versioned format";
try {
boost::filesystem::rename(legacy_path, versioned_path);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": successfully renamed " << legacy_path.string() << " to "
<< versioned_path.string();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to rename legacy library: " << e.what();
}
}
}
// Load versioned library
#if defined(_MSC_VER) || defined(_WIN32)
library = plugin_folder.string() + "\\" + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dll";
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": loading versioned library at " << library;
#else
#if defined(__WXMAC__)
std::string lib_ext = ".dylib";
#else
std::string lib_ext = ".so";
#endif
library = plugin_folder.string() + "/" + std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + lib_ext;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": loading versioned library at " << library;
#endif
#if defined(_MSC_VER) || defined(_WIN32)
wchar_t lib_wstr[256];
memset(lib_wstr, 0, sizeof(lib_wstr));
::MultiByteToWideChar(CP_UTF8, NULL, library.c_str(), strlen(library.c_str())+1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0]));
m_networking_module = LoadLibrary(lib_wstr);
if (!m_networking_module) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": versioned library not found, trying current directory";
std::string library_path = get_libpath_in_current_directory(std::string(BAMBU_NETWORK_LIBRARY));
if (library_path.empty()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", can not get path in current directory for %1%") % BAMBU_NETWORK_LIBRARY;
set_load_error(
"Network library not found",
"Could not locate versioned library: " + library,
library
);
return -1;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", current path %1%")%library_path;
memset(lib_wstr, 0, sizeof(lib_wstr));
::MultiByteToWideChar(CP_UTF8, NULL, library_path.c_str(), strlen(library_path.c_str())+1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0]));
m_networking_module = LoadLibrary(lib_wstr);
}
#else
m_networking_module = dlopen(library.c_str(), RTLD_LAZY);
if (!m_networking_module) {
char* dll_error = dlerror();
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": dlopen failed: " << (dll_error ? dll_error : "unknown error");
set_load_error(
"Failed to load network library",
dll_error ? std::string(dll_error) : "Unknown dlopen error",
library
);
}
printf("after dlopen, network_module is %p\n", m_networking_module);
#endif
if (!m_networking_module) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", can not Load Library for %1%")%library;
if (!m_load_error.has_error) {
set_load_error(
"Network library failed to load",
"LoadLibrary/dlopen returned null",
library
);
}
return -1;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", successfully loaded library %1%, module %2%")%library %m_networking_module;
// Load file transfer interface
InitFTModule(m_networking_module);
// Load all function pointers
load_all_function_pointers();
if (m_get_version) {
std::string ver = m_get_version();
printf("network plugin version: %s\n", ver.c_str());
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": network plugin version = " << ver;
}
return 0;
}
int BBLNetworkPlugin::unload()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", network module %1%")%m_networking_module;
UnloadFTModule();
#if defined(_MSC_VER) || defined(_WIN32)
if (m_networking_module) {
FreeLibrary(m_networking_module);
m_networking_module = NULL;
}
if (m_source_module) {
FreeLibrary(m_source_module);
m_source_module = NULL;
}
#else
if (m_networking_module) {
dlclose(m_networking_module);
m_networking_module = NULL;
}
if (m_source_module) {
dlclose(m_source_module);
m_source_module = NULL;
}
#endif
clear_all_function_pointers();
return 0;
}
bool BBLNetworkPlugin::is_loaded() const
{
return m_networking_module != nullptr;
}
std::string BBLNetworkPlugin::get_version() const
{
bool consistent = true;
// Check the debug consistent first
if (m_check_debug_consistent) {
#if defined(NDEBUG)
consistent = m_check_debug_consistent(false);
#else
consistent = m_check_debug_consistent(true);
#endif
}
if (!consistent) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", inconsistent library, return 00.00.00.00!");
return "00.00.00.00";
}
if (m_get_version) {
return m_get_version();
}
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", get_version not supported, return 00.00.00.00!");
return "00.00.00.00";
}
// ============================================================================
// Agent Lifecycle
// ============================================================================
void* BBLNetworkPlugin::create_agent(const std::string& log_dir)
{
if (m_agent) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": agent already exists";
return m_agent;
}
if (m_create_agent) {
m_agent = m_create_agent(log_dir);
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", agent=%1%, create_agent=%2%, log_dir=%3%")
% m_agent % (m_create_agent ? "yes" : "no") % log_dir;
return m_agent;
}
int BBLNetworkPlugin::destroy_agent()
{
int ret = 0;
if (m_agent && m_destroy_agent) {
ret = m_destroy_agent(m_agent);
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", agent=%1%, destroy_agent=%2%, ret=%3%")
% m_agent % (m_destroy_agent ? "yes" : "no") % ret;
m_agent = nullptr;
return ret;
}
// ============================================================================
// DLL Module Accessors
// ============================================================================
#if defined(_MSC_VER) || defined(_WIN32)
HMODULE BBLNetworkPlugin::get_source_module()
#else
void* BBLNetworkPlugin::get_source_module()
#endif
{
if ((m_source_module) || (!m_networking_module))
return m_source_module;
std::string library;
std::string data_dir_str = data_dir();
boost::filesystem::path data_dir_path(data_dir_str);
auto plugin_folder = data_dir_path / "plugins";
#if defined(_MSC_VER) || defined(_WIN32)
wchar_t lib_wstr[128];
library = plugin_folder.string() + "/" + std::string(BAMBU_SOURCE_LIBRARY) + ".dll";
memset(lib_wstr, 0, sizeof(lib_wstr));
::MultiByteToWideChar(CP_UTF8, NULL, library.c_str(), strlen(library.c_str())+1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0]));
m_source_module = LoadLibrary(lib_wstr);
if (!m_source_module) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", try load BambuSource directly from current directory");
std::string library_path = get_libpath_in_current_directory(std::string(BAMBU_SOURCE_LIBRARY));
if (library_path.empty()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", can not get path in current directory for %1%") % BAMBU_SOURCE_LIBRARY;
return m_source_module;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", current path %1%")%library_path;
memset(lib_wstr, 0, sizeof(lib_wstr));
::MultiByteToWideChar(CP_UTF8, NULL, library_path.c_str(), strlen(library_path.c_str()) + 1, lib_wstr, sizeof(lib_wstr) / sizeof(lib_wstr[0]));
m_source_module = LoadLibrary(lib_wstr);
}
#else
#if defined(__WXMAC__)
library = plugin_folder.string() + "/" + std::string("lib") + std::string(BAMBU_SOURCE_LIBRARY) + ".dylib";
#else
library = plugin_folder.string() + "/" + std::string("lib") + std::string(BAMBU_SOURCE_LIBRARY) + ".so";
#endif
m_source_module = dlopen(library.c_str(), RTLD_LAZY);
#endif
return m_source_module;
}
void* BBLNetworkPlugin::get_function(const char* name)
{
void* function = nullptr;
if (!m_networking_module)
return function;
#if defined(_MSC_VER) || defined(_WIN32)
function = GetProcAddress(m_networking_module, name);
#else
function = dlsym(m_networking_module, name);
#endif
if (!function) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find function %1%")%name;
}
return function;
}
// ============================================================================
// Utility Methods
// ============================================================================
std::string BBLNetworkPlugin::get_libpath_in_current_directory(const std::string& library_name)
{
std::string lib_path;
#if defined(_MSC_VER) || defined(_WIN32)
wchar_t file_name[512];
DWORD ret = GetModuleFileNameW(NULL, file_name, 512);
if (!ret) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", GetModuleFileNameW return error, can not Load Library for %1%") % library_name;
return lib_path;
}
int size_needed = ::WideCharToMultiByte(0, 0, file_name, wcslen(file_name), nullptr, 0, nullptr, nullptr);
std::string file_name_string(size_needed, 0);
::WideCharToMultiByte(0, 0, file_name, wcslen(file_name), file_name_string.data(), size_needed, nullptr, nullptr);
std::size_t found = file_name_string.find("orca-slicer.exe");
if (found == (file_name_string.size() - 16)) {
lib_path = library_name + ".dll";
lib_path = file_name_string.replace(found, 16, lib_path);
}
#else
(void)library_name;
#endif
return lib_path;
}
std::string BBLNetworkPlugin::get_versioned_library_path(const std::string& version)
{
std::string data_dir_str = data_dir();
boost::filesystem::path data_dir_path(data_dir_str);
auto plugin_folder = data_dir_path / "plugins";
#if defined(_MSC_VER) || defined(_WIN32)
return (plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dll")).string();
#elif defined(__WXMAC__)
return (plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".dylib")).string();
#else
return (plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_" + version + ".so")).string();
#endif
}
bool BBLNetworkPlugin::versioned_library_exists(const std::string& version)
{
if (version.empty()) return false;
std::string path = get_versioned_library_path(version);
if (boost::filesystem::exists(path)) return true;
if (version == BAMBU_NETWORK_AGENT_VERSION_LEGACY) {
return legacy_library_exists();
}
return false;
}
bool BBLNetworkPlugin::legacy_library_exists()
{
std::string data_dir_str = data_dir();
boost::filesystem::path data_dir_path(data_dir_str);
auto plugin_folder = data_dir_path / "plugins";
#if defined(_MSC_VER) || defined(_WIN32)
auto legacy_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + ".dll");
#elif defined(__WXMAC__)
auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".dylib");
#else
auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".so");
#endif
return boost::filesystem::exists(legacy_path);
}
void BBLNetworkPlugin::remove_legacy_library()
{
std::string data_dir_str = data_dir();
boost::filesystem::path data_dir_path(data_dir_str);
auto plugin_folder = data_dir_path / "plugins";
#if defined(_MSC_VER) || defined(_WIN32)
auto legacy_path = plugin_folder / (std::string(BAMBU_NETWORK_LIBRARY) + ".dll");
#elif defined(__WXMAC__)
auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".dylib");
#else
auto legacy_path = plugin_folder / (std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + ".so");
#endif
if (boost::filesystem::exists(legacy_path)) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": removing legacy library at " << legacy_path.string();
boost::system::error_code ec;
boost::filesystem::remove(legacy_path, ec);
if (ec) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": failed to remove legacy library: " << ec.message();
}
}
}
std::vector<std::string> BBLNetworkPlugin::scan_plugin_versions()
{
std::vector<std::string> discovered_versions;
std::string data_dir_str = data_dir();
boost::filesystem::path plugin_folder = boost::filesystem::path(data_dir_str) / "plugins";
if (!boost::filesystem::is_directory(plugin_folder)) {
return discovered_versions;
}
#if defined(_MSC_VER) || defined(_WIN32)
std::string prefix = std::string(BAMBU_NETWORK_LIBRARY) + "_";
std::string extension = ".dll";
#elif defined(__WXMAC__)
std::string prefix = std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_";
std::string extension = ".dylib";
#else
std::string prefix = std::string("lib") + std::string(BAMBU_NETWORK_LIBRARY) + "_";
std::string extension = ".so";
#endif
boost::system::error_code ec;
for (auto& entry : boost::filesystem::directory_iterator(plugin_folder, ec)) {
if (ec) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": error iterating directory: " << ec.message();
break;
}
if (!boost::filesystem::is_regular_file(entry.status()))
continue;
std::string filename = entry.path().filename().string();
if (filename.rfind(prefix, 0) != 0)
continue;
if (filename.size() <= extension.size() ||
filename.compare(filename.size() - extension.size(), extension.size(), extension) != 0)
continue;
std::string version = filename.substr(prefix.size(),
filename.size() - prefix.size() - extension.size());
discovered_versions.push_back(version);
}
return discovered_versions;
}
// ============================================================================
// Error Handling
// ============================================================================
void BBLNetworkPlugin::clear_load_error()
{
m_load_error = NetworkLibraryLoadError{};
}
void BBLNetworkPlugin::set_load_error(const std::string& message,
const std::string& technical_details,
const std::string& attempted_path)
{
m_load_error.has_error = true;
m_load_error.message = message;
m_load_error.technical_details = technical_details;
m_load_error.attempted_path = attempted_path;
}
// ============================================================================
// Legacy Helper
// ============================================================================
PrintParams_Legacy BBLNetworkPlugin::as_legacy(PrintParams& param)
{
PrintParams_Legacy l;
l.dev_id = std::move(param.dev_id);
l.task_name = std::move(param.task_name);
l.project_name = std::move(param.project_name);
l.preset_name = std::move(param.preset_name);
l.filename = std::move(param.filename);
l.config_filename = std::move(param.config_filename);
l.plate_index = param.plate_index;
l.ftp_folder = std::move(param.ftp_folder);
l.ftp_file = std::move(param.ftp_file);
l.ftp_file_md5 = std::move(param.ftp_file_md5);
l.ams_mapping = std::move(param.ams_mapping);
l.ams_mapping_info = std::move(param.ams_mapping_info);
l.connection_type = std::move(param.connection_type);
l.comments = std::move(param.comments);
l.origin_profile_id = param.origin_profile_id;
l.stl_design_id = param.stl_design_id;
l.origin_model_id = std::move(param.origin_model_id);
l.print_type = std::move(param.print_type);
l.dst_file = std::move(param.dst_file);
l.dev_name = std::move(param.dev_name);
l.dev_ip = std::move(param.dev_ip);
l.use_ssl_for_ftp = param.use_ssl_for_ftp;
l.use_ssl_for_mqtt = param.use_ssl_for_mqtt;
l.username = std::move(param.username);
l.password = std::move(param.password);
l.task_bed_leveling = param.task_bed_leveling;
l.task_flow_cali = param.task_flow_cali;
l.task_vibration_cali = param.task_vibration_cali;
l.task_layer_inspect = param.task_layer_inspect;
l.task_record_timelapse = param.task_record_timelapse;
l.task_use_ams = param.task_use_ams;
l.task_bed_type = std::move(param.task_bed_type);
l.extra_options = std::move(param.extra_options);
return l;
}
// ============================================================================
// Function Pointer Loading
// ============================================================================
void BBLNetworkPlugin::load_all_function_pointers()
{
m_check_debug_consistent = reinterpret_cast<func_check_debug_consistent>(get_function("bambu_network_check_debug_consistent"));
m_get_version = reinterpret_cast<func_get_version>(get_function("bambu_network_get_version"));
m_create_agent = reinterpret_cast<func_create_agent>(get_function("bambu_network_create_agent"));
m_destroy_agent = reinterpret_cast<func_destroy_agent>(get_function("bambu_network_destroy_agent"));
m_init_log = reinterpret_cast<func_init_log>(get_function("bambu_network_init_log"));
m_set_config_dir = reinterpret_cast<func_set_config_dir>(get_function("bambu_network_set_config_dir"));
m_set_cert_file = reinterpret_cast<func_set_cert_file>(get_function("bambu_network_set_cert_file"));
m_set_country_code = reinterpret_cast<func_set_country_code>(get_function("bambu_network_set_country_code"));
m_start = reinterpret_cast<func_start>(get_function("bambu_network_start"));
m_set_on_ssdp_msg_fn = reinterpret_cast<func_set_on_ssdp_msg_fn>(get_function("bambu_network_set_on_ssdp_msg_fn"));
m_set_on_user_login_fn = reinterpret_cast<func_set_on_user_login_fn>(get_function("bambu_network_set_on_user_login_fn"));
m_set_on_printer_connected_fn = reinterpret_cast<func_set_on_printer_connected_fn>(get_function("bambu_network_set_on_printer_connected_fn"));
m_set_on_server_connected_fn = reinterpret_cast<func_set_on_server_connected_fn>(get_function("bambu_network_set_on_server_connected_fn"));
m_set_on_http_error_fn = reinterpret_cast<func_set_on_http_error_fn>(get_function("bambu_network_set_on_http_error_fn"));
m_set_get_country_code_fn = reinterpret_cast<func_set_get_country_code_fn>(get_function("bambu_network_set_get_country_code_fn"));
m_set_on_subscribe_failure_fn = reinterpret_cast<func_set_on_subscribe_failure_fn>(get_function("bambu_network_set_on_subscribe_failure_fn"));
m_set_on_message_fn = reinterpret_cast<func_set_on_message_fn>(get_function("bambu_network_set_on_message_fn"));
m_set_on_user_message_fn = reinterpret_cast<func_set_on_user_message_fn>(get_function("bambu_network_set_on_user_message_fn"));
m_set_on_local_connect_fn = reinterpret_cast<func_set_on_local_connect_fn>(get_function("bambu_network_set_on_local_connect_fn"));
m_set_on_local_message_fn = reinterpret_cast<func_set_on_local_message_fn>(get_function("bambu_network_set_on_local_message_fn"));
m_set_queue_on_main_fn = reinterpret_cast<func_set_queue_on_main_fn>(get_function("bambu_network_set_queue_on_main_fn"));
m_connect_server = reinterpret_cast<func_connect_server>(get_function("bambu_network_connect_server"));
m_is_server_connected = reinterpret_cast<func_is_server_connected>(get_function("bambu_network_is_server_connected"));
m_refresh_connection = reinterpret_cast<func_refresh_connection>(get_function("bambu_network_refresh_connection"));
m_start_subscribe = reinterpret_cast<func_start_subscribe>(get_function("bambu_network_start_subscribe"));
m_stop_subscribe = reinterpret_cast<func_stop_subscribe>(get_function("bambu_network_stop_subscribe"));
m_add_subscribe = reinterpret_cast<func_add_subscribe>(get_function("bambu_network_add_subscribe"));
m_del_subscribe = reinterpret_cast<func_del_subscribe>(get_function("bambu_network_del_subscribe"));
m_enable_multi_machine = reinterpret_cast<func_enable_multi_machine>(get_function("bambu_network_enable_multi_machine"));
m_send_message = reinterpret_cast<func_send_message>(get_function("bambu_network_send_message"));
m_connect_printer = reinterpret_cast<func_connect_printer>(get_function("bambu_network_connect_printer"));
m_disconnect_printer = reinterpret_cast<func_disconnect_printer>(get_function("bambu_network_disconnect_printer"));
m_send_message_to_printer = reinterpret_cast<func_send_message_to_printer>(get_function("bambu_network_send_message_to_printer"));
m_check_cert = reinterpret_cast<func_check_cert>(get_function("bambu_network_update_cert"));
m_install_device_cert = reinterpret_cast<func_install_device_cert>(get_function("bambu_network_install_device_cert"));
m_start_discovery = reinterpret_cast<func_start_discovery>(get_function("bambu_network_start_discovery"));
m_change_user = reinterpret_cast<func_change_user>(get_function("bambu_network_change_user"));
m_is_user_login = reinterpret_cast<func_is_user_login>(get_function("bambu_network_is_user_login"));
m_user_logout = reinterpret_cast<func_user_logout>(get_function("bambu_network_user_logout"));
m_get_user_id = reinterpret_cast<func_get_user_id>(get_function("bambu_network_get_user_id"));
m_get_user_name = reinterpret_cast<func_get_user_name>(get_function("bambu_network_get_user_name"));
m_get_user_avatar = reinterpret_cast<func_get_user_avatar>(get_function("bambu_network_get_user_avatar"));
m_get_user_nickanme = reinterpret_cast<func_get_user_nickanme>(get_function("bambu_network_get_user_nickanme"));
m_build_login_cmd = reinterpret_cast<func_build_login_cmd>(get_function("bambu_network_build_login_cmd"));
m_build_logout_cmd = reinterpret_cast<func_build_logout_cmd>(get_function("bambu_network_build_logout_cmd"));
m_build_login_info = reinterpret_cast<func_build_login_info>(get_function("bambu_network_build_login_info"));
m_ping_bind = reinterpret_cast<func_ping_bind>(get_function("bambu_network_ping_bind"));
m_bind_detect = reinterpret_cast<func_bind_detect>(get_function("bambu_network_bind_detect"));
m_set_server_callback = reinterpret_cast<func_set_server_callback>(get_function("bambu_network_set_server_callback"));
m_bind = reinterpret_cast<func_bind>(get_function("bambu_network_bind"));
m_unbind = reinterpret_cast<func_unbind>(get_function("bambu_network_unbind"));
m_get_bambulab_host = reinterpret_cast<func_get_bambulab_host>(get_function("bambu_network_get_bambulab_host"));
m_get_user_selected_machine = reinterpret_cast<func_get_user_selected_machine>(get_function("bambu_network_get_user_selected_machine"));
m_set_user_selected_machine = reinterpret_cast<func_set_user_selected_machine>(get_function("bambu_network_set_user_selected_machine"));
m_start_print = reinterpret_cast<func_start_print>(get_function("bambu_network_start_print"));
m_start_local_print_with_record = reinterpret_cast<func_start_local_print_with_record>(get_function("bambu_network_start_local_print_with_record"));
m_start_send_gcode_to_sdcard = reinterpret_cast<func_start_send_gcode_to_sdcard>(get_function("bambu_network_start_send_gcode_to_sdcard"));
m_start_local_print = reinterpret_cast<func_start_local_print>(get_function("bambu_network_start_local_print"));
m_start_sdcard_print = reinterpret_cast<func_start_sdcard_print>(get_function("bambu_network_start_sdcard_print"));
m_get_user_presets = reinterpret_cast<func_get_user_presets>(get_function("bambu_network_get_user_presets"));
m_request_setting_id = reinterpret_cast<func_request_setting_id>(get_function("bambu_network_request_setting_id"));
m_put_setting = reinterpret_cast<func_put_setting>(get_function("bambu_network_put_setting"));
m_get_setting_list = reinterpret_cast<func_get_setting_list>(get_function("bambu_network_get_setting_list"));
m_get_setting_list2 = reinterpret_cast<func_get_setting_list2>(get_function("bambu_network_get_setting_list2"));
m_delete_setting = reinterpret_cast<func_delete_setting>(get_function("bambu_network_delete_setting"));
m_get_studio_info_url = reinterpret_cast<func_get_studio_info_url>(get_function("bambu_network_get_studio_info_url"));
m_set_extra_http_header = reinterpret_cast<func_set_extra_http_header>(get_function("bambu_network_set_extra_http_header"));
m_get_my_message = reinterpret_cast<func_get_my_message>(get_function("bambu_network_get_my_message"));
m_check_user_task_report = reinterpret_cast<func_check_user_task_report>(get_function("bambu_network_check_user_task_report"));
m_get_user_print_info = reinterpret_cast<func_get_user_print_info>(get_function("bambu_network_get_user_print_info"));
m_get_user_tasks = reinterpret_cast<func_get_user_tasks>(get_function("bambu_network_get_user_tasks"));
m_get_printer_firmware = reinterpret_cast<func_get_printer_firmware>(get_function("bambu_network_get_printer_firmware"));
m_get_task_plate_index = reinterpret_cast<func_get_task_plate_index>(get_function("bambu_network_get_task_plate_index"));
m_get_user_info = reinterpret_cast<func_get_user_info>(get_function("bambu_network_get_user_info"));
m_request_bind_ticket = reinterpret_cast<func_request_bind_ticket>(get_function("bambu_network_request_bind_ticket"));
m_get_subtask_info = reinterpret_cast<func_get_subtask_info>(get_function("bambu_network_get_subtask_info"));
m_get_slice_info = reinterpret_cast<func_get_slice_info>(get_function("bambu_network_get_slice_info"));
m_query_bind_status = reinterpret_cast<func_query_bind_status>(get_function("bambu_network_query_bind_status"));
m_modify_printer_name = reinterpret_cast<func_modify_printer_name>(get_function("bambu_network_modify_printer_name"));
m_get_camera_url = reinterpret_cast<func_get_camera_url>(get_function("bambu_network_get_camera_url"));
m_get_design_staffpick = reinterpret_cast<func_get_design_staffpick>(get_function("bambu_network_get_design_staffpick"));
m_start_publish = reinterpret_cast<func_start_pubilsh>(get_function("bambu_network_start_publish"));
m_get_model_publish_url = reinterpret_cast<func_get_model_publish_url>(get_function("bambu_network_get_model_publish_url"));
m_get_subtask = reinterpret_cast<func_get_subtask>(get_function("bambu_network_get_subtask"));
m_get_model_mall_home_url = reinterpret_cast<func_get_model_mall_home_url>(get_function("bambu_network_get_model_mall_home_url"));
m_get_model_mall_detail_url = reinterpret_cast<func_get_model_mall_detail_url>(get_function("bambu_network_get_model_mall_detail_url"));
m_get_my_profile = reinterpret_cast<func_get_my_profile>(get_function("bambu_network_get_my_profile"));
m_track_enable = reinterpret_cast<func_track_enable>(get_function("bambu_network_track_enable"));
m_track_remove_files = reinterpret_cast<func_track_remove_files>(get_function("bambu_network_track_remove_files"));
m_track_event = reinterpret_cast<func_track_event>(get_function("bambu_network_track_event"));
m_track_header = reinterpret_cast<func_track_header>(get_function("bambu_network_track_header"));
m_track_update_property = reinterpret_cast<func_track_update_property>(get_function("bambu_network_track_update_property"));
m_track_get_property = reinterpret_cast<func_track_get_property>(get_function("bambu_network_track_get_property"));
m_put_model_mall_rating = reinterpret_cast<func_put_model_mall_rating_url>(get_function("bambu_network_put_model_mall_rating"));
m_get_oss_config = reinterpret_cast<func_get_oss_config>(get_function("bambu_network_get_oss_config"));
m_put_rating_picture_oss = reinterpret_cast<func_put_rating_picture_oss>(get_function("bambu_network_put_rating_picture_oss"));
m_get_model_mall_rating_result = reinterpret_cast<func_get_model_mall_rating_result>(get_function("bambu_network_get_model_mall_rating"));
m_get_mw_user_preference = reinterpret_cast<func_get_mw_user_preference>(get_function("bambu_network_get_mw_user_preference"));
m_get_mw_user_4ulist = reinterpret_cast<func_get_mw_user_4ulist>(get_function("bambu_network_get_mw_user_4ulist"));
}
void BBLNetworkPlugin::clear_all_function_pointers()
{
m_check_debug_consistent = nullptr;
m_get_version = nullptr;
m_create_agent = nullptr;
m_destroy_agent = nullptr;
m_init_log = nullptr;
m_set_config_dir = nullptr;
m_set_cert_file = nullptr;
m_set_country_code = nullptr;
m_start = nullptr;
m_set_on_ssdp_msg_fn = nullptr;
m_set_on_user_login_fn = nullptr;
m_set_on_printer_connected_fn = nullptr;
m_set_on_server_connected_fn = nullptr;
m_set_on_http_error_fn = nullptr;
m_set_get_country_code_fn = nullptr;
m_set_on_subscribe_failure_fn = nullptr;
m_set_on_message_fn = nullptr;
m_set_on_user_message_fn = nullptr;
m_set_on_local_connect_fn = nullptr;
m_set_on_local_message_fn = nullptr;
m_set_queue_on_main_fn = nullptr;
m_connect_server = nullptr;
m_is_server_connected = nullptr;
m_refresh_connection = nullptr;
m_start_subscribe = nullptr;
m_stop_subscribe = nullptr;
m_add_subscribe = nullptr;
m_del_subscribe = nullptr;
m_enable_multi_machine = nullptr;
m_send_message = nullptr;
m_connect_printer = nullptr;
m_disconnect_printer = nullptr;
m_send_message_to_printer = nullptr;
m_check_cert = nullptr;
m_install_device_cert = nullptr;
m_start_discovery = nullptr;
m_change_user = nullptr;
m_is_user_login = nullptr;
m_user_logout = nullptr;
m_get_user_id = nullptr;
m_get_user_name = nullptr;
m_get_user_avatar = nullptr;
m_get_user_nickanme = nullptr;
m_build_login_cmd = nullptr;
m_build_logout_cmd = nullptr;
m_build_login_info = nullptr;
m_ping_bind = nullptr;
m_bind_detect = nullptr;
m_set_server_callback = nullptr;
m_bind = nullptr;
m_unbind = nullptr;
m_get_bambulab_host = nullptr;
m_get_user_selected_machine = nullptr;
m_set_user_selected_machine = nullptr;
m_start_print = nullptr;
m_start_local_print_with_record = nullptr;
m_start_send_gcode_to_sdcard = nullptr;
m_start_local_print = nullptr;
m_start_sdcard_print = nullptr;
m_get_user_presets = nullptr;
m_request_setting_id = nullptr;
m_put_setting = nullptr;
m_get_setting_list = nullptr;
m_get_setting_list2 = nullptr;
m_delete_setting = nullptr;
m_get_studio_info_url = nullptr;
m_set_extra_http_header = nullptr;
m_get_my_message = nullptr;
m_check_user_task_report = nullptr;
m_get_user_print_info = nullptr;
m_get_user_tasks = nullptr;
m_get_printer_firmware = nullptr;
m_get_task_plate_index = nullptr;
m_get_user_info = nullptr;
m_request_bind_ticket = nullptr;
m_get_subtask_info = nullptr;
m_get_slice_info = nullptr;
m_query_bind_status = nullptr;
m_modify_printer_name = nullptr;
m_get_camera_url = nullptr;
m_get_design_staffpick = nullptr;
m_start_publish = nullptr;
m_get_model_publish_url = nullptr;
m_get_subtask = nullptr;
m_get_model_mall_home_url = nullptr;
m_get_model_mall_detail_url = nullptr;
m_get_my_profile = nullptr;
m_track_enable = nullptr;
m_track_remove_files = nullptr;
m_track_event = nullptr;
m_track_header = nullptr;
m_track_update_property = nullptr;
m_track_get_property = nullptr;
m_put_model_mall_rating = nullptr;
m_get_oss_config = nullptr;
m_put_rating_picture_oss = nullptr;
m_get_model_mall_rating_result = nullptr;
m_get_mw_user_preference = nullptr;
m_get_mw_user_4ulist = nullptr;
}
std::vector<NetworkLibraryVersionInfo> get_all_available_versions()
{
std::vector<NetworkLibraryVersionInfo> result;
std::set<std::string> known_base_versions;
std::set<std::string> all_known_versions;
for (size_t i = 0; i < AVAILABLE_NETWORK_VERSIONS_COUNT; ++i) {
result.push_back(NetworkLibraryVersionInfo::from_static(AVAILABLE_NETWORK_VERSIONS[i]));
known_base_versions.insert(AVAILABLE_NETWORK_VERSIONS[i].version);
all_known_versions.insert(AVAILABLE_NETWORK_VERSIONS[i].version);
}
std::vector<std::string> discovered = BBLNetworkPlugin::scan_plugin_versions();
std::vector<std::pair<std::string, std::string>> suffixed_versions;
for (const auto& version : discovered) {
if (all_known_versions.count(version) > 0)
continue;
std::string base = extract_base_version(version);
std::string suffix = extract_suffix(version);
if (suffix.empty())
continue;
if (known_base_versions.count(base) == 0)
continue;
suffixed_versions.emplace_back(base, version);
all_known_versions.insert(version);
}
std::sort(suffixed_versions.begin(), suffixed_versions.end(),
[](const auto& a, const auto& b) {
if (a.first != b.first) return a.first > b.first;
return a.second < b.second;
});
for (const auto& [base, full] : suffixed_versions) {
size_t insert_pos = 0;
for (size_t i = 0; i < result.size(); ++i) {
if (result[i].base_version == base) {
insert_pos = i + 1;
while (insert_pos < result.size() &&
result[insert_pos].base_version == base) {
++insert_pos;
}
break;
}
}
std::string sfx = extract_suffix(full);
result.insert(result.begin() + insert_pos,
NetworkLibraryVersionInfo::from_discovered(full, base, sfx));
}
return result;
}
} // namespace Slic3r

View File

@@ -0,0 +1,515 @@
#ifndef __BBL_NETWORK_PLUGIN_HPP__
#define __BBL_NETWORK_PLUGIN_HPP__
#include "bambu_networking.hpp"
#include "libslic3r/ProjectTask.hpp"
#include <string>
#include <memory>
#include <vector>
#include <map>
#include <functional>
#include <mutex>
#if defined(_MSC_VER) || defined(_WIN32)
#include <Windows.h>
#endif
namespace Slic3r {
// ============================================================================
// Function Pointer Types (copied from NetworkAgent.hpp)
// ============================================================================
typedef bool (*func_check_debug_consistent)(bool is_debug);
typedef std::string (*func_get_version)(void);
typedef void* (*func_create_agent)(std::string log_dir);
typedef int (*func_destroy_agent)(void *agent);
typedef int (*func_init_log)(void *agent);
typedef int (*func_set_config_dir)(void *agent, std::string config_dir);
typedef int (*func_set_cert_file)(void *agent, std::string folder, std::string filename);
typedef int (*func_set_country_code)(void *agent, std::string country_code);
typedef int (*func_start)(void *agent);
typedef int (*func_set_on_ssdp_msg_fn)(void *agent, OnMsgArrivedFn fn);
typedef int (*func_set_on_user_login_fn)(void *agent, OnUserLoginFn fn);
typedef int (*func_set_on_printer_connected_fn)(void *agent, OnPrinterConnectedFn fn);
typedef int (*func_set_on_server_connected_fn)(void *agent, OnServerConnectedFn fn);
typedef int (*func_set_on_http_error_fn)(void *agent, OnHttpErrorFn fn);
typedef int (*func_set_get_country_code_fn)(void *agent, GetCountryCodeFn fn);
typedef int (*func_set_on_subscribe_failure_fn)(void *agent, GetSubscribeFailureFn fn);
typedef int (*func_set_on_message_fn)(void *agent, OnMessageFn fn);
typedef int (*func_set_on_user_message_fn)(void *agent, OnMessageFn fn);
typedef int (*func_set_on_local_connect_fn)(void *agent, OnLocalConnectedFn fn);
typedef int (*func_set_on_local_message_fn)(void *agent, OnMessageFn fn);
typedef int (*func_set_queue_on_main_fn)(void *agent, QueueOnMainFn fn);
typedef int (*func_connect_server)(void *agent);
typedef bool (*func_is_server_connected)(void *agent);
typedef int (*func_refresh_connection)(void *agent);
typedef int (*func_start_subscribe)(void *agent, std::string module);
typedef int (*func_stop_subscribe)(void *agent, std::string module);
typedef int (*func_add_subscribe)(void *agent, std::vector<std::string> dev_list);
typedef int (*func_del_subscribe)(void *agent, std::vector<std::string> dev_list);
typedef void (*func_enable_multi_machine)(void *agent, bool enable);
typedef int (*func_send_message)(void *agent, std::string dev_id, std::string json_str, int qos, int flag);
typedef int (*func_connect_printer)(void *agent, std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl);
typedef int (*func_disconnect_printer)(void *agent);
typedef int (*func_send_message_to_printer)(void *agent, std::string dev_id, std::string json_str, int qos, int flag);
typedef int (*func_check_cert)(void* agent);
typedef void (*func_install_device_cert)(void* agent, std::string dev_id, bool lan_only);
typedef bool (*func_start_discovery)(void *agent, bool start, bool sending);
typedef int (*func_change_user)(void *agent, std::string user_info);
typedef bool (*func_is_user_login)(void *agent);
typedef int (*func_user_logout)(void *agent, bool request);
typedef std::string (*func_get_user_id)(void *agent);
typedef std::string (*func_get_user_name)(void *agent);
typedef std::string (*func_get_user_avatar)(void *agent);
typedef std::string (*func_get_user_nickanme)(void *agent);
typedef std::string (*func_build_login_cmd)(void *agent);
typedef std::string (*func_build_logout_cmd)(void *agent);
typedef std::string (*func_build_login_info)(void *agent);
typedef int (*func_ping_bind)(void *agent, std::string ping_code);
typedef int (*func_bind_detect)(void *agent, std::string dev_ip, std::string sec_link, detectResult& detect);
typedef int (*func_set_server_callback)(void *agent, OnServerErrFn fn);
typedef int (*func_bind)(void *agent, std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn);
typedef int (*func_unbind)(void *agent, std::string dev_id);
typedef std::string (*func_get_bambulab_host)(void *agent);
typedef std::string (*func_get_user_selected_machine)(void *agent);
typedef int (*func_set_user_selected_machine)(void *agent, std::string dev_id);
typedef int (*func_start_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_local_print_with_record)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_send_gcode_to_sdcard)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_local_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
typedef int (*func_start_sdcard_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
typedef int (*func_get_user_presets)(void *agent, std::map<std::string, std::map<std::string, std::string>>* user_presets);
typedef std::string (*func_request_setting_id)(void *agent, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
typedef int (*func_put_setting)(void *agent, std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
typedef int (*func_get_setting_list)(void *agent, std::string bundle_version, ProgressFn pro_fn, WasCancelledFn cancel_fn);
typedef int (*func_get_setting_list2)(void *agent, std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn, WasCancelledFn cancel_fn);
typedef int (*func_delete_setting)(void *agent, std::string setting_id);
typedef std::string (*func_get_studio_info_url)(void *agent);
typedef int (*func_set_extra_http_header)(void *agent, std::map<std::string, std::string> extra_headers);
typedef int (*func_get_my_message)(void *agent, int type, int after, int limit, unsigned int* http_code, std::string* http_body);
typedef int (*func_check_user_task_report)(void *agent, int* task_id, bool* printable);
typedef int (*func_get_user_print_info)(void *agent, unsigned int* http_code, std::string* http_body);
typedef int (*func_get_user_tasks)(void *agent, TaskQueryParams params, std::string* http_body);
typedef int (*func_get_printer_firmware)(void *agent, std::string dev_id, unsigned* http_code, std::string* http_body);
typedef int (*func_get_task_plate_index)(void *agent, std::string task_id, int* plate_index);
typedef int (*func_get_user_info)(void *agent, int* identifier);
typedef int (*func_request_bind_ticket)(void *agent, std::string* ticket);
typedef int (*func_get_subtask_info)(void *agent, std::string subtask_id, std::string* task_json, unsigned int* http_code, std::string *http_body);
typedef int (*func_get_slice_info)(void *agent, std::string project_id, std::string profile_id, int plate_index, std::string* slice_json);
typedef int (*func_query_bind_status)(void *agent, std::vector<std::string> query_list, unsigned int* http_code, std::string* http_body);
typedef int (*func_modify_printer_name)(void *agent, std::string dev_id, std::string dev_name);
typedef int (*func_get_camera_url)(void *agent, std::string dev_id, std::function<void(std::string)> callback);
typedef int (*func_get_design_staffpick)(void *agent, int offset, int limit, std::function<void(std::string)> callback);
typedef int (*func_start_pubilsh)(void *agent, PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out);
typedef int (*func_get_model_publish_url)(void *agent, std::string* url);
typedef int (*func_get_subtask)(void *agent, BBLModelTask* task, OnGetSubTaskFn getsub_fn);
typedef int (*func_get_model_mall_home_url)(void *agent, std::string* url);
typedef int (*func_get_model_mall_detail_url)(void *agent, std::string* url, std::string id);
typedef int (*func_get_my_profile)(void *agent, std::string token, unsigned int *http_code, std::string *http_body);
typedef int (*func_track_enable)(void *agent, bool enable);
typedef int (*func_track_remove_files)(void *agent);
typedef int (*func_track_event)(void *agent, std::string evt_key, std::string content);
typedef int (*func_track_header)(void *agent, std::string header);
typedef int (*func_track_update_property)(void *agent, std::string name, std::string value, std::string type);
typedef int (*func_track_get_property)(void *agent, std::string name, std::string& value, std::string type);
typedef int (*func_put_model_mall_rating_url)(void *agent, int rating_id, int score, std::string content, std::vector<std::string> images, unsigned int &http_code, std::string &http_error);
typedef int (*func_get_oss_config)(void *agent, std::string &config, std::string country_code, unsigned int &http_code, std::string &http_error);
typedef int (*func_put_rating_picture_oss)(void *agent, std::string &config, std::string &pic_oss_path, std::string model_id, int profile_id, unsigned int &http_code, std::string &http_error);
typedef int (*func_get_model_mall_rating_result)(void *agent, int job_id, std::string &rating_result, unsigned int &http_code, std::string &http_error);
typedef int (*func_get_mw_user_preference)(void *agent, std::function<void(std::string)> callback);
typedef int (*func_get_mw_user_4ulist)(void *agent, int seed, int limit, std::function<void(std::string)> callback);
// Legacy function pointer types (for older DLL versions)
typedef int (*func_start_print_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_local_print_with_record_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_send_gcode_to_sdcard_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_local_print_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
typedef int (*func_start_sdcard_print_legacy)(void* agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
typedef int (*func_send_message_legacy)(void* agent, std::string dev_id, std::string json_str, int qos);
typedef int (*func_send_message_to_printer_legacy)(void* agent, std::string dev_id, std::string json_str, int qos);
/**
* BBLNetworkPlugin - Singleton managing the Bambu Lab network DLL.
*
* Responsibilities:
* - Owns the DLL module handle (netwoking_module)
* - Owns the DLL source module handle (source_module)
* - Manages the shared void* agent handle
* - Provides all function pointers to BBL agents
*
* Usage:
* auto& plugin = BBLNetworkPlugin::instance();
* if (plugin.initialize(version)) {
* plugin.create_agent(log_dir);
* // Now BBLCloudServiceAgent/BBLPrinterAgent can use plugin
* }
*/
class BBLNetworkPlugin {
public:
// Singleton access
static BBLNetworkPlugin& instance();
// Delete copy/move
BBLNetworkPlugin(const BBLNetworkPlugin&) = delete;
BBLNetworkPlugin& operator=(const BBLNetworkPlugin&) = delete;
BBLNetworkPlugin(BBLNetworkPlugin&&) = delete;
BBLNetworkPlugin& operator=(BBLNetworkPlugin&&) = delete;
// ========================================================================
// Module Lifecycle
// ========================================================================
/**
* Load the network DLL from the plugins folder.
* @param using_backup If true, look in plugins/backup folder
* @param version Required version string (e.g., "01.09.05.01")
* @return 0 on success, -1 on failure
*/
int initialize(bool using_backup = false, const std::string& version = "");
/**
* Unload the network DLL and clear all function pointers.
* @return 0 on success
*/
int unload();
/**
* Destroy the singleton instance.
* Safe to call multiple times - does nothing if already destroyed.
* Must be called during application shutdown before main() returns.
*/
static void shutdown();
/**
* Check if DLL is currently loaded.
*/
bool is_loaded() const;
/**
* Get the plugin version string.
*/
std::string get_version() const;
// ========================================================================
// Agent Lifecycle
// ========================================================================
/**
* Create the shared agent handle.
* Only one agent can exist at a time.
* @param log_dir Directory for log files
* @return The created agent handle, or nullptr on failure
*/
void* create_agent(const std::string& log_dir);
/**
* Destroy the shared agent handle.
* @return 0 on success
*/
int destroy_agent();
/**
* Get the current agent handle.
* Returns nullptr if no agent created.
*/
void* get_agent() const { return m_agent; }
/**
* Check if an agent has been created.
*/
bool has_agent() const { return m_agent != nullptr; }
// ========================================================================
// DLL Module Accessors
// ========================================================================
#if defined(_MSC_VER) || defined(_WIN32)
HMODULE get_networking_module() const { return m_networking_module; }
HMODULE get_source_module();
#else
void* get_networking_module() const { return m_networking_module; }
void* get_source_module();
#endif
void* get_function(const char* name);
// Aliases for backward compatibility with NetworkAgent API
void* get_network_function(const char* name) { return get_function(name); }
#if defined(_MSC_VER) || defined(_WIN32)
HMODULE get_bambu_source_entry() { return get_source_module(); }
#else
void* get_bambu_source_entry() { return get_source_module(); }
#endif
// ========================================================================
// Utility Methods
// ========================================================================
static std::string get_libpath_in_current_directory(const std::string& library_name);
static std::string get_versioned_library_path(const std::string& version);
static bool versioned_library_exists(const std::string& version);
static bool legacy_library_exists();
static void remove_legacy_library();
static std::vector<std::string> scan_plugin_versions();
// ========================================================================
// Error Handling
// ========================================================================
NetworkLibraryLoadError get_load_error() const { return m_load_error; }
void clear_load_error();
void set_load_error(const std::string& message,
const std::string& technical_details,
const std::string& attempted_path);
// ========================================================================
// Legacy Network Flag
// ========================================================================
bool use_legacy_network() const { return m_use_legacy_network; }
void set_use_legacy_network(bool legacy) { m_use_legacy_network = legacy; }
// ========================================================================
// Function Pointer Accessors
// ========================================================================
func_check_debug_consistent get_check_debug_consistent() const { return m_check_debug_consistent; }
func_get_version get_get_version() const { return m_get_version; }
func_create_agent get_create_agent() const { return m_create_agent; }
func_destroy_agent get_destroy_agent() const { return m_destroy_agent; }
func_init_log get_init_log() const { return m_init_log; }
func_set_config_dir get_set_config_dir() const { return m_set_config_dir; }
func_set_cert_file get_set_cert_file() const { return m_set_cert_file; }
func_set_country_code get_set_country_code() const { return m_set_country_code; }
func_start get_start() const { return m_start; }
func_set_on_ssdp_msg_fn get_set_on_ssdp_msg_fn() const { return m_set_on_ssdp_msg_fn; }
func_set_on_user_login_fn get_set_on_user_login_fn() const { return m_set_on_user_login_fn; }
func_set_on_printer_connected_fn get_set_on_printer_connected_fn() const { return m_set_on_printer_connected_fn; }
func_set_on_server_connected_fn get_set_on_server_connected_fn() const { return m_set_on_server_connected_fn; }
func_set_on_http_error_fn get_set_on_http_error_fn() const { return m_set_on_http_error_fn; }
func_set_get_country_code_fn get_set_get_country_code_fn() const { return m_set_get_country_code_fn; }
func_set_on_subscribe_failure_fn get_set_on_subscribe_failure_fn() const { return m_set_on_subscribe_failure_fn; }
func_set_on_message_fn get_set_on_message_fn() const { return m_set_on_message_fn; }
func_set_on_user_message_fn get_set_on_user_message_fn() const { return m_set_on_user_message_fn; }
func_set_on_local_connect_fn get_set_on_local_connect_fn() const { return m_set_on_local_connect_fn; }
func_set_on_local_message_fn get_set_on_local_message_fn() const { return m_set_on_local_message_fn; }
func_set_queue_on_main_fn get_set_queue_on_main_fn() const { return m_set_queue_on_main_fn; }
func_connect_server get_connect_server() const { return m_connect_server; }
func_is_server_connected get_is_server_connected() const { return m_is_server_connected; }
func_refresh_connection get_refresh_connection() const { return m_refresh_connection; }
func_start_subscribe get_start_subscribe() const { return m_start_subscribe; }
func_stop_subscribe get_stop_subscribe() const { return m_stop_subscribe; }
func_add_subscribe get_add_subscribe() const { return m_add_subscribe; }
func_del_subscribe get_del_subscribe() const { return m_del_subscribe; }
func_enable_multi_machine get_enable_multi_machine() const { return m_enable_multi_machine; }
func_send_message get_send_message() const { return m_send_message; }
func_connect_printer get_connect_printer() const { return m_connect_printer; }
func_disconnect_printer get_disconnect_printer() const { return m_disconnect_printer; }
func_send_message_to_printer get_send_message_to_printer() const { return m_send_message_to_printer; }
func_check_cert get_check_cert() const { return m_check_cert; }
func_install_device_cert get_install_device_cert() const { return m_install_device_cert; }
func_start_discovery get_start_discovery() const { return m_start_discovery; }
func_change_user get_change_user() const { return m_change_user; }
func_is_user_login get_is_user_login() const { return m_is_user_login; }
func_user_logout get_user_logout() const { return m_user_logout; }
func_get_user_id get_get_user_id() const { return m_get_user_id; }
func_get_user_name get_get_user_name() const { return m_get_user_name; }
func_get_user_avatar get_get_user_avatar() const { return m_get_user_avatar; }
func_get_user_nickanme get_get_user_nickanme() const { return m_get_user_nickanme; }
func_build_login_cmd get_build_login_cmd() const { return m_build_login_cmd; }
func_build_logout_cmd get_build_logout_cmd() const { return m_build_logout_cmd; }
func_build_login_info get_build_login_info() const { return m_build_login_info; }
func_ping_bind get_ping_bind() const { return m_ping_bind; }
func_bind_detect get_bind_detect() const { return m_bind_detect; }
func_set_server_callback get_set_server_callback() const { return m_set_server_callback; }
func_bind get_bind() const { return m_bind; }
func_unbind get_unbind() const { return m_unbind; }
func_get_bambulab_host get_get_bambulab_host() const { return m_get_bambulab_host; }
func_get_user_selected_machine get_get_user_selected_machine() const { return m_get_user_selected_machine; }
func_set_user_selected_machine get_set_user_selected_machine() const { return m_set_user_selected_machine; }
func_start_print get_start_print() const { return m_start_print; }
func_start_local_print_with_record get_start_local_print_with_record() const { return m_start_local_print_with_record; }
func_start_send_gcode_to_sdcard get_start_send_gcode_to_sdcard() const { return m_start_send_gcode_to_sdcard; }
func_start_local_print get_start_local_print() const { return m_start_local_print; }
func_start_sdcard_print get_start_sdcard_print() const { return m_start_sdcard_print; }
func_get_user_presets get_get_user_presets() const { return m_get_user_presets; }
func_request_setting_id get_request_setting_id() const { return m_request_setting_id; }
func_put_setting get_put_setting() const { return m_put_setting; }
func_get_setting_list get_get_setting_list() const { return m_get_setting_list; }
func_get_setting_list2 get_get_setting_list2() const { return m_get_setting_list2; }
func_delete_setting get_delete_setting() const { return m_delete_setting; }
func_get_studio_info_url get_get_studio_info_url() const { return m_get_studio_info_url; }
func_set_extra_http_header get_set_extra_http_header() const { return m_set_extra_http_header; }
func_get_my_message get_get_my_message() const { return m_get_my_message; }
func_check_user_task_report get_check_user_task_report() const { return m_check_user_task_report; }
func_get_user_print_info get_get_user_print_info() const { return m_get_user_print_info; }
func_get_user_tasks get_get_user_tasks() const { return m_get_user_tasks; }
func_get_printer_firmware get_get_printer_firmware() const { return m_get_printer_firmware; }
func_get_task_plate_index get_get_task_plate_index() const { return m_get_task_plate_index; }
func_get_user_info get_get_user_info() const { return m_get_user_info; }
func_request_bind_ticket get_request_bind_ticket() const { return m_request_bind_ticket; }
func_get_subtask_info get_get_subtask_info() const { return m_get_subtask_info; }
func_get_slice_info get_get_slice_info() const { return m_get_slice_info; }
func_query_bind_status get_query_bind_status() const { return m_query_bind_status; }
func_modify_printer_name get_modify_printer_name() const { return m_modify_printer_name; }
func_get_camera_url get_get_camera_url() const { return m_get_camera_url; }
func_get_design_staffpick get_get_design_staffpick() const { return m_get_design_staffpick; }
func_start_pubilsh get_start_publish() const { return m_start_publish; }
func_get_model_publish_url get_get_model_publish_url() const { return m_get_model_publish_url; }
func_get_subtask get_get_subtask() const { return m_get_subtask; }
func_get_model_mall_home_url get_get_model_mall_home_url() const { return m_get_model_mall_home_url; }
func_get_model_mall_detail_url get_get_model_mall_detail_url() const { return m_get_model_mall_detail_url; }
func_get_my_profile get_get_my_profile() const { return m_get_my_profile; }
func_track_enable get_track_enable() const { return m_track_enable; }
func_track_remove_files get_track_remove_files() const { return m_track_remove_files; }
func_track_event get_track_event() const { return m_track_event; }
func_track_header get_track_header() const { return m_track_header; }
func_track_update_property get_track_update_property() const { return m_track_update_property; }
func_track_get_property get_track_get_property() const { return m_track_get_property; }
func_put_model_mall_rating_url get_put_model_mall_rating() const { return m_put_model_mall_rating; }
func_get_oss_config get_get_oss_config() const { return m_get_oss_config; }
func_put_rating_picture_oss get_put_rating_picture_oss() const { return m_put_rating_picture_oss; }
func_get_model_mall_rating_result get_get_model_mall_rating_result() const { return m_get_model_mall_rating_result; }
func_get_mw_user_preference get_get_mw_user_preference() const { return m_get_mw_user_preference; }
func_get_mw_user_4ulist get_get_mw_user_4ulist() const { return m_get_mw_user_4ulist; }
// ========================================================================
// Legacy Helper
// ========================================================================
static PrintParams_Legacy as_legacy(PrintParams& param);
private:
// Singleton instance pointer (heap-allocated for explicit lifetime control)
static BBLNetworkPlugin* s_instance;
BBLNetworkPlugin();
~BBLNetworkPlugin();
void load_all_function_pointers();
void clear_all_function_pointers();
// Module handles
#if defined(_MSC_VER) || defined(_WIN32)
HMODULE m_networking_module{nullptr};
HMODULE m_source_module{nullptr};
#else
void* m_networking_module{nullptr};
void* m_source_module{nullptr};
#endif
// Shared agent handle
void* m_agent{nullptr};
// Load error state
NetworkLibraryLoadError m_load_error;
// Legacy network compatibility flag
bool m_use_legacy_network{true};
// Function pointers
func_check_debug_consistent m_check_debug_consistent{nullptr};
func_get_version m_get_version{nullptr};
func_create_agent m_create_agent{nullptr};
func_destroy_agent m_destroy_agent{nullptr};
func_init_log m_init_log{nullptr};
func_set_config_dir m_set_config_dir{nullptr};
func_set_cert_file m_set_cert_file{nullptr};
func_set_country_code m_set_country_code{nullptr};
func_start m_start{nullptr};
func_set_on_ssdp_msg_fn m_set_on_ssdp_msg_fn{nullptr};
func_set_on_user_login_fn m_set_on_user_login_fn{nullptr};
func_set_on_printer_connected_fn m_set_on_printer_connected_fn{nullptr};
func_set_on_server_connected_fn m_set_on_server_connected_fn{nullptr};
func_set_on_http_error_fn m_set_on_http_error_fn{nullptr};
func_set_get_country_code_fn m_set_get_country_code_fn{nullptr};
func_set_on_subscribe_failure_fn m_set_on_subscribe_failure_fn{nullptr};
func_set_on_message_fn m_set_on_message_fn{nullptr};
func_set_on_user_message_fn m_set_on_user_message_fn{nullptr};
func_set_on_local_connect_fn m_set_on_local_connect_fn{nullptr};
func_set_on_local_message_fn m_set_on_local_message_fn{nullptr};
func_set_queue_on_main_fn m_set_queue_on_main_fn{nullptr};
func_connect_server m_connect_server{nullptr};
func_is_server_connected m_is_server_connected{nullptr};
func_refresh_connection m_refresh_connection{nullptr};
func_start_subscribe m_start_subscribe{nullptr};
func_stop_subscribe m_stop_subscribe{nullptr};
func_add_subscribe m_add_subscribe{nullptr};
func_del_subscribe m_del_subscribe{nullptr};
func_enable_multi_machine m_enable_multi_machine{nullptr};
func_send_message m_send_message{nullptr};
func_connect_printer m_connect_printer{nullptr};
func_disconnect_printer m_disconnect_printer{nullptr};
func_send_message_to_printer m_send_message_to_printer{nullptr};
func_check_cert m_check_cert{nullptr};
func_install_device_cert m_install_device_cert{nullptr};
func_start_discovery m_start_discovery{nullptr};
func_change_user m_change_user{nullptr};
func_is_user_login m_is_user_login{nullptr};
func_user_logout m_user_logout{nullptr};
func_get_user_id m_get_user_id{nullptr};
func_get_user_name m_get_user_name{nullptr};
func_get_user_avatar m_get_user_avatar{nullptr};
func_get_user_nickanme m_get_user_nickanme{nullptr};
func_build_login_cmd m_build_login_cmd{nullptr};
func_build_logout_cmd m_build_logout_cmd{nullptr};
func_build_login_info m_build_login_info{nullptr};
func_ping_bind m_ping_bind{nullptr};
func_bind_detect m_bind_detect{nullptr};
func_set_server_callback m_set_server_callback{nullptr};
func_bind m_bind{nullptr};
func_unbind m_unbind{nullptr};
func_get_bambulab_host m_get_bambulab_host{nullptr};
func_get_user_selected_machine m_get_user_selected_machine{nullptr};
func_set_user_selected_machine m_set_user_selected_machine{nullptr};
func_start_print m_start_print{nullptr};
func_start_local_print_with_record m_start_local_print_with_record{nullptr};
func_start_send_gcode_to_sdcard m_start_send_gcode_to_sdcard{nullptr};
func_start_local_print m_start_local_print{nullptr};
func_start_sdcard_print m_start_sdcard_print{nullptr};
func_get_user_presets m_get_user_presets{nullptr};
func_request_setting_id m_request_setting_id{nullptr};
func_put_setting m_put_setting{nullptr};
func_get_setting_list m_get_setting_list{nullptr};
func_get_setting_list2 m_get_setting_list2{nullptr};
func_delete_setting m_delete_setting{nullptr};
func_get_studio_info_url m_get_studio_info_url{nullptr};
func_set_extra_http_header m_set_extra_http_header{nullptr};
func_get_my_message m_get_my_message{nullptr};
func_check_user_task_report m_check_user_task_report{nullptr};
func_get_user_print_info m_get_user_print_info{nullptr};
func_get_user_tasks m_get_user_tasks{nullptr};
func_get_printer_firmware m_get_printer_firmware{nullptr};
func_get_task_plate_index m_get_task_plate_index{nullptr};
func_get_user_info m_get_user_info{nullptr};
func_request_bind_ticket m_request_bind_ticket{nullptr};
func_get_subtask_info m_get_subtask_info{nullptr};
func_get_slice_info m_get_slice_info{nullptr};
func_query_bind_status m_query_bind_status{nullptr};
func_modify_printer_name m_modify_printer_name{nullptr};
func_get_camera_url m_get_camera_url{nullptr};
func_get_design_staffpick m_get_design_staffpick{nullptr};
func_start_pubilsh m_start_publish{nullptr};
func_get_model_publish_url m_get_model_publish_url{nullptr};
func_get_subtask m_get_subtask{nullptr};
func_get_model_mall_home_url m_get_model_mall_home_url{nullptr};
func_get_model_mall_detail_url m_get_model_mall_detail_url{nullptr};
func_get_my_profile m_get_my_profile{nullptr};
func_track_enable m_track_enable{nullptr};
func_track_remove_files m_track_remove_files{nullptr};
func_track_event m_track_event{nullptr};
func_track_header m_track_header{nullptr};
func_track_update_property m_track_update_property{nullptr};
func_track_get_property m_track_get_property{nullptr};
func_put_model_mall_rating_url m_put_model_mall_rating{nullptr};
func_get_oss_config m_get_oss_config{nullptr};
func_put_rating_picture_oss m_put_rating_picture_oss{nullptr};
func_get_model_mall_rating_result m_get_model_mall_rating_result{nullptr};
func_get_mw_user_preference m_get_mw_user_preference{nullptr};
func_get_mw_user_4ulist m_get_mw_user_4ulist{nullptr};
};
} // namespace Slic3r
#endif // __BBL_NETWORK_PLUGIN_HPP__

View File

@@ -0,0 +1,379 @@
#include "BBLPrinterAgent.hpp"
#include "BBLNetworkPlugin.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
BBLPrinterAgent::BBLPrinterAgent()
{
BOOST_LOG_TRIVIAL(info) << "BBLPrinterAgent: Constructor - using BBLNetworkPlugin singleton";
}
BBLPrinterAgent::~BBLPrinterAgent() = default;
void BBLPrinterAgent::set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud)
{
m_cloud_agent = cloud;
// BBL DLL manages tokens internally, so this is just for interface compliance
}
// ============================================================================
// Communication
// ============================================================================
int BBLPrinterAgent::send_message(std::string dev_id, std::string json_str, int qos, int flag)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_send_message();
if (func && agent) {
return func(agent, dev_id, json_str, qos, flag);
}
return -1;
}
int BBLPrinterAgent::connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_connect_printer();
if (func && agent) {
return func(agent, dev_id, dev_ip, username, password, use_ssl);
}
return -1;
}
int BBLPrinterAgent::disconnect_printer()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_disconnect_printer();
if (func && agent) {
return func(agent);
}
return -1;
}
int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_send_message_to_printer();
if (func && agent) {
return func(agent, dev_id, json_str, qos, flag);
}
return -1;
}
// ============================================================================
// Certificates
// ============================================================================
int BBLPrinterAgent::check_cert()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_check_cert();
if (func && agent) {
return func(agent);
}
return -1;
}
void BBLPrinterAgent::install_device_cert(std::string dev_id, bool lan_only)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_install_device_cert();
if (func && agent) {
func(agent, dev_id, lan_only);
}
}
// ============================================================================
// Discovery
// ============================================================================
bool BBLPrinterAgent::start_discovery(bool start, bool sending)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_discovery();
if (func && agent) {
return func(agent, start, sending);
}
return false;
}
// ============================================================================
// Binding
// ============================================================================
int BBLPrinterAgent::ping_bind(std::string ping_code)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_ping_bind();
if (func && agent) {
return func(agent, ping_code);
}
return -1;
}
int BBLPrinterAgent::bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_bind_detect();
if (func && agent) {
return func(agent, dev_ip, sec_link, detect);
}
return -1;
}
int BBLPrinterAgent::bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_bind();
if (func && agent) {
return func(agent, dev_ip, dev_id, sec_link, timezone, improved, update_fn);
}
return -1;
}
int BBLPrinterAgent::unbind(std::string dev_id)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_unbind();
if (func && agent) {
return func(agent, dev_id);
}
return -1;
}
int BBLPrinterAgent::request_bind_ticket(std::string* ticket)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_request_bind_ticket();
if (func && agent) {
return func(agent, ticket);
}
return -1;
}
int BBLPrinterAgent::set_server_callback(OnServerErrFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_server_callback();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
// ============================================================================
// Machine Selection
// ============================================================================
std::string BBLPrinterAgent::get_user_selected_machine()
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_user_selected_machine();
if (func && agent) {
return func(agent);
}
return "";
}
int BBLPrinterAgent::set_user_selected_machine(std::string dev_id)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_user_selected_machine();
if (func && agent) {
return func(agent, dev_id);
}
return -1;
}
// ============================================================================
// Agent Information
// ============================================================================
AgentInfo BBLPrinterAgent::get_agent_info_static()
{
return AgentInfo{
.id = "bbl",
.name = "Bambu Lab Printer Agent",
.version = "",
.description = "Bambu Lab printer agent"
};
}
// ============================================================================
// Print Job Operations
// ============================================================================
int BBLPrinterAgent::start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_print();
if (func && agent) {
return func(agent, params, update_fn, cancel_fn, wait_fn);
}
return -1;
}
int BBLPrinterAgent::start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_local_print_with_record();
if (func && agent) {
return func(agent, params, update_fn, cancel_fn, wait_fn);
}
return -1;
}
int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_send_gcode_to_sdcard();
if (func && agent) {
return func(agent, params, update_fn, cancel_fn, wait_fn);
}
return -1;
}
int BBLPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_local_print();
if (func && agent) {
return func(agent, params, update_fn, cancel_fn);
}
return -1;
}
int BBLPrinterAgent::start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_start_sdcard_print();
if (func && agent) {
return func(agent, params, update_fn, cancel_fn);
}
return -1;
}
// ============================================================================
// Callbacks
// ============================================================================
int BBLPrinterAgent::set_on_ssdp_msg_fn(OnMsgArrivedFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_ssdp_msg_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_on_printer_connected_fn(OnPrinterConnectedFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_printer_connected_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_on_subscribe_failure_fn(GetSubscribeFailureFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_subscribe_failure_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_on_message_fn(OnMessageFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_message_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_on_user_message_fn(OnMessageFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_user_message_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_on_local_connect_fn(OnLocalConnectedFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_local_connect_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_on_local_message_fn(OnMessageFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_on_local_message_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
int BBLPrinterAgent::set_queue_on_main_fn(QueueOnMainFn fn)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_set_queue_on_main_fn();
if (func && agent) {
return func(agent, fn);
}
return -1;
}
// ============================================================================
// Filament Operations
// ============================================================================
FilamentSyncMode BBLPrinterAgent::get_filament_sync_mode() const
{
// BBL uses MQTT subscription for real-time filament updates
return FilamentSyncMode::subscription;
}
} // namespace Slic3r

View File

@@ -0,0 +1,86 @@
#ifndef __BBL_PRINTER_AGENT_HPP__
#define __BBL_PRINTER_AGENT_HPP__
#include "IPrinterAgent.hpp"
#include "ICloudServiceAgent.hpp"
#include <string>
#include <memory>
namespace Slic3r {
/**
* BBLPrinterAgent - BBL DLL wrapper implementation of IPrinterAgent.
*
* Delegates all printer operations to the proprietary BBL network DLL
* through function pointers obtained from BBLNetworkPlugin singleton.
*/
class BBLPrinterAgent : public IPrinterAgent {
public:
BBLPrinterAgent();
~BBLPrinterAgent() override;
// Cloud Agent Dependency (not used by BBL - tokens managed internally)
void set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud) override;
// ========================================================================
// IPrinterAgent Interface Implementation
// ========================================================================
// Communication
int send_message(std::string dev_id, std::string json_str, int qos, int flag) override;
int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override;
int disconnect_printer() override;
int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) override;
// Certificates
int check_cert() override;
void install_device_cert(std::string dev_id, bool lan_only) override;
// Discovery
bool start_discovery(bool start, bool sending) override;
// Binding
int ping_bind(std::string ping_code) override;
int bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect) override;
int bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn) override;
int unbind(std::string dev_id) override;
int request_bind_ticket(std::string* ticket) override;
int set_server_callback(OnServerErrFn fn) override;
// Machine Selection
std::string get_user_selected_machine() override;
int set_user_selected_machine(std::string dev_id) override;
/**
* Get agent information.
*
* @return AgentInfo struct containing agent identification and descriptive information
*/
static AgentInfo get_agent_info_static();
AgentInfo get_agent_info() override { return get_agent_info_static(); }
// Print Job Operations
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
// Callbacks
int set_on_ssdp_msg_fn(OnMsgArrivedFn fn) override;
int set_on_printer_connected_fn(OnPrinterConnectedFn fn) override;
int set_on_subscribe_failure_fn(GetSubscribeFailureFn fn) override;
int set_on_message_fn(OnMessageFn fn) override;
int set_on_user_message_fn(OnMessageFn fn) override;
int set_on_local_connect_fn(OnLocalConnectedFn fn) override;
int set_on_local_message_fn(OnMessageFn fn) override;
int set_queue_on_main_fn(QueueOnMainFn fn) override;
FilamentSyncMode get_filament_sync_mode() const override;
private:
std::shared_ptr<ICloudServiceAgent> m_cloud_agent;
};
} // namespace Slic3r
#endif // __BBL_PRINTER_AGENT_HPP__

View File

@@ -110,6 +110,9 @@ struct Http::priv
::curl_httppost *form_end;
::curl_mime* mime;
::curl_slist *headerlist;
// For debug printing
std::string url;
std::string method;
// Used for reading the body
std::string buffer;
// Used for storing file streams added as multipart form parts
@@ -170,6 +173,8 @@ Http::priv::priv(const std::string &url)
, form_end(nullptr)
, mime(nullptr)
, headerlist(nullptr)
, url(url)
, method("GET")
, error_buffer(CURL_ERROR_SIZE + 1, '\0')
, limit(0)
, cancel(false)
@@ -757,6 +762,74 @@ void Http::cancel()
if (p) { p->cancel = true; }
}
void Http::print() const
{
if (!p) {
BOOST_LOG_TRIVIAL(info) << "Http::print() - no request data";
return;
}
std::ostringstream cmd;
cmd << "curl";
// Method
if (p->method != "GET") {
cmd << " -X " << p->method;
}
// URL
cmd << " '" << p->url << "'";
// Headers (iterate through curl_slist)
::curl_slist *header = p->headerlist;
while (header) {
// Skip empty "Expect:" header we add by default
if (header->data && std::string(header->data) != "Expect:") {
cmd << " \\\n -H '" << header->data << "'";
}
header = header->next;
}
// Form fields (multipart) - iterate through curl_httppost
::curl_httppost *formpost = p->form;
while (formpost) {
if (formpost->showfilename) {
// File upload (showfilename is set when CURLFORM_FILENAME is used)
cmd << " \\\n -F '" << formpost->name << "=@" << formpost->showfilename << "'";
} else if (formpost->contents) {
// Regular form field with contents
cmd << " \\\n -F '" << formpost->name << "=" << formpost->contents << "'";
} else {
// Stream or other type without direct contents
cmd << " \\\n -F '" << formpost->name << "=<data>'";
}
formpost = formpost->next;
}
// Post body
if (!p->postfields.empty()) {
// Escape single quotes in the body for shell safety
std::string escaped_body = p->postfields;
size_t pos = 0;
while ((pos = escaped_body.find('\'', pos)) != std::string::npos) {
escaped_body.replace(pos, 1, "'\\''");
pos += 4;
}
// Truncate if too long for display
if (escaped_body.length() > 1000) {
escaped_body = escaped_body.substr(0, 1000) + "...<truncated>";
}
cmd << " \\\n -d '" << escaped_body << "'";
}
// Put file
if (p->putFile) {
cmd << " \\\n --upload-file <file-stream>";
}
BOOST_LOG_TRIVIAL(info) << "Http request:\n" << cmd.str();
}
Http Http::get(std::string url)
{
return Http{std::move(url)};
@@ -765,6 +838,7 @@ Http Http::get(std::string url)
Http Http::post(std::string url)
{
Http http{std::move(url)};
http.p->method = "POST";
curl_easy_setopt(http.p->curl, CURLOPT_POST, 1L);
return http;
}
@@ -772,6 +846,7 @@ Http Http::post(std::string url)
Http Http::put(std::string url)
{
Http http{std::move(url)};
http.p->method = "PUT";
curl_easy_setopt(http.p->curl, CURLOPT_UPLOAD, 1L);
return http;
}
@@ -779,6 +854,7 @@ Http Http::put(std::string url)
Http Http::put2(std::string url)
{
Http http{ std::move(url) };
http.p->method = "PUT";
curl_easy_setopt(http.p->curl, CURLOPT_CUSTOMREQUEST, "PUT");
return http;
}
@@ -786,6 +862,7 @@ Http Http::put2(std::string url)
Http Http::patch(std::string url)
{
Http http{ std::move(url) };
http.p->method = "PATCH";
curl_easy_setopt(http.p->curl, CURLOPT_CUSTOMREQUEST, "PATCH");
return http;
}
@@ -793,6 +870,7 @@ Http Http::patch(std::string url)
Http Http::del(std::string url)
{
Http http{ std::move(url) };
http.p->method = "DELETE";
curl_easy_setopt(http.p->curl, CURLOPT_CUSTOMREQUEST, "DELETE");
return http;
}

View File

@@ -183,6 +183,9 @@ public:
// Cancels a request in progress
void cancel();
// Print the request as a curl command for debugging
void print() const;
// Tells whether current backend supports seting up a CA file using ca_file()
static bool ca_file_supported();

View File

@@ -0,0 +1,477 @@
#ifndef __I_CLOUD_SERVICE_AGENT_HPP__
#define __I_CLOUD_SERVICE_AGENT_HPP__
#include "bambu_networking.hpp"
#include "../../libslic3r/ProjectTask.hpp"
#include <string>
#include <map>
#include <vector>
#include <functional>
#include <memory>
namespace Slic3r {
/**
* ICloudServiceAgent - Interface for authentication and cloud service operations.
*
* This interface encapsulates all cloud-related functionality including authentication:
* - Lifecycle methods for agent initialization
* - User session management (login/logout)
* - Token access for dependent agents (IPrinterAgent)
* - Login UI command builders for WebView integration
* - Server connectivity and subscription management
* - Settings synchronization (presets upload/download)
* - Cloud user services (messages, tasks, firmware)
* - Model mall and publishing
* - Analytics and telemetry
* - Ratings and reviews
*
* Implementations:
* - OrcaCloudServiceAgent: Native implementation for Orca Cloud (includes OAuth PKCE)
* - BBLCloudServiceAgent: Wrapper around Bambu Lab's proprietary DLL
*
* Token Sharing Pattern:
* IPrinterAgent receives an ICloudServiceAgent instance via set_cloud_agent() to
* access tokens for cloud-relay operations without coupling to a specific auth
* implementation.
*/
class ICloudServiceAgent {
public:
virtual ~ICloudServiceAgent() = default;
// ========================================================================
// Lifecycle Methods
// ========================================================================
/**
* Initialize the logging backend for the agent.
* Call after set_config_dir() so logs have a destination.
*/
virtual int init_log() = 0;
/**
* Provide the writable configuration directory for storing auth state.
* Must be called before start().
*/
virtual int set_config_dir(std::string config_dir) = 0;
/**
* Register the client certificate file for TLS authentication.
* May be unused by some implementations (e.g., OrcaCloudServiceAgent).
*/
virtual int set_cert_file(std::string folder, std::string filename) = 0;
/**
* Set the country code for region-specific backend selection.
*/
virtual int set_country_code(std::string country_code) = 0;
/**
* Start the agent, performing any expensive initialization.
* Typically regenerates PKCE bundles and attempts silent sign-in.
*/
virtual int start() = 0;
// ========================================================================
// User Session Management
// ========================================================================
/**
* Authenticate the user with the provided JSON payload.
*
* Supported formats:
* 1. Traditional: {"username": "...", "password": "..."}
* 2. WebView/OAuth: {"command": "user_login", "data": {...}}
* 3. Token format: {"data": {"token": "...", "refresh_token": "...", "user": {...}}}
*
* On completion, invokes the registered OnUserLoginFn callback.
*/
virtual int change_user(std::string user_info) = 0;
/**
* Check whether a valid authenticated session exists.
*/
virtual bool is_user_login() = 0;
/**
* Terminate the current session.
* @param request If true, also notify the backend to invalidate the session.
*/
virtual int user_logout(bool request = false) = 0;
/**
* Return the backend-generated user ID for the current session.
*/
virtual std::string get_user_id() = 0;
/**
* Return the display name for the current user.
*/
virtual std::string get_user_name() = 0;
/**
* Return the avatar URL/path for the current user.
*/
virtual std::string get_user_avatar() = 0;
/**
* Return the nickname for the current user.
*/
virtual std::string get_user_nickname() = 0;
// ========================================================================
// Login UI Support
// ========================================================================
/**
* Build a JSON command for the WebView login flow.
* Contains backend URL, API key, and PKCE parameters.
*/
virtual std::string build_login_cmd() = 0;
/**
* Build a JSON command for WebView logout.
*/
virtual std::string build_logout_cmd() = 0;
/**
* Return a JSON snapshot of the active session (user info, no tokens).
* Used by WebView to display current user state.
*/
virtual std::string build_login_info() = 0;
// ========================================================================
// Token Access (for dependent agents)
// ========================================================================
/**
* Return the current access token for API calls.
* Cloud and printer agents use this for Authorization headers.
*/
virtual std::string get_access_token() const = 0;
/**
* Return the current refresh token (if available).
*/
virtual std::string get_refresh_token() const = 0;
/**
* Ensure the access token is fresh, refreshing if necessary.
* Call before making API requests to avoid 401 errors.
*
* @param reason Descriptive string for logging (e.g., "connect_server")
* @return true if the token is fresh or was successfully refreshed
*/
virtual bool ensure_token_fresh(const std::string& reason) = 0;
// ========================================================================
// Server Connectivity
// ========================================================================
/**
* Return the base hostname for cloud API calls (varies by region).
* Helpful for diagnostics and when building browser URLs.
*/
virtual std::string get_cloud_service_host() = 0;
/**
* Return the login URL for the cloud service.
* @param language Optional language code (e.g., "en-US", "zh-CN") for localized login page.
* If empty, returns the default (non-localized) login URL.
* @return The full URL to the login page, or a local file:// URL for native implementations.
*/
virtual std::string get_cloud_login_url(const std::string& language = "") = 0;
/**
* Perform a health check against the configured backend.
* Updates is_server_connected() state and triggers OnServerConnectedFn.
*/
virtual int connect_server() = 0;
/**
* Return whether the server is currently reachable.
*/
virtual bool is_server_connected() = 0;
/**
* Force a server state recheck, clearing any cached state.
*/
virtual int refresh_connection() = 0;
/**
* Subscribe to a logical module (e.g., "printer", "user").
*/
virtual int start_subscribe(std::string module) = 0;
/**
* Stop listening to a formerly subscribed module.
*/
virtual int stop_subscribe(std::string module) = 0;
/**
* Subscribe to push streams for specific device identifiers.
*/
virtual int add_subscribe(std::vector<std::string> dev_list) = 0;
/**
* Remove device-level subscriptions.
*/
virtual int del_subscribe(std::vector<std::string> dev_list) = 0;
/**
* Enable or disable multi-machine mode.
*/
virtual void enable_multi_machine(bool enable) = 0;
// ========================================================================
// Settings Synchronization
// ========================================================================
/**
* Fetch all presets owned by the logged-in user.
* @param user_presets Map populated with [type][setting_id] = json
*/
virtual int get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets) = 0;
/**
* Request a new preset identifier from the server.
*/
virtual std::string request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code) = 0;
/**
* Update or create a preset with a known setting_id.
*/
virtual int put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code) = 0;
/**
* Trigger bulk download of user presets.
*/
virtual int get_setting_list(std::string bundle_version, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) = 0;
/**
* Enhanced preset sync with per-item validation.
*/
virtual int get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) = 0;
/**
* Delete a remote preset.
*/
virtual int delete_setting(std::string setting_id) = 0;
// ========================================================================
// Cloud User Services
// ========================================================================
/**
* Retrieve inbox/notification messages.
*/
virtual int get_my_message(int type, int after, int limit, unsigned int* http_code, std::string* http_body) = 0;
/**
* Check for pending task reports.
*/
virtual int check_user_task_report(int* task_id, bool* printable) = 0;
/**
* Fetch aggregated print statistics.
*/
virtual int get_user_print_info(unsigned int* http_code, std::string* http_body) = 0;
/**
* Query user's tasks/prints.
*/
virtual int get_user_tasks(TaskQueryParams params, std::string* http_body) = 0;
/**
* Fetch firmware information for a printer.
*/
virtual int get_printer_firmware(std::string dev_id, unsigned* http_code, std::string* http_body) = 0;
/**
* Get plate index for a cloud task.
*/
virtual int get_task_plate_index(std::string task_id, int* plate_index) = 0;
/**
* Retrieve extended user profile info.
*/
virtual int get_user_info(int* identifier) = 0;
/**
* Fetch subtask information.
*/
virtual int get_subtask_info(std::string subtask_id, std::string* task_json, unsigned int* http_code, std::string* http_body) = 0;
/**
* Retrieve slicing job info.
*/
virtual int get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json) = 0;
/**
* Query binding status for multiple devices.
*/
virtual int query_bind_status(std::vector<std::string> query_list, unsigned int* http_code, std::string* http_body) = 0;
/**
* Update printer name in cloud profile.
*/
virtual int modify_printer_name(std::string dev_id, std::string dev_name) = 0;
// ========================================================================
// Model Mall & Publishing
// ========================================================================
/**
* Request live camera streaming URL.
*/
virtual int get_camera_url(std::string dev_id, std::function<void(std::string)> callback) = 0;
/**
* Fetch staff-picked designs from model mall.
*/
virtual int get_design_staffpick(int offset, int limit, std::function<void(std::string)> callback) = 0;
/**
* Run multi-stage publishing workflow.
*/
virtual int start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out) = 0;
/**
* Get model publish URL.
*/
virtual int get_model_publish_url(std::string* url) = 0;
/**
* Fetch publishing subtask information.
*/
virtual int get_subtask(BBLModelTask* task, OnGetSubTaskFn getsub_fn) = 0;
/**
* Get model mall home URL.
*/
virtual int get_model_mall_home_url(std::string* url) = 0;
/**
* Build model detail page URL.
*/
virtual int get_model_mall_detail_url(std::string* url, std::string id) = 0;
/**
* Retrieve user's model mall profile.
*/
virtual int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) = 0;
// ========================================================================
// Analytics & Tracking
// ========================================================================
/**
* Enable/disable telemetry.
*/
virtual int track_enable(bool enable) = 0;
/**
* Delete telemetry files.
*/
virtual int track_remove_files() = 0;
/**
* Report a custom analytics event.
*/
virtual int track_event(std::string evt_key, std::string content) = 0;
/**
* Set telemetry headers.
*/
virtual int track_header(std::string header) = 0;
/**
* Update a tracked user property.
*/
virtual int track_update_property(std::string name, std::string value, std::string type = "string") = 0;
/**
* Read a tracked user property.
*/
virtual int track_get_property(std::string name, std::string& value, std::string type = "string") = 0;
/**
* Check if tracking is enabled.
*/
virtual bool get_track_enable() = 0;
// ========================================================================
// Ratings & Reviews
// ========================================================================
/**
* Submit a review for a marketplace design.
*/
virtual int put_model_mall_rating(int design_id, int score, std::string content, std::vector<std::string> images, unsigned int& http_code, std::string& http_error) = 0;
/**
* Get OSS configuration for image uploads.
*/
virtual int get_oss_config(std::string& config, std::string country_code, unsigned int& http_code, std::string& http_error) = 0;
/**
* Upload rating images to OSS.
*/
virtual int put_rating_picture_oss(std::string& config, std::string& pic_oss_path, std::string model_id, int profile_id, unsigned int& http_code, std::string& http_error) = 0;
/**
* Poll for rating result.
*/
virtual int get_model_mall_rating_result(int job_id, std::string& rating_result, unsigned int& http_code, std::string& http_error) = 0;
// ========================================================================
// Extra Features
// ========================================================================
/**
* Set additional HTTP headers for all requests.
*/
virtual int set_extra_http_header(std::map<std::string, std::string> extra_headers) = 0;
/**
* Get the studio info URL.
*/
virtual std::string get_studio_info_url() = 0;
/**
* Fetch MakerWorld user preferences.
*/
virtual int get_mw_user_preference(std::function<void(std::string)> callback) = 0;
/**
* Retrieve MakerWorld "For You" list.
*/
virtual int get_mw_user_4ulist(int seed, int limit, std::function<void(std::string)> callback) = 0;
/**
* Return the version of the cloud service implementation.
*/
virtual std::string get_version() = 0;
// ========================================================================
// Callback Registration
// ========================================================================
/**
* Register the login status callback.
* Called after change_user() finishes or when the session expires.
*/
virtual int set_on_user_login_fn(OnUserLoginFn fn) = 0;
/**
* Register server connection status callback.
*/
virtual int set_on_server_connected_fn(OnServerConnectedFn fn) = 0;
/**
* Register HTTP error callback.
*/
virtual int set_on_http_error_fn(OnHttpErrorFn fn) = 0;
/**
* Provide country code getter callback.
*/
virtual int set_get_country_code_fn(GetCountryCodeFn fn) = 0;
/**
* Provide main thread queue callback.
*/
virtual int set_queue_on_main_fn(QueueOnMainFn fn) = 0;
};
} // namespace Slic3r
#endif // __I_CLOUD_SERVICE_AGENT_HPP__

View File

@@ -0,0 +1,260 @@
#ifndef __I_PRINTER_AGENT_HPP__
#define __I_PRINTER_AGENT_HPP__
#include "bambu_networking.hpp"
#include <string>
#include <memory>
namespace Slic3r {
class ICloudServiceAgent;
/**
* AgentInfo - Metadata structure for printer agent information.
*
* Contains identification and descriptive information about a printer agent
* implementation, used for discovery and selection purposes.
*/
struct AgentInfo {
std::string id; ///< Unique identifier for the agent, e.g. "orca", "bbl"
std::string name; ///< Human-readable agent name, e.g. "Orca", "Bambu Lab"
std::string version; ///< Agent version string, e.g. "1.0.0"
std::string description; ///< Brief description of the agent's capabilities, e.g. "Orca printer agent"
};
/**
* FilamentSyncMode - Modes for filament data synchronization.
*
* Defines how filament information is obtained from the printer:
* - Subscription: Real-time push updates (e.g., MQTT subscriptions)
* - Pull: On-demand fetch via REST API (blocking call)
* - None: Filament sync unavailable
*/
enum class FilamentSyncMode {
none = 0, ///< Filament synchronization not supported
subscription, ///< Real-time push updates via subscription (e.g., MQTT)
pull ///< On-demand fetch via REST API (blocking call)
};
/**
* IPrinterAgent - Interface for printer operations.
*
* This interface encapsulates all printer-related functionality:
* - Direct printer communication (LAN and cloud relay)
* - Certificate management
* - Device discovery (SSDP)
* - Printer binding/unbinding
* - Print job operations
*
* Implementations:
* - OrcaPrinterAgent: Stub implementation (printer ops not yet supported)
* - BBLPrinterAgent: Wrapper around Bambu Lab's proprietary DLL
*
* Token Access:
* Printer agents receive an ICloudServiceAgent instance via set_cloud_agent() to
* access tokens for cloud-relay operations.
*/
class IPrinterAgent {
public:
virtual ~IPrinterAgent() = default;
// ========================================================================
// Cloud Agent Dependency
// ========================================================================
/**
* Set the cloud agent used for token access.
* Must be called before any cloud-relay operations.
*/
virtual void set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud) = 0;
// ========================================================================
// Communication
// ========================================================================
/**
* Publish a JSON command to a printer through cloud relay.
*/
virtual int send_message(std::string dev_id, std::string json_str, int qos, int flag) = 0;
/**
* Establish a direct LAN connection to a printer.
*/
virtual int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) = 0;
/**
* Tear down the active LAN printer connection.
*/
virtual int disconnect_printer() = 0;
/**
* Send a JSON command to a LAN printer (bypassing cloud).
*/
virtual int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) = 0;
// ========================================================================
// Certificates
// ========================================================================
/**
* Validate current user certificates for the printer.
*/
virtual int check_cert() = 0;
/**
* Install or refresh device certificate for LAN TLS.
*/
virtual void install_device_cert(std::string dev_id, bool lan_only) = 0;
// ========================================================================
// Discovery
// ========================================================================
/**
* Start or stop SSDP discovery.
*/
virtual bool start_discovery(bool start, bool sending) = 0;
// ========================================================================
// Binding
// ========================================================================
/**
* Ping the binding endpoint to check printer readiness.
*/
virtual int ping_bind(std::string ping_code) = 0;
/**
* Perform binding detection/handshake on a LAN printer.
*/
virtual int bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect) = 0;
/**
* Execute the multi-stage printer binding workflow.
*/
virtual int bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn) = 0;
/**
* Remove the association between account and printer.
*/
virtual int unbind(std::string dev_id) = 0;
/**
* Request a one-time bind ticket from the server.
*/
virtual int request_bind_ticket(std::string* ticket) = 0;
/**
* Register callback for fatal HTTP errors.
*/
virtual int set_server_callback(OnServerErrFn fn) = 0;
// ========================================================================
// Machine Selection
// ========================================================================
/**
* Return the currently selected printer ID.
*/
virtual std::string get_user_selected_machine() = 0;
/**
* Update the selected machine preference.
*/
virtual int set_user_selected_machine(std::string dev_id) = 0;
// ========================================================================
// Print Job Operations
// ========================================================================
/**
* Start a fully managed cloud print.
*/
virtual int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) = 0;
/**
* Start a local print with cloud record.
*/
virtual int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) = 0;
/**
* Upload gcode to printer's SD card without starting.
*/
virtual int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) = 0;
/**
* Start a LAN-only print.
*/
virtual int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) = 0;
/**
* Start a print from printer's SD card.
*/
virtual int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) = 0;
// ========================================================================
// Callback Registration
// ========================================================================
/**
* Register SSDP discovery callback.
*/
virtual int set_on_ssdp_msg_fn(OnMsgArrivedFn fn) = 0;
/**
* Register printer MQTT connection callback.
*/
virtual int set_on_printer_connected_fn(OnPrinterConnectedFn fn) = 0;
/**
* Register subscription failure callback.
*/
virtual int set_on_subscribe_failure_fn(GetSubscribeFailureFn fn) = 0;
/**
* Register cloud device message callback.
*/
virtual int set_on_message_fn(OnMessageFn fn) = 0;
/**
* Register user-scoped message callback.
*/
virtual int set_on_user_message_fn(OnMessageFn fn) = 0;
/**
* Register LAN connection status callback.
*/
virtual int set_on_local_connect_fn(OnLocalConnectedFn fn) = 0;
/**
* Register LAN message callback.
*/
virtual int set_on_local_message_fn(OnMessageFn fn) = 0;
/**
* Provide main thread queue callback.
*/
virtual int set_queue_on_main_fn(QueueOnMainFn fn) = 0;
/**
* Get agent information.
*/
virtual AgentInfo get_agent_info() = 0;
// ========================================================================
// Filament Operations
// ========================================================================
/**
* Get the filament synchronization mode for this agent.
*
* @return FilamentSyncMode indicating how filament data is obtained:
* - subscription: Real-time push updates via MQTT (no fetch needed)
* - pull: On-demand fetch via REST API (call fetch_filament_info())
* - none: Filament synchronization not supported
*/
virtual FilamentSyncMode get_filament_sync_mode() const { return FilamentSyncMode::none; }
/**
* Refresh filament info from the printer synchronously.
* Should only be called when get_filament_sync_mode() returns FilamentSyncMode::pull.
* Populates the MachineObject's DevFilaSystem with fetched filament data.
*/
virtual void fetch_filament_info(std::string dev_id) {}
};
} // namespace Slic3r
#endif // __I_PRINTER_AGENT_HPP__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
#ifndef __MOONRAKER_PRINTER_AGENT_HPP__
#define __MOONRAKER_PRINTER_AGENT_HPP__
#include "IPrinterAgent.hpp"
#include "ICloudServiceAgent.hpp"
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <nlohmann/json.hpp>
namespace Slic3r {
class MoonrakerPrinterAgent final : public IPrinterAgent
{
public:
explicit MoonrakerPrinterAgent(std::string log_dir);
~MoonrakerPrinterAgent() override;
static AgentInfo get_agent_info_static();
AgentInfo get_agent_info() override { return get_agent_info_static(); }
// Cloud Agent Dependency
void set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud) override;
// Communication
int send_message(std::string dev_id, std::string json_str, int qos, int flag) override;
int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override;
int disconnect_printer() override;
int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) override;
// Certificates
int check_cert() override;
void install_device_cert(std::string dev_id, bool lan_only) override;
// Discovery
bool start_discovery(bool start, bool sending) override;
// Binding
int ping_bind(std::string ping_code) override;
int bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect) override;
int bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn) override;
int unbind(std::string dev_id) override;
int request_bind_ticket(std::string* ticket) override;
int set_server_callback(OnServerErrFn fn) override;
// Machine Selection
std::string get_user_selected_machine() override;
int set_user_selected_machine(std::string dev_id) override;
// Print Job Operations
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
// Callbacks
int set_on_ssdp_msg_fn(OnMsgArrivedFn fn) override;
int set_on_printer_connected_fn(OnPrinterConnectedFn fn) override;
int set_on_subscribe_failure_fn(GetSubscribeFailureFn fn) override;
int set_on_message_fn(OnMessageFn fn) override;
int set_on_user_message_fn(OnMessageFn fn) override;
int set_on_local_connect_fn(OnLocalConnectedFn fn) override;
int set_on_local_message_fn(OnMessageFn fn) override;
int set_queue_on_main_fn(QueueOnMainFn fn) override;
// Pull-mode agent (on-demand filament sync)
FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; }
void fetch_filament_info(std::string dev_id) override;
private:
struct PrinthostConfig
{
std::string host;
std::string port;
std::string api_key;
std::string base_url;
std::string model_name;
};
struct MoonrakerDeviceInfo
{
std::string dev_id;
std::string dev_name;
std::string version;
};
int handle_request(const std::string& dev_id, const std::string& json_str);
int send_version_info(const std::string& dev_id);
int send_access_code(const std::string& dev_id);
bool get_printhost_config(PrinthostConfig& config) const;
bool fetch_device_info(const std::string& base_url, const std::string& api_key, MoonrakerDeviceInfo& info, std::string& error) const;
bool fetch_server_info(const std::string& base_url, const std::string& api_key, std::string& version, std::string& error) const;
bool fetch_object_list(const std::string& base_url, const std::string& api_key, std::set<std::string>& objects, std::string& error) const;
bool query_printer_status(const std::string& base_url, const std::string& api_key, nlohmann::json& status, std::string& error) const;
bool send_gcode(const std::string& dev_id, const std::string& gcode) const;
std::string resolve_host(const std::string& dev_id) const;
std::string resolve_api_key(const std::string& dev_id, const std::string& fallback) const;
void store_host(const std::string& dev_id, const std::string& host, const std::string& api_key);
void announce_printhost_device();
void dispatch_local_connect(int state, const std::string& dev_id, const std::string& msg);
void dispatch_printer_connected(const std::string& dev_id);
void dispatch_message(const std::string& dev_id, const std::string& payload);
void start_status_stream(const std::string& dev_id, const std::string& base_url, const std::string& api_key);
void stop_status_stream();
void run_status_stream(std::string dev_id, std::string base_url, std::string api_key);
void handle_ws_message(const std::string& dev_id, const std::string& payload);
void update_status_cache(const nlohmann::json& updates);
nlohmann::json build_print_payload_locked() const;
// Print control helpers
int pause_print(const std::string& dev_id);
int resume_print(const std::string& dev_id);
int cancel_print(const std::string& dev_id);
// File upload
bool upload_gcode(const std::string& local_path, const std::string& filename,
const std::string& base_url, const std::string& api_key,
OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
// JSON-RPC helper
bool send_jsonrpc_command(const std::string& base_url, const std::string& api_key,
const nlohmann::json& request, std::string& response) const;
// Server info (returns JSON, not just version string)
bool fetch_server_info_json(const std::string& base_url, const std::string& api_key,
nlohmann::json& info, std::string& error) const;
// Connection thread management
void perform_connection_async(const std::string& dev_id,
const std::string& base_url,
const std::string& api_key);
void finish_connection();
mutable std::recursive_mutex state_mutex;
std::map<std::string, std::string> host_by_device;
std::map<std::string, std::string> api_key_by_device;
std::string ssdp_announced_host;
std::string ssdp_announced_id;
std::shared_ptr<ICloudServiceAgent> m_cloud_agent;
std::string selected_machine;
OnMsgArrivedFn on_ssdp_msg_fn;
OnPrinterConnectedFn on_printer_connected_fn;
GetSubscribeFailureFn on_subscribe_failure_fn;
OnMessageFn on_message_fn;
OnMessageFn on_user_message_fn;
OnLocalConnectedFn on_local_connect_fn;
OnMessageFn on_local_message_fn;
QueueOnMainFn queue_on_main_fn;
OnServerErrFn on_server_err_fn;
mutable std::recursive_mutex payload_mutex;
nlohmann::json status_cache;
std::atomic<int> next_jsonrpc_id{1};
std::set<std::string> available_objects; // Track for feature detection
std::atomic<bool> ws_stop{false};
std::atomic<bool> ws_reconnect_requested{false}; // Flag to trigger reconnection
std::atomic<uint64_t> ws_last_emit_ms{0};
std::thread ws_thread;
// Connection thread management
std::atomic<bool> connect_in_progress{false};
std::atomic<bool> connect_stop_requested{false};
std::thread connect_thread;
std::recursive_mutex connect_mutex;
std::condition_variable connect_cv;
};
} // namespace Slic3r
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,118 +3,22 @@
#include "bambu_networking.hpp"
#include "libslic3r/ProjectTask.hpp"
#include "ICloudServiceAgent.hpp"
#include "IPrinterAgent.hpp"
#include <memory>
using namespace BBL;
namespace Slic3r {
typedef bool (*func_check_debug_consistent)(bool is_debug);
typedef std::string (*func_get_version)(void);
typedef void* (*func_create_agent)(std::string log_dir);
typedef int (*func_destroy_agent)(void *agent);
typedef int (*func_init_log)(void *agent);
typedef int (*func_set_config_dir)(void *agent, std::string config_dir);
typedef int (*func_set_cert_file)(void *agent, std::string folder, std::string filename);
typedef int (*func_set_country_code)(void *agent, std::string country_code);
typedef int (*func_start)(void *agent);
typedef int (*func_set_on_ssdp_msg_fn)(void *agent, OnMsgArrivedFn fn);
typedef int (*func_set_on_user_login_fn)(void *agent, OnUserLoginFn fn);
typedef int (*func_set_on_printer_connected_fn)(void *agent, OnPrinterConnectedFn fn);
typedef int (*func_set_on_server_connected_fn)(void *agent, OnServerConnectedFn fn);
typedef int (*func_set_on_http_error_fn)(void *agent, OnHttpErrorFn fn);
typedef int (*func_set_get_country_code_fn)(void *agent, GetCountryCodeFn fn);
typedef int (*func_set_on_subscribe_failure_fn)(void *agent, GetSubscribeFailureFn fn);
typedef int (*func_set_on_message_fn)(void *agent, OnMessageFn fn);
typedef int (*func_set_on_user_message_fn)(void *agent, OnMessageFn fn);
typedef int (*func_set_on_local_connect_fn)(void *agent, OnLocalConnectedFn fn);
typedef int (*func_set_on_local_message_fn)(void *agent, OnMessageFn fn);
typedef int (*func_set_queue_on_main_fn)(void *agent, QueueOnMainFn fn);
typedef int (*func_connect_server)(void *agent);
typedef bool (*func_is_server_connected)(void *agent);
typedef int (*func_refresh_connection)(void *agent);
typedef int (*func_start_subscribe)(void *agent, std::string module);
typedef int (*func_stop_subscribe)(void *agent, std::string module);
typedef int (*func_add_subscribe)(void *agent, std::vector<std::string> dev_list);
typedef int (*func_del_subscribe)(void *agent, std::vector<std::string> dev_list);
typedef void (*func_enable_multi_machine)(void *agent, bool enable);
typedef int (*func_send_message)(void *agent, std::string dev_id, std::string json_str, int qos, int flag);
typedef int (*func_connect_printer)(void *agent, std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl);
typedef int (*func_disconnect_printer)(void *agent);
typedef int (*func_send_message_to_printer)(void *agent, std::string dev_id, std::string json_str, int qos, int flag);
typedef int (*func_check_cert)(void* agent);
typedef void (*func_install_device_cert)(void* agent, std::string dev_id, bool lan_only);
typedef bool (*func_start_discovery)(void *agent, bool start, bool sending);
typedef int (*func_change_user)(void *agent, std::string user_info);
typedef bool (*func_is_user_login)(void *agent);
typedef int (*func_user_logout)(void *agent, bool request);
typedef std::string (*func_get_user_id)(void *agent);
typedef std::string (*func_get_user_name)(void *agent);
typedef std::string (*func_get_user_avatar)(void *agent);
typedef std::string (*func_get_user_nickanme)(void *agent);
typedef std::string (*func_build_login_cmd)(void *agent);
typedef std::string (*func_build_logout_cmd)(void *agent);
typedef std::string (*func_build_login_info)(void *agent);
typedef int (*func_ping_bind)(void *agent, std::string ping_code);
typedef int (*func_bind_detect)(void *agent, std::string dev_ip, std::string sec_link, detectResult& detect);
typedef int (*func_set_server_callback)(void *agent, OnServerErrFn fn);
typedef int (*func_bind)(void *agent, std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn);
typedef int (*func_unbind)(void *agent, std::string dev_id);
typedef std::string (*func_get_bambulab_host)(void *agent);
typedef std::string (*func_get_user_selected_machine)(void *agent);
typedef int (*func_set_user_selected_machine)(void *agent, std::string dev_id);
typedef int (*func_start_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_local_print_with_record)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_send_gcode_to_sdcard)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
typedef int (*func_start_local_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
typedef int (*func_start_sdcard_print)(void *agent, PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
typedef int (*func_get_user_presets)(void *agent, std::map<std::string, std::map<std::string, std::string>>* user_presets);
typedef std::string (*func_request_setting_id)(void *agent, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
typedef int (*func_put_setting)(void *agent, std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
typedef int (*func_get_setting_list)(void *agent, std::string bundle_version, ProgressFn pro_fn, WasCancelledFn cancel_fn);
typedef int (*func_get_setting_list2)(void *agent, std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn, WasCancelledFn cancel_fn);
typedef int (*func_delete_setting)(void *agent, std::string setting_id);
typedef std::string (*func_get_studio_info_url)(void *agent);
typedef int (*func_set_extra_http_header)(void *agent, std::map<std::string, std::string> extra_headers);
typedef int (*func_get_my_message)(void *agent, int type, int after, int limit, unsigned int* http_code, std::string* http_body);
typedef int (*func_check_user_task_report)(void *agent, int* task_id, bool* printable);
typedef int (*func_get_user_print_info)(void *agent, unsigned int* http_code, std::string* http_body);
typedef int (*func_get_user_tasks)(void *agent, TaskQueryParams params, std::string* http_body);
typedef int (*func_get_printer_firmware)(void *agent, std::string dev_id, unsigned* http_code, std::string* http_body);
typedef int (*func_get_task_plate_index)(void *agent, std::string task_id, int* plate_index);
typedef int (*func_get_user_info)(void *agent, int* identifier);
typedef int (*func_request_bind_ticket)(void *agent, std::string* ticket);
typedef int (*func_get_subtask_info)(void *agent, std::string subtask_id, std::string* task_json, unsigned int* http_code, std::string *http_body);
typedef int (*func_get_slice_info)(void *agent, std::string project_id, std::string profile_id, int plate_index, std::string* slice_json);
typedef int (*func_query_bind_status)(void *agent, std::vector<std::string> query_list, unsigned int* http_code, std::string* http_body);
typedef int (*func_modify_printer_name)(void *agent, std::string dev_id, std::string dev_name);
typedef int (*func_get_camera_url)(void *agent, std::string dev_id, std::function<void(std::string)> callback);
typedef int (*func_get_design_staffpick)(void *agent, int offset, int limit, std::function<void(std::string)> callback);
typedef int (*func_start_pubilsh)(void *agent, PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out);
typedef int (*func_get_model_publish_url)(void *agent, std::string* url);
typedef int (*func_get_subtask)(void *agent, BBLModelTask* task, OnGetSubTaskFn getsub_fn);
typedef int (*func_get_model_mall_home_url)(void *agent, std::string* url);
typedef int (*func_get_model_mall_detail_url)(void *agent, std::string* url, std::string id);
typedef int (*func_get_my_profile)(void *agent, std::string token, unsigned int *http_code, std::string *http_body);
typedef int (*func_track_enable)(void *agent, bool enable);
typedef int (*func_track_remove_files)(void *agent);
typedef int (*func_track_event)(void *agent, std::string evt_key, std::string content);
typedef int (*func_track_header)(void *agent, std::string header);
typedef int (*func_track_update_property)(void *agent, std::string name, std::string value, std::string type);
typedef int (*func_track_get_property)(void *agent, std::string name, std::string& value, std::string type);
typedef int (*func_put_model_mall_rating_url)(
void *agent, int rating_id, int score, std::string content, std::vector<std::string> images, unsigned int &http_code, std::string &http_error);
typedef int (*func_get_oss_config)(void *agent, std::string &config, std::string country_code, unsigned int &http_code, std::string &http_error);
typedef int (*func_put_rating_picture_oss)(
void *agent, std::string &config, std::string &pic_oss_path, std::string model_id, int profile_id, unsigned int &http_code, std::string &http_error);
typedef int (*func_get_model_mall_rating_result)(void *agent, int job_id, std::string &rating_result, unsigned int &http_code, std::string &http_error);
typedef int (*func_get_mw_user_preference)(void *agent, std::function<void(std::string)> callback);
typedef int (*func_get_mw_user_4ulist)(void *agent, int seed, int limit, std::function<void(std::string)> callback);
// Forward declaration
class BBLNetworkPlugin;
//the NetworkAgent class
class NetworkAgent
{
public:
// Static utility methods - delegate to BBLNetworkPlugin
static std::string get_libpath_in_current_directory(std::string library_name);
static std::string get_versioned_library_path(const std::string& version);
static bool versioned_library_exists(const std::string& version);
@@ -136,9 +40,24 @@ public:
static NetworkLibraryLoadError get_load_error();
static void clear_load_error();
static void set_load_error(const std::string& message, const std::string& technical_details, const std::string& attempted_path);
// Traditional constructor (uses BBL DLL via singleton)
NetworkAgent(std::string log_dir);
// Sub-agent composition constructor (uses injected sub-agents)
NetworkAgent(std::shared_ptr<ICloudServiceAgent> cloud_agent,
std::shared_ptr<IPrinterAgent> printer_agent);
~NetworkAgent();
// Sub-agent accessors
std::shared_ptr<ICloudServiceAgent> get_cloud_agent() const { return m_cloud_agent; }
std::shared_ptr<IPrinterAgent> get_printer_agent() const { return m_printer_agent; }
// Set the printer agent (for dynamic agent switching)
void set_printer_agent(std::shared_ptr<IPrinterAgent> printer_agent);
// Instance methods - delegate to sub-agents or BBLNetworkPlugin
int init_log();
int set_config_dir(std::string config_dir);
int set_cert_file(std::string folder, std::string filename);
@@ -177,7 +96,7 @@ public:
std::string get_user_id();
std::string get_user_name();
std::string get_user_avatar();
std::string get_user_nickanme();
std::string get_user_nickname();
std::string build_login_cmd();
std::string build_logout_cmd();
std::string build_login_info();
@@ -186,7 +105,8 @@ public:
int set_server_callback(OnServerErrFn fn);
int bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn);
int unbind(std::string dev_id);
std::string get_bambulab_host();
std::string get_cloud_service_host();
std::string get_cloud_login_url(const std::string& language = "");
std::string get_user_selected_machine();
int set_user_selected_machine(std::string dev_id);
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
@@ -194,6 +114,8 @@ public:
int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn);
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
FilamentSyncMode get_filament_sync_mode() const;
void fetch_filament_info(std::string dev_id);
int get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets);
std::string request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
int put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code);
@@ -236,117 +158,36 @@ public:
int get_mw_user_preference(std::function<void(std::string)> callback);
int get_mw_user_4ulist(int seed, int limit, std::function<void(std::string)> callback);
void *get_network_agent() { return network_agent; }
// Get underlying agent handle from BBLNetworkPlugin
void* get_network_agent();
private:
struct PrinterCallbacks {
OnMsgArrivedFn on_ssdp_msg_fn = nullptr;
OnPrinterConnectedFn on_printer_connected_fn = nullptr;
GetSubscribeFailureFn on_subscribe_failure_fn = nullptr;
OnMessageFn on_message_fn = nullptr;
OnMessageFn on_user_message_fn = nullptr;
OnLocalConnectedFn on_local_connect_fn = nullptr;
OnMessageFn on_local_message_fn = nullptr;
QueueOnMainFn queue_on_main_fn = nullptr;
OnServerErrFn on_server_err_fn = nullptr;
};
void apply_printer_callbacks(const std::shared_ptr<IPrinterAgent>& printer_agent,
const PrinterCallbacks& callbacks);
mutable std::mutex m_agent_mutex; // Protect agent swapping
PrinterCallbacks m_printer_callbacks;
bool enable_track = false;
void* network_agent { nullptr };
static NetworkLibraryLoadError s_load_error;
static func_check_debug_consistent check_debug_consistent_ptr;
static func_get_version get_version_ptr;
static func_create_agent create_agent_ptr;
static func_destroy_agent destroy_agent_ptr;
static func_init_log init_log_ptr;
static func_set_config_dir set_config_dir_ptr;
static func_set_cert_file set_cert_file_ptr;
static func_set_country_code set_country_code_ptr;
static func_start start_ptr;
static func_set_on_ssdp_msg_fn set_on_ssdp_msg_fn_ptr;
static func_set_on_user_login_fn set_on_user_login_fn_ptr;
static func_set_on_printer_connected_fn set_on_printer_connected_fn_ptr;
static func_set_on_server_connected_fn set_on_server_connected_fn_ptr;
static func_set_on_http_error_fn set_on_http_error_fn_ptr;
static func_set_get_country_code_fn set_get_country_code_fn_ptr;
static func_set_on_subscribe_failure_fn set_on_subscribe_failure_fn_ptr;
static func_set_on_message_fn set_on_message_fn_ptr;
static func_set_on_user_message_fn set_on_user_message_fn_ptr;
static func_set_on_local_connect_fn set_on_local_connect_fn_ptr;
static func_set_on_local_message_fn set_on_local_message_fn_ptr;
static func_set_queue_on_main_fn set_queue_on_main_fn_ptr;
static func_connect_server connect_server_ptr;
static func_is_server_connected is_server_connected_ptr;
static func_refresh_connection refresh_connection_ptr;
static func_start_subscribe start_subscribe_ptr;
static func_stop_subscribe stop_subscribe_ptr;
static func_add_subscribe add_subscribe_ptr;
static func_del_subscribe del_subscribe_ptr;
static func_enable_multi_machine enable_multi_machine_ptr;
static func_send_message send_message_ptr;
static func_connect_printer connect_printer_ptr;
static func_disconnect_printer disconnect_printer_ptr;
static func_send_message_to_printer send_message_to_printer_ptr;
static func_check_cert check_cert_ptr;
static func_install_device_cert install_device_cert_ptr;
static func_start_discovery start_discovery_ptr;
static func_change_user change_user_ptr;
static func_is_user_login is_user_login_ptr;
static func_user_logout user_logout_ptr;
static func_get_user_id get_user_id_ptr;
static func_get_user_name get_user_name_ptr;
static func_get_user_avatar get_user_avatar_ptr;
static func_get_user_nickanme get_user_nickanme_ptr;
static func_build_login_cmd build_login_cmd_ptr;
static func_build_logout_cmd build_logout_cmd_ptr;
static func_build_login_info build_login_info_ptr;
static func_ping_bind ping_bind_ptr;
static func_bind_detect bind_detect_ptr;
static func_set_server_callback set_server_callback_ptr;
static func_bind bind_ptr;
static func_unbind unbind_ptr;
static func_get_bambulab_host get_bambulab_host_ptr;
static func_get_user_selected_machine get_user_selected_machine_ptr;
static func_set_user_selected_machine set_user_selected_machine_ptr;
static func_start_print start_print_ptr;
static func_start_local_print_with_record start_local_print_with_record_ptr;
static func_start_send_gcode_to_sdcard start_send_gcode_to_sdcard_ptr;
static func_start_local_print start_local_print_ptr;
static func_start_sdcard_print start_sdcard_print_ptr;
static func_get_user_presets get_user_presets_ptr;
static func_request_setting_id request_setting_id_ptr;
static func_put_setting put_setting_ptr;
static func_get_setting_list get_setting_list_ptr;
static func_get_setting_list2 get_setting_list2_ptr;
static func_delete_setting delete_setting_ptr;
static func_get_studio_info_url get_studio_info_url_ptr;
static func_set_extra_http_header set_extra_http_header_ptr;
static func_get_my_message get_my_message_ptr;
static func_check_user_task_report check_user_task_report_ptr;
static func_get_user_print_info get_user_print_info_ptr;
static func_get_user_tasks get_user_tasks_ptr;
static func_get_printer_firmware get_printer_firmware_ptr;
static func_get_task_plate_index get_task_plate_index_ptr;
static func_get_user_info get_user_info_ptr;
static func_request_bind_ticket request_bind_ticket_ptr;
static func_get_subtask_info get_subtask_info_ptr;
static func_get_slice_info get_slice_info_ptr;
static func_query_bind_status query_bind_status_ptr;
static func_modify_printer_name modify_printer_name_ptr;
static func_get_camera_url get_camera_url_ptr;
static func_get_design_staffpick get_design_staffpick_ptr;
static func_start_pubilsh start_publish_ptr;
static func_get_model_publish_url get_model_publish_url_ptr;
static func_get_subtask get_subtask_ptr;
static func_get_model_mall_home_url get_model_mall_home_url_ptr;
static func_get_model_mall_detail_url get_model_mall_detail_url_ptr;
static func_get_my_profile get_my_profile_ptr;
static func_track_enable track_enable_ptr;
static func_track_remove_files track_remove_files_ptr;
static func_track_event track_event_ptr;
static func_track_header track_header_ptr;
static func_track_update_property track_update_property_ptr;
static func_track_get_property track_get_property_ptr;
static func_put_model_mall_rating_url put_model_mall_rating_url_ptr;
static func_get_oss_config get_oss_config_ptr;
static func_put_rating_picture_oss put_rating_picture_oss_ptr;
static func_get_model_mall_rating_result get_model_mall_rating_result_ptr;
static func_get_mw_user_preference get_mw_user_preference_ptr;
static func_get_mw_user_4ulist get_mw_user_4ulist_ptr;
// Sub-agent composition (for Orca/BBL mixed mode)
std::shared_ptr<ICloudServiceAgent> m_cloud_agent;
std::shared_ptr<IPrinterAgent> m_printer_agent;
std::string m_printer_agent_id;
};
}
#endif

View File

@@ -0,0 +1,176 @@
#include "NetworkAgentFactory.hpp"
#include "IPrinterAgent.hpp"
#include "ICloudServiceAgent.hpp"
#include "BBLPrinterAgent.hpp"
#include "OrcaPrinterAgent.hpp"
#include "QidiPrinterAgent.hpp"
#include "MoonrakerPrinterAgent.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
namespace {
static std::mutex s_registry_mutex;
std::map<std::string, PrinterAgentInfo>& get_printer_agents()
{
static std::map<std::string, PrinterAgentInfo> agents;
return agents;
}
std::string& get_default_agent_id()
{
static std::string default_id;
return default_id;
}
} // 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();
auto result = agents.emplace(id, PrinterAgentInfo(id, display_name, std::move(factory)));
if (result.second) {
BOOST_LOG_TRIVIAL(info) << "Registered printer agent: " << id << " (" << display_name << ")";
// Set as default if it's the first agent registered
auto& default_id = get_default_agent_id();
if (default_id.empty()) {
default_id = id;
}
return true;
} else {
BOOST_LOG_TRIVIAL(warning) << "Printer agent already registered: " << id;
return false;
}
}
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::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) {
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);
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;
}
return it->second.factory(cloud_agent, log_dir);
}
std::string NetworkAgentFactory::get_default_printer_agent_id()
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
return get_default_agent_id();
}
void NetworkAgentFactory::set_default_printer_agent_id(const std::string& id)
{
std::lock_guard<std::mutex> lock(s_registry_mutex);
auto& agents = get_printer_agents();
if (agents.find(id) != agents.end()) {
get_default_agent_id() = id;
BOOST_LOG_TRIVIAL(info) << "Default printer agent set to: " << id;
} else {
BOOST_LOG_TRIVIAL(warning) << "Cannot set default to unregistered agent: " << id;
}
}
void NetworkAgentFactory::register_all_agents()
{
// Register Orca printer agent
{
auto info = OrcaPrinterAgent::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<OrcaPrinterAgent>(log_dir);
if (cloud_agent) {
agent->set_cloud_agent(cloud_agent);
}
return agent;
});
}
// Register Qidi printer agent
{
auto info = QidiPrinterAgent::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<QidiPrinterAgent>(log_dir);
if (cloud_agent) {
agent->set_cloud_agent(cloud_agent);
}
return agent;
});
}
// Register Moonraker printer agent
{
auto info = MoonrakerPrinterAgent::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<MoonrakerPrinterAgent>(log_dir);
if (cloud_agent) {
agent->set_cloud_agent(cloud_agent);
}
return agent;
});
}
// Register BBL printer agent (only if bbl network agent is available)
{
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;
});
}
BOOST_LOG_TRIVIAL(info) << "Registered " << get_printer_agents().size() << " printer agents";
}
} // namespace Slic3r

View File

@@ -0,0 +1,250 @@
#ifndef __NETWORK_AGENT_FACTORY_HPP__
#define __NETWORK_AGENT_FACTORY_HPP__
#include "ICloudServiceAgent.hpp"
#include "IPrinterAgent.hpp"
#include "NetworkAgent.hpp"
#include "OrcaCloudServiceAgent.hpp"
#include "BBLCloudServiceAgent.hpp"
#include "BBLNetworkPlugin.hpp"
#include "libslic3r/AppConfig.hpp"
#include <memory>
#include <string>
#include <functional>
#include <vector>
#include <map>
#include <mutex>
namespace Slic3r {
// Forward declarations
class ICloudServiceAgent;
class IPrinterAgent;
/**
* AgentProvider - Specifies which implementation to use for each agent type.
*
* - Orca: Native Orca implementations (OrcaCloudServiceAgent, OrcaPrinterAgent)
* - BBL: BBL DLL wrapper implementations (BBLCloudServiceAgent, BBLPrinterAgent)
*/
enum class AgentProvider { Orca, BBL };
// Factory function type for creating printer agents
using PrinterAgentFactory =
std::function<std::shared_ptr<IPrinterAgent>(std::shared_ptr<ICloudServiceAgent> cloud_agent, const std::string& log_dir)>;
// Information about a registered printer agent
struct PrinterAgentInfo
{
std::string id; // e.g., "orca", "bbl"
std::string display_name; // e.g., "Orca Native", "Bambu Lab"
PrinterAgentFactory factory; // Function to create the agent
PrinterAgentInfo(const std::string& id_, const std::string& display_name_, PrinterAgentFactory factory_)
: id(id_), display_name(display_name_), factory(std::move(factory_))
{}
};
/**
* NetworkAgentFactory - Factory for creating network agent instances
*
* This factory creates cloud agents and printer agents for the networking subsystem.
* The architecture separates cloud services (authentication, project sync) from
* printer communication (device discovery, print jobs).
*
* Startup flow:
* 1. Call register_all_agents() during app initialization
* 2. Cloud agent created at startup via create_agent_from_config()
* 3. Printer agent created on-demand when a printer is selected
*
* Usage:
* // At app startup (before any agent creation)
* NetworkAgentFactory::register_all_agents();
*
* // Create NetworkAgent with cloud agent only
* auto agent = create_agent_from_config(log_dir, app_config);
*
* // When printer is selected - create printer agent from registry
* auto printer = NetworkAgentFactory::create_printer_agent_by_id("orca", cloud, log_dir);
*/
class NetworkAgentFactory
{
public:
// ========================================================================
// Printer Agent Registry
// ========================================================================
/**
* Register all built-in printer agents.
* Must be called once during application initialization, before any
* calls to get_registered_printer_agents() or create_printer_agent_by_id().
*/
static void register_all_agents();
/**
* Register a printer agent type
*
* @param id Unique identifier for the agent (e.g., "orca", "bbl")
* @param display_name Human-readable name for UI
* @param factory Factory function to create the agent
* @return true if registration succeeded, false if already registered
*/
static bool register_printer_agent(const std::string& id, const std::string& display_name, PrinterAgentFactory factory);
/**
* Check if an agent ID is registered
*/
static bool is_printer_agent_registered(const std::string& id);
/**
* Get info about a registered agent
*/
static const PrinterAgentInfo* get_printer_agent_info(const std::string& id);
/**
* Get all registered printer agents (for UI population)
*/
static std::vector<PrinterAgentInfo> get_registered_printer_agents();
/**
* Create a printer agent by ID (using registry)
*
* @param id Agent ID to create
* @param cloud_agent Cloud agent for token access
* @param log_dir Directory for log files
* @return Shared pointer to IPrinterAgent, or nullptr if ID not found
*/
static std::shared_ptr<IPrinterAgent> create_printer_agent_by_id(const std::string& id,
std::shared_ptr<ICloudServiceAgent> cloud_agent,
const std::string& log_dir);
/**
* Get default printer agent ID
*/
static std::string get_default_printer_agent_id();
/**
* Set a specific agent as the default
*/
static void set_default_printer_agent_id(const std::string& id);
// ========================================================================
// Cloud Agent Factory
// ========================================================================
/**
* Create a cloud service agent based on provider type.
* Handles authentication, project sync, and other cloud services.
*
* @param provider Which implementation to use (Orca or BBL)
* @param log_dir Directory for log files
* @return Shared pointer to ICloudServiceAgent implementation
*/
static std::shared_ptr<ICloudServiceAgent> create_cloud_agent(AgentProvider provider, const std::string& log_dir)
{
switch (provider) {
case AgentProvider::Orca: return std::make_shared<OrcaCloudServiceAgent>(log_dir);
case AgentProvider::BBL: {
auto& plugin = BBLNetworkPlugin::instance();
if (!plugin.is_loaded()) {
return nullptr;
}
if (!plugin.has_agent()) {
plugin.create_agent(log_dir);
}
if (!plugin.has_agent()) {
return nullptr;
}
return std::make_shared<BBLCloudServiceAgent>();
}
default: return nullptr;
}
}
// ========================================================================
// NetworkAgent Facade Creation
// ========================================================================
/**
* Create a NetworkAgent from pre-created sub-agents
*
* @param cloud_agent Cloud service agent (required, includes auth)
* @param printer_agent Printer agent (optional, can be nullptr)
* @return Unique pointer to NetworkAgent facade
*/
static std::unique_ptr<NetworkAgent> create_from_agents(std::shared_ptr<ICloudServiceAgent> cloud_agent,
std::shared_ptr<IPrinterAgent> printer_agent)
{
return std::make_unique<NetworkAgent>(std::move(cloud_agent), std::move(printer_agent));
}
private:
// Factory is not instantiable
NetworkAgentFactory() = delete;
~NetworkAgentFactory() = delete;
NetworkAgentFactory(const NetworkAgentFactory&) = delete;
NetworkAgentFactory& operator=(const NetworkAgentFactory&) = delete;
};
/**
* Create a NetworkAgent from AppConfig settings (main entry point)
*
* Creates a NetworkAgent with cloud agent only. The printer agent is created
* separately when a printer is selected, via create_printer_agent_by_id().
*
* Cloud provider selection:
* - use_orca_cloud=true → OrcaCloudServiceAgent (default)
* - use_orca_cloud=false → BBLCloudServiceAgent (requires plugin)
*
* @param log_dir Directory for log files
* @param app_config Application configuration object
* @return NetworkAgent with cloud agent, or nullptr on failure
*/
inline std::unique_ptr<NetworkAgent> create_agent_from_config(const std::string& log_dir, AppConfig* app_config)
{
// Determine cloud provider from config
bool use_orca_cloud = false;
if (app_config) {
try {
use_orca_cloud = app_config->get("use_orca_cloud") == "true" || app_config->get_bool("use_orca_cloud");
} catch (...) {
use_orca_cloud = false;
}
}
// Create cloud agent
AgentProvider provider = use_orca_cloud ? AgentProvider::Orca : AgentProvider::BBL;
auto cloud_agent = NetworkAgentFactory::create_cloud_agent(provider, log_dir);
// Fall back to Orca if BBL plugin not available
if (!cloud_agent && provider == AgentProvider::BBL) {
BOOST_LOG_TRIVIAL(warning) << "BBL plugin not loaded, falling back to Orca cloud agent";
cloud_agent = NetworkAgentFactory::create_cloud_agent(AgentProvider::Orca, log_dir);
}
if (!cloud_agent) {
BOOST_LOG_TRIVIAL(error) << "Failed to create cloud agent";
return nullptr;
}
// auto bbl_printer_agent = NetworkAgentFactory::create_printer_agent_by_id("bbl", cloud_agent, log_dir);
// Create NetworkAgent with cloud agent only (printer agent added later)
// We will create the printer agent later when the printer is selected, so we pass nullptr for the printer agent here.
auto agent = NetworkAgentFactory::create_from_agents(std::move(cloud_agent), nullptr);
// Configure URL overrides for Orca cloud
if (agent && app_config && use_orca_cloud) {
auto* orca_cloud = dynamic_cast<OrcaCloudServiceAgent*>(agent->get_cloud_agent().get());
if (orca_cloud) {
orca_cloud->configure_urls(app_config);
}
}
BOOST_LOG_TRIVIAL(info) << "Created NetworkAgent with cloud agent";
return agent;
}
} // namespace Slic3r
#endif // __NETWORK_AGENT_FACTORY_HPP__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,359 @@
#ifndef __ORCA_CLOUD_SERVICE_AGENT_HPP__
#define __ORCA_CLOUD_SERVICE_AGENT_HPP__
#include "ICloudServiceAgent.hpp"
#include <string>
#include <map>
#include <mutex>
#include <memory>
#include <atomic>
#include <chrono>
#include <functional>
#include <thread>
#include <nlohmann/json.hpp>
class wxSecretStore;
namespace Slic3r {
// Forward declaration
class AppConfig;
// Constants for OAuth loopback server
namespace auth_constants {
constexpr int LOOPBACK_PORT = 41172;
constexpr const char* LOOPBACK_PATH = "/callback";
constexpr const char* TOKEN_PATH = "/auth/v1/token";
constexpr const char* LOGOUT_PATH = "/auth/v1/logout";
} // namespace auth_constants
// ============================================================================
// Sync Protocol Data Structures (per Orca Cloud Sync Protocol Specification)
// ============================================================================
// Note: These may also be defined in OrcaNetwork.hpp - guards prevent redefinition
#ifndef ORCA_SYNC_STRUCTS_DEFINED
#define ORCA_SYNC_STRUCTS_DEFINED
struct ProfileUpsert {
std::string id;
std::string name;
nlohmann::json content;
std::string updated_at;
std::string created_at;
};
struct SyncPullResponse {
std::string next_cursor;
std::vector<ProfileUpsert> upserts;
std::vector<std::string> deletes;
};
struct SyncPushResult {
bool success;
int http_code;
std::string new_updated_at;
ProfileUpsert server_version;
bool server_deleted;
std::string error_message;
};
struct SyncState {
std::string last_sync_timestamp;
};
#endif // ORCA_SYNC_STRUCTS_DEFINED
/**
* OrcaCloudServiceAgent - Native cloud service and authentication implementation for Orca Cloud.
*
* Implements the ICloudServiceAgent interface with:
* - Full OAuth 2.0 PKCE authentication support
* - Token storage via wxSecretStore with AES-256-GCM encrypted file fallback
* - JWT expiry decoding and proactive token refresh
* - Session management with thread-safe state access
* - Settings synchronization (sync_pull, sync_push)
* - Server connectivity management
* - HTTP helpers with automatic token injection
*
* This class combines the functionality of the former OrcaAuthAgent and OrcaCloudServiceAgent.
*/
class OrcaCloudServiceAgent : public ICloudServiceAgent {
public:
// ========================================================================
// Auth Session Types
// ========================================================================
struct SessionInfo {
std::string access_token;
std::string refresh_token;
std::string user_id;
std::string user_name;
std::string user_nickname;
std::string user_avatar;
std::chrono::system_clock::time_point expires_at{};
bool logged_in = false;
};
struct PkceBundle {
std::string verifier;
std::string challenge;
std::string state;
std::string redirect;
int loopback_port = auth_constants::LOOPBACK_PORT;
};
using SessionHandler = std::function<bool(const std::string&)>;
using OnLoginCompleteHandler = std::function<void(bool success, const std::string& user_id)>;
explicit OrcaCloudServiceAgent(std::string log_dir);
~OrcaCloudServiceAgent() override;
// Configuration
void configure_urls(AppConfig* app_config);
void set_api_base_url(const std::string& url);
void set_auth_base_url(const std::string& url);
void set_use_encrypted_token_file(bool use);
bool get_use_encrypted_token_file() const;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Lifecycle Methods
// ========================================================================
int init_log() override;
int set_config_dir(std::string config_dir) override;
int set_cert_file(std::string folder, std::string filename) override;
int set_country_code(std::string country_code) override;
int start() override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - User Session Management
// ========================================================================
int change_user(std::string user_info) override;
bool is_user_login() override;
int user_logout(bool request = false) override;
std::string get_user_id() override;
std::string get_user_name() override;
std::string get_user_avatar() override;
std::string get_user_nickname() override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Login UI Support
// ========================================================================
std::string build_login_cmd() override;
std::string build_logout_cmd() override;
std::string build_login_info() override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Token Access
// ========================================================================
std::string get_access_token() const override;
std::string get_refresh_token() const override;
bool ensure_token_fresh(const std::string& reason) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Server Connectivity
// ========================================================================
std::string get_cloud_service_host() override;
std::string get_cloud_login_url(const std::string& language = "") override;
int connect_server() override;
bool is_server_connected() override;
int refresh_connection() override;
int start_subscribe(std::string module) override;
int stop_subscribe(std::string module) override;
int add_subscribe(std::vector<std::string> dev_list) override;
int del_subscribe(std::vector<std::string> dev_list) override;
void enable_multi_machine(bool enable) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Settings Synchronization
// ========================================================================
int get_user_presets(std::map<std::string, std::map<std::string, std::string>>* user_presets) override;
std::string request_setting_id(std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code) override;
int put_setting(std::string setting_id, std::string name, std::map<std::string, std::string>* values_map, unsigned int* http_code) override;
int get_setting_list(std::string bundle_version, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int delete_setting(std::string setting_id) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Cloud User Services
// ========================================================================
int get_my_message(int type, int after, int limit, unsigned int* http_code, std::string* http_body) override;
int check_user_task_report(int* task_id, bool* printable) override;
int get_user_print_info(unsigned int* http_code, std::string* http_body) override;
int get_user_tasks(TaskQueryParams params, std::string* http_body) override;
int get_printer_firmware(std::string dev_id, unsigned* http_code, std::string* http_body) override;
int get_task_plate_index(std::string task_id, int* plate_index) override;
int get_user_info(int* identifier) override;
int get_subtask_info(std::string subtask_id, std::string* task_json, unsigned int* http_code, std::string* http_body) override;
int get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json) override;
int query_bind_status(std::vector<std::string> query_list, unsigned int* http_code, std::string* http_body) override;
int modify_printer_name(std::string dev_id, std::string dev_name) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Model Mall & Publishing
// ========================================================================
int get_camera_url(std::string dev_id, std::function<void(std::string)> callback) override;
int get_design_staffpick(int offset, int limit, std::function<void(std::string)> callback) override;
int start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out) override;
int get_model_publish_url(std::string* url) override;
int get_subtask(BBLModelTask* task, OnGetSubTaskFn getsub_fn) override;
int get_model_mall_home_url(std::string* url) override;
int get_model_mall_detail_url(std::string* url, std::string id) override;
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Analytics & Tracking
// ========================================================================
int track_enable(bool enable) override;
int track_remove_files() override;
int track_event(std::string evt_key, std::string content) override;
int track_header(std::string header) override;
int track_update_property(std::string name, std::string value, std::string type = "string") override;
int track_get_property(std::string name, std::string& value, std::string type = "string") override;
bool get_track_enable() override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Ratings & Reviews
// ========================================================================
int put_model_mall_rating(int design_id, int score, std::string content, std::vector<std::string> images, unsigned int& http_code, std::string& http_error) override;
int get_oss_config(std::string& config, std::string country_code, unsigned int& http_code, std::string& http_error) override;
int put_rating_picture_oss(std::string& config, std::string& pic_oss_path, std::string model_id, int profile_id, unsigned int& http_code, std::string& http_error) override;
int get_model_mall_rating_result(int job_id, std::string& rating_result, unsigned int& http_code, std::string& http_error) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Extra Features
// ========================================================================
int set_extra_http_header(std::map<std::string, std::string> extra_headers) override;
std::string get_studio_info_url() override;
int get_mw_user_preference(std::function<void(std::string)> callback) override;
int get_mw_user_4ulist(int seed, int limit, std::function<void(std::string)> callback) override;
std::string get_version() override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Callbacks
// ========================================================================
int set_on_user_login_fn(OnUserLoginFn fn) override;
int set_on_server_connected_fn(OnServerConnectedFn fn) override;
int set_on_http_error_fn(OnHttpErrorFn fn) override;
int set_get_country_code_fn(GetCountryCodeFn fn) override;
int set_queue_on_main_fn(QueueOnMainFn fn) override;
// Sync state management
void load_sync_state();
void save_sync_state();
void clear_sync_state();
const SyncState& get_sync_state() const { return sync_state; }
// ========================================================================
// Additional Public Methods - Auth
// ========================================================================
void set_session_handler(SessionHandler handler);
void set_on_login_complete_handler(OnLoginCompleteHandler handler);
const PkceBundle& pkce();
void regenerate_pkce();
void persist_refresh_token(const std::string& token);
bool load_refresh_token(std::string& out_token);
void clear_refresh_token();
// Token refresh helpers
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
bool refresh_from_storage(const std::string& reason, bool async = false);
bool refresh_now(const std::string& refresh_token, const std::string& reason, bool async = false);
bool refresh_session_with_token(const std::string& refresh_token);
// Session state helpers
bool set_user_session(const std::string& token,
const std::string& user_id,
const std::string& username,
const std::string& name,
const std::string& nickname,
const std::string& avatar,
const std::string& refresh_token = "");
void clear_session();
private:
// Sync protocol helpers
int sync_pull(
std::function<void(const SyncPullResponse&)> on_success,
std::function<void(int http_code, const std::string& error)> on_error
);
SyncPushResult sync_push(
const std::string& profile_id,
const std::string& name,
const nlohmann::json& content,
const std::string& original_updated_at = ""
);
// HTTP request helpers
int http_get(const std::string& path, std::string* response_body, unsigned int* http_code);
int http_post(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
int http_put(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
int http_delete(const std::string& path, std::string* response_body, unsigned int* http_code);
std::map<std::string, std::string> data_headers();
bool attempt_refresh_after_unauthorized(const std::string& reason);
// Auth HTTP helpers
bool http_post_token(const std::string& body, std::string* response_body, unsigned int* http_code, const std::string& url = "");
bool http_post_auth(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
bool exchange_auth_code(const std::string& auth_code, const std::string& state, std::string& session_payload);
void update_redirect_uri();
void compute_fallback_path();
bool decode_jwt_expiry(const std::string& token, std::chrono::system_clock::time_point& out_tp);
bool should_refresh_locked(std::chrono::seconds skew) const;
void invoke_user_login_callback(int online_login, bool login);
// Callback invocation
void invoke_server_connected_callback(int return_code, int reason_code);
void invoke_http_error_callback(unsigned http_code, const std::string& http_body);
// JSON helpers
std::string map_to_json(const std::map<std::string, std::string>& map);
void json_to_map(const std::string& json, std::map<std::string, std::string>& map);
// Member variables - configuration
std::string log_dir;
std::string config_dir;
std::string api_base_url;
std::string auth_base_url;
std::string country_code;
std::map<std::string, std::string> extra_headers;
std::map<std::string, std::string> auth_headers;
mutable std::mutex headers_mutex;
bool m_use_encrypted_token_file{false};
// Member variables - auth state
PkceBundle pkce_bundle;
std::string refresh_fallback_path;
SessionHandler session_handler;
OnLoginCompleteHandler on_login_complete_handler;
SessionInfo session;
mutable std::mutex session_mutex;
// Member variables - connection state
bool is_connected{false};
bool enable_track{false};
bool multi_machine_enabled{false};
// Sync state
SyncState sync_state;
std::string sync_state_path;
// Callbacks
OnUserLoginFn on_user_login_fn;
OnServerConnectedFn on_server_connected_fn;
OnHttpErrorFn on_http_error_fn;
GetCountryCodeFn get_country_code_fn;
QueueOnMainFn queue_on_main_fn;
mutable std::mutex callback_mutex;
// Thread safety
mutable std::recursive_mutex state_mutex;
std::thread refresh_thread;
std::atomic_bool refresh_running{false};
};
} // namespace Slic3r
#endif // __ORCA_CLOUD_SERVICE_AGENT_HPP__

View File

@@ -0,0 +1,245 @@
#include "OrcaPrinterAgent.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
const std::string OrcaPrinterAgent_VERSION = "0.0.1";
OrcaPrinterAgent::OrcaPrinterAgent(std::string log_dir) : log_dir(std::move(log_dir))
{
BOOST_LOG_TRIVIAL(info) << "OrcaPrinterAgent: Constructor - log_dir=" << this->log_dir;
}
OrcaPrinterAgent::~OrcaPrinterAgent() = default;
void OrcaPrinterAgent::set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud)
{
std::lock_guard<std::mutex> lock(state_mutex);
m_cloud_agent = cloud;
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: Cloud agent set";
}
// ============================================================================
// Communication - All Stubs
// ============================================================================
int OrcaPrinterAgent::send_message(std::string dev_id, std::string json_str, int qos, int flag)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: send_message (stub) - dev_id=" << dev_id;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: connect_printer (stub) - dev_id=" << dev_id << ", dev_ip=" << dev_ip;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::disconnect_printer()
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: disconnect_printer (stub)";
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: send_message_to_printer (stub) - dev_id=" << dev_id;
return BAMBU_NETWORK_SUCCESS;
}
// ============================================================================
// Certificates - All Stubs
// ============================================================================
int OrcaPrinterAgent::check_cert()
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: check_cert (stub)";
return BAMBU_NETWORK_SUCCESS;
}
void OrcaPrinterAgent::install_device_cert(std::string dev_id, bool lan_only)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: install_device_cert (stub) - dev_id=" << dev_id;
}
// ============================================================================
// Discovery - Stub
// ============================================================================
bool OrcaPrinterAgent::start_discovery(bool start, bool sending)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: start_discovery (stub) - start=" << start << ", sending=" << sending;
return true;
}
// ============================================================================
// Binding - All Stubs
// ============================================================================
int OrcaPrinterAgent::ping_bind(std::string ping_code)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: ping_bind (stub) - ping_code=" << ping_code;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: bind_detect (stub) - dev_ip=" << dev_ip;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::bind(
std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: bind (stub) - dev_id=" << dev_id;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::unbind(std::string dev_id)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: unbind (stub) - dev_id=" << dev_id;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::request_bind_ticket(std::string* ticket)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: request_bind_ticket (stub)";
if (ticket)
*ticket = "";
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_server_callback(OnServerErrFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_server_err_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
// ============================================================================
// Machine Selection
// ============================================================================
std::string OrcaPrinterAgent::get_user_selected_machine()
{
std::lock_guard<std::mutex> lock(state_mutex);
return selected_machine;
}
int OrcaPrinterAgent::set_user_selected_machine(std::string dev_id)
{
std::lock_guard<std::mutex> lock(state_mutex);
selected_machine = dev_id;
return BAMBU_NETWORK_SUCCESS;
}
// ============================================================================
// Agent Information
// ============================================================================
AgentInfo OrcaPrinterAgent::get_agent_info_static()
{
return AgentInfo{.id = "orca",
.name = "Orca Printer Agent",
.version = OrcaPrinterAgent_VERSION,
.description = "Orca Printer Communication Protocol Agent"};
}
// ============================================================================
// Print Job Operations - All Stubs
// ============================================================================
int OrcaPrinterAgent::start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: start_print (stub) - task_name=" << params.task_name;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::start_local_print_with_record(PrintParams params,
OnUpdateStatusFn update_fn,
WasCancelledFn cancel_fn,
OnWaitFn wait_fn)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: start_local_print_with_record (stub)";
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: start_send_gcode_to_sdcard (stub)";
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: start_local_print (stub)";
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaPrinterAgent: start_sdcard_print (stub)";
return BAMBU_NETWORK_SUCCESS;
}
// ============================================================================
// Callback Registration
// ============================================================================
int OrcaPrinterAgent::set_on_ssdp_msg_fn(OnMsgArrivedFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_ssdp_msg_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_on_printer_connected_fn(OnPrinterConnectedFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_printer_connected_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_on_subscribe_failure_fn(GetSubscribeFailureFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_subscribe_failure_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_on_message_fn(OnMessageFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_message_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_on_user_message_fn(OnMessageFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_user_message_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_on_local_connect_fn(OnLocalConnectedFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_local_connect_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_on_local_message_fn(OnMessageFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
on_local_message_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
int OrcaPrinterAgent::set_queue_on_main_fn(QueueOnMainFn fn)
{
std::lock_guard<std::mutex> lock(state_mutex);
queue_on_main_fn = fn;
return BAMBU_NETWORK_SUCCESS;
}
} // namespace Slic3r

View File

@@ -0,0 +1,100 @@
#ifndef __ORCA_PRINTER_AGENT_HPP__
#define __ORCA_PRINTER_AGENT_HPP__
#include "IPrinterAgent.hpp"
#include "ICloudServiceAgent.hpp"
#include <string>
#include <mutex>
#include <memory>
namespace Slic3r {
/**
* OrcaPrinterAgent - Stub implementation for printer operations.
*
* All printer-related operations are currently stubs that return success.
* Actual printer connectivity requires the BBL SDK or future Orca implementation.
*/
class OrcaPrinterAgent : public IPrinterAgent {
public:
explicit OrcaPrinterAgent(std::string log_dir);
~OrcaPrinterAgent() override;
// ========================================================================
// IPrinterAgent Interface Implementation
// ========================================================================
void set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud) override;
// Communication
int send_message(std::string dev_id, std::string json_str, int qos, int flag) override;
int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override;
int disconnect_printer() override;
int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) override;
// Certificates
int check_cert() override;
void install_device_cert(std::string dev_id, bool lan_only) override;
// Discovery
bool start_discovery(bool start, bool sending) override;
// Binding
int ping_bind(std::string ping_code) override;
int bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect) override;
int bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn) override;
int unbind(std::string dev_id) override;
int request_bind_ticket(std::string* ticket) override;
int set_server_callback(OnServerErrFn fn) override;
// Machine Selection
std::string get_user_selected_machine() override;
int set_user_selected_machine(std::string dev_id) override;
/**
* Get agent information.
*
* @return AgentInfo struct containing agent identification and descriptive information
*/
static AgentInfo get_agent_info_static();
AgentInfo get_agent_info() override { return get_agent_info_static(); }
// Print Job Operations
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
// Callbacks
int set_on_ssdp_msg_fn(OnMsgArrivedFn fn) override;
int set_on_printer_connected_fn(OnPrinterConnectedFn fn) override;
int set_on_subscribe_failure_fn(GetSubscribeFailureFn fn) override;
int set_on_message_fn(OnMessageFn fn) override;
int set_on_user_message_fn(OnMessageFn fn) override;
int set_on_local_connect_fn(OnLocalConnectedFn fn) override;
int set_on_local_message_fn(OnMessageFn fn) override;
int set_queue_on_main_fn(QueueOnMainFn fn) override;
private:
std::string log_dir;
std::string selected_machine;
std::shared_ptr<ICloudServiceAgent> m_cloud_agent;
// Callbacks
OnMsgArrivedFn on_ssdp_msg_fn;
OnPrinterConnectedFn on_printer_connected_fn;
GetSubscribeFailureFn on_subscribe_failure_fn;
OnMessageFn on_message_fn;
OnMessageFn on_user_message_fn;
OnLocalConnectedFn on_local_connect_fn;
OnMessageFn on_local_message_fn;
QueueOnMainFn queue_on_main_fn;
OnServerErrFn on_server_err_fn;
mutable std::mutex state_mutex;
};
} // namespace Slic3r
#endif // __ORCA_PRINTER_AGENT_HPP__

View File

@@ -849,7 +849,7 @@ void PresetUpdater::priv::sync_plugins(std::string http_url, std::string plugin_
BOOST_LOG_TRIVIAL(info) << "non need to sync plugins for there is no plugins currently.";
return;
}
std::string curr_version = NetworkAgent::use_legacy_network ? BAMBU_NETWORK_AGENT_VERSION_LEGACY : BBL::get_latest_network_version();
std::string curr_version = NetworkAgent::use_legacy_network ? BAMBU_NETWORK_AGENT_VERSION_LEGACY : get_latest_network_version();
std::string using_version = curr_version.substr(0, 9) + "00";
std::string cached_version;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,137 @@
#ifndef __QIDI_PRINTER_AGENT_HPP__
#define __QIDI_PRINTER_AGENT_HPP__
#include "OrcaPrinterAgent.hpp"
#include <atomic>
#include <cstdint>
#include <map>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <vector>
namespace Slic3r {
class QidiPrinterAgent final : public OrcaPrinterAgent
{
public:
explicit QidiPrinterAgent(std::string log_dir);
~QidiPrinterAgent() override;
static AgentInfo get_agent_info_static();
AgentInfo get_agent_info() override { return get_agent_info_static(); }
int send_message(std::string dev_id, std::string json_str, int qos, int flag) override;
int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) override;
int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override;
int disconnect_printer() override;
bool start_discovery(bool start, bool sending) override;
int bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect) override;
int set_on_ssdp_msg_fn(OnMsgArrivedFn fn) override;
int set_on_printer_connected_fn(OnPrinterConnectedFn fn) override;
int set_on_message_fn(OnMessageFn fn) override;
int set_on_local_connect_fn(OnLocalConnectedFn fn) override;
int set_on_local_message_fn(OnMessageFn fn) override;
int set_queue_on_main_fn(QueueOnMainFn fn) override;
FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; }
void fetch_filament_info(std::string dev_id) override;
private:
struct PrinthostConfig
{
std::string host;
std::string port;
std::string api_key;
std::string base_url;
std::string model_id;
std::string model_name;
};
struct QidiDeviceInfo
{
std::string dev_id;
std::string dev_name;
std::string model_id;
std::string version;
};
struct QidiSlotInfo
{
int slot_index = 0;
int color_index = 0;
int filament_type = 0;
int vendor_type = 0;
bool filament_exists = false;
};
struct QidiFilamentDict
{
std::map<int, std::string> colors;
std::map<int, std::string> filaments;
};
int handle_request(const std::string& dev_id, const std::string& json_str);
int send_version_info(const std::string& dev_id);
int send_access_code(const std::string& dev_id);
bool get_printhost_config(PrinthostConfig& config) const;
bool fetch_device_info(const std::string& base_url, const std::string& api_key, QidiDeviceInfo& info, std::string& error) const;
bool fetch_server_info(const std::string& base_url, const std::string& api_key, std::string& version, std::string& error) const;
bool fetch_object_list(const std::string& base_url, const std::string& api_key, std::set<std::string>& objects, std::string& error) const;
std::string resolve_host(const std::string& dev_id) const;
std::string resolve_api_key(const std::string& dev_id, const std::string& fallback) const;
void store_host(const std::string& dev_id, const std::string& host, const std::string& api_key);
bool fetch_slot_info(const std::string& base_url,
const std::string& api_key,
std::vector<QidiSlotInfo>& slots,
int& box_count,
std::string& error) const;
bool fetch_filament_dict(const std::string& base_url, const std::string& api_key, QidiFilamentDict& dict, std::string& error) const;
static void parse_ini_section(const std::string& content, const std::string& section_name, std::map<int, std::string>& result);
static void parse_filament_sections(const std::string& content, std::map<int, std::string>& result);
static std::string normalize_color(const std::string& color);
static std::string map_filament_type_to_setting_id(const std::string& filament_type);
void announce_printhost_device();
void dispatch_local_connect(int state, const std::string& dev_id, const std::string& msg);
void dispatch_printer_connected(const std::string& dev_id);
void dispatch_message(const std::string& dev_id, const std::string& payload);
void start_status_stream(const std::string& dev_id, const std::string& base_url, const std::string& api_key);
void stop_status_stream();
void run_status_stream(std::string dev_id, std::string base_url, std::string api_key);
void handle_ws_message(const std::string& dev_id, const std::string& payload);
void update_status_cache(const nlohmann::json& updates);
nlohmann::json build_print_payload_locked(const nlohmann::json* ams_override) const;
mutable std::mutex state_mutex;
std::map<std::string, std::string> host_by_device;
std::map<std::string, std::string> api_key_by_device;
std::string ssdp_announced_host;
std::string ssdp_announced_id;
OnMsgArrivedFn on_ssdp_msg_fn;
OnPrinterConnectedFn on_printer_connected_fn;
OnLocalConnectedFn on_local_connect_fn;
OnMessageFn on_message_fn;
OnMessageFn on_local_message_fn;
QueueOnMainFn queue_on_main_fn;
mutable std::mutex payload_mutex;
nlohmann::json status_cache;
nlohmann::json last_ams_payload;
std::atomic<bool> ws_stop{false};
std::atomic<uint64_t> ws_last_emit_ms{0};
std::thread ws_thread;
};
} // namespace Slic3r
#endif

View File

@@ -10,7 +10,7 @@
extern std::string g_log_folder;
extern std::string g_log_start_time;
namespace BBL {
namespace Slic3r {
#define BAMBU_NETWORK_SUCCESS 0
#define BAMBU_NETWORK_ERR_INVALID_HANDLE -1