From f7caf0db07266a6ec4f7518143028ec0bc93e5d7 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Thu, 23 Jul 2026 21:04:08 +0800 Subject: [PATCH 01/15] feat(plugin): storage API --- src/slic3r/plugin/host/PluginHost.cpp | 51 +++++++++++++++++++ src/slic3r/plugin/host/PluginHostBindings.hpp | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/slic3r/plugin/host/PluginHost.cpp b/src/slic3r/plugin/host/PluginHost.cpp index 524f07f12c..5fca4d4fbc 100644 --- a/src/slic3r/plugin/host/PluginHost.cpp +++ b/src/slic3r/plugin/host/PluginHost.cpp @@ -1,9 +1,59 @@ #include "PluginHost.hpp" #include "PluginHostBindings.hpp" #include "PluginHostUi.hpp" +#include +#include +#include +#include +#include + +#include namespace Slic3r { +namespace host_bindings { +void register_plugin(pybind11::module_& host) +{ + auto plugin_host = host.def_submodule("plugin", "Plugin host API"); + + plugin_host.def( + "storage", + []() -> std::string { + const std::string plugin_key = PluginAuditManager::instance().current_plugin(); + if (plugin_key.empty()) + throw std::runtime_error("plugin.storage() must be called from a plugin callback"); + + PluginDescriptor descriptor; + if (!PluginManager::instance().try_get_plugin_descriptor(plugin_key, descriptor)) + throw std::runtime_error("The current plugin is not registered"); + + // plugin_root is populated for installed packages. If it is unavailable, the entry + // path still identifies the same package directory. This is important for local + // plugins: their directory is based on the source filename (including its extension), + // while plugin_key is based on the filename stem. + const boost::filesystem::path plugin_root = resolve_plugin_root_from_descriptor(descriptor); + if (!plugin_root.empty()) + return plugin_root.string(); + + if (!descriptor.is_cloud_plugin()) + throw std::runtime_error("The current local plugin folder is unavailable"); + + if (wxTheApp == nullptr || GUI::wxGetApp().getAgent() == nullptr) + throw std::runtime_error("Cloud plugin storage is unavailable before networking is initialized"); + + const std::string user_id = GUI::wxGetApp().getAgent()->get_user_id(); + if (user_id.empty()) + throw std::runtime_error("Cloud plugin storage is unavailable without a logged-in user"); + + if (!is_valid_plugin_id(plugin_key)) + throw std::runtime_error("The current cloud plugin key is not a valid folder name"); + + return (boost::filesystem::path(get_cloud_plugin_dir(user_id)) / plugin_key).string(); + }, + "Return the installed folder of the current plugin."); +} +} // namespace host_bindings + void PluginHost::RegisterBindings(pybind11::module_& module) { auto host = module.def_submodule("host", "Host application API"); @@ -15,6 +65,7 @@ void PluginHost::RegisterBindings(pybind11::module_& module) host_bindings::register_presets(host); host_bindings::register_model(host); host_bindings::register_app(host); + host_bindings::register_plugin(host); // UI: native dialogs and interactive HTML windows for plugins. PluginHostUi::RegisterBindings(host); diff --git a/src/slic3r/plugin/host/PluginHostBindings.hpp b/src/slic3r/plugin/host/PluginHostBindings.hpp index 0f206d5992..94601ad99c 100644 --- a/src/slic3r/plugin/host/PluginHostBindings.hpp +++ b/src/slic3r/plugin/host/PluginHostBindings.hpp @@ -12,5 +12,5 @@ void register_presets(pybind11::module_& host); // PluginHostPresets.cpp void register_model(pybind11::module_& host); // PluginHostModel.cpp void register_app(pybind11::module_& host); // PluginHostApp.cpp void register_slicing(pybind11::module_& host); // PluginHostSlicing.cpp - +void register_plugin(pybind11::module_& host); // PluginHost.cpp } // namespace Slic3r::host_bindings From 2e246341d16bc655f409d2882365508b020c097b Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Fri, 24 Jul 2026 18:57:46 +0800 Subject: [PATCH 02/15] move the storage directory outside the actual plugin code folder --- src/slic3r/plugin/PluginFsUtils.cpp | 2 +- src/slic3r/plugin/PluginFsUtils.hpp | 1 + src/slic3r/plugin/PluginManager.cpp | 32 +++++++++++++++++++++++++++ src/slic3r/plugin/PluginManager.hpp | 4 ++++ src/slic3r/plugin/host/PluginHost.cpp | 30 +------------------------ 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/slic3r/plugin/PluginFsUtils.cpp b/src/slic3r/plugin/PluginFsUtils.cpp index 445dd00b9b..63dc229967 100644 --- a/src/slic3r/plugin/PluginFsUtils.cpp +++ b/src/slic3r/plugin/PluginFsUtils.cpp @@ -632,7 +632,7 @@ void parse_metadata_rfc822(const std::string& content, bool is_ignored_plugin_directory(const boost::filesystem::path& path) { const std::string name = path.filename().string(); - return name.empty() || name[0] == '.' || name.rfind("__", 0) == 0 || name == PLUGIN_SUBSCRIBED_DIR; + return name.empty() || name[0] == '.' || name.rfind("__", 0) == 0 || name == PLUGIN_SUBSCRIBED_DIR || name == PLUGIN_DATA_DIR; } bool is_safe_relative_path(const boost::filesystem::path& path) diff --git a/src/slic3r/plugin/PluginFsUtils.hpp b/src/slic3r/plugin/PluginFsUtils.hpp index 7922946b1c..5f57dbf807 100644 --- a/src/slic3r/plugin/PluginFsUtils.hpp +++ b/src/slic3r/plugin/PluginFsUtils.hpp @@ -12,6 +12,7 @@ #include #define PLUGIN_SUBSCRIBED_DIR "_subscribed" +#define PLUGIN_DATA_DIR "plugin_data" namespace Slic3r { diff --git a/src/slic3r/plugin/PluginManager.cpp b/src/slic3r/plugin/PluginManager.cpp index 761a9aad63..abc2446d55 100644 --- a/src/slic3r/plugin/PluginManager.cpp +++ b/src/slic3r/plugin/PluginManager.cpp @@ -486,6 +486,38 @@ bool PluginManager::try_get_plugin_descriptor_for_capability(const std::string& return false; } +std::string PluginManager::get_storage_dir(const std::string& plugin_key) const +{ + namespace fs = boost::filesystem; + + PluginDescriptor descriptor; + if (!try_get_plugin_descriptor(plugin_key, descriptor)) + throw std::runtime_error("The current plugin is not registered"); + + const fs::path base_storage_dir = fs::path(get_orca_plugins_dir()) / PLUGIN_DATA_DIR; + + if (!descriptor.is_cloud_plugin()) { + const fs::path local_storage_dir = base_storage_dir / plugin_key; + fs::create_directories(local_storage_dir); + return local_storage_dir.string(); + } + + auto agent = m_cloud_service.get_cloud_agent(); + if (!agent) + throw std::runtime_error("Cloud plugin storage is unavailable before networking is initialized"); + + const std::string user_id = agent->get_user_id(); + if (user_id.empty()) + throw std::runtime_error("Cloud plugin storage is unavailable without a logged-in user"); + + if (!is_valid_plugin_id(plugin_key)) + throw std::runtime_error("The current cloud plugin key is not a valid folder name"); + + const fs::path cloud_storage_dir = base_storage_dir / PLUGIN_SUBSCRIBED_DIR / user_id / plugin_key; + fs::create_directories(cloud_storage_dir); + return cloud_storage_dir.string(); +} + // ── Capability instances ──────────────────────────────────────────────────────────────────── std::vector> PluginManager::get_plugin_capabilities(const std::string& plugin_key, diff --git a/src/slic3r/plugin/PluginManager.hpp b/src/slic3r/plugin/PluginManager.hpp index 59a2791355..a0bab2afe3 100644 --- a/src/slic3r/plugin/PluginManager.hpp +++ b/src/slic3r/plugin/PluginManager.hpp @@ -138,6 +138,10 @@ public: bool try_get_plugin_descriptor_for_capability(const std::string& capability_name, PluginCapabilityType type, PluginDescriptor& out) const; + // Per-plugin storage directory under orca_plugins/plugin_data, created if missing. Throws + // std::runtime_error if the plugin is unregistered, the key is invalid, or (cloud plugins) + // no user is logged in yet. + std::string get_storage_dir(const std::string& plugin_key) const; std::vector> get_plugin_capabilities( const std::string& plugin_key = "", // "" => all plugins diff --git a/src/slic3r/plugin/host/PluginHost.cpp b/src/slic3r/plugin/host/PluginHost.cpp index 5fca4d4fbc..2830d6f276 100644 --- a/src/slic3r/plugin/host/PluginHost.cpp +++ b/src/slic3r/plugin/host/PluginHost.cpp @@ -2,10 +2,7 @@ #include "PluginHostBindings.hpp" #include "PluginHostUi.hpp" #include -#include -#include #include -#include #include @@ -23,32 +20,7 @@ void register_plugin(pybind11::module_& host) if (plugin_key.empty()) throw std::runtime_error("plugin.storage() must be called from a plugin callback"); - PluginDescriptor descriptor; - if (!PluginManager::instance().try_get_plugin_descriptor(plugin_key, descriptor)) - throw std::runtime_error("The current plugin is not registered"); - - // plugin_root is populated for installed packages. If it is unavailable, the entry - // path still identifies the same package directory. This is important for local - // plugins: their directory is based on the source filename (including its extension), - // while plugin_key is based on the filename stem. - const boost::filesystem::path plugin_root = resolve_plugin_root_from_descriptor(descriptor); - if (!plugin_root.empty()) - return plugin_root.string(); - - if (!descriptor.is_cloud_plugin()) - throw std::runtime_error("The current local plugin folder is unavailable"); - - if (wxTheApp == nullptr || GUI::wxGetApp().getAgent() == nullptr) - throw std::runtime_error("Cloud plugin storage is unavailable before networking is initialized"); - - const std::string user_id = GUI::wxGetApp().getAgent()->get_user_id(); - if (user_id.empty()) - throw std::runtime_error("Cloud plugin storage is unavailable without a logged-in user"); - - if (!is_valid_plugin_id(plugin_key)) - throw std::runtime_error("The current cloud plugin key is not a valid folder name"); - - return (boost::filesystem::path(get_cloud_plugin_dir(user_id)) / plugin_key).string(); + return PluginManager::instance().get_storage_dir(plugin_key); }, "Return the installed folder of the current plugin."); } From 58be7f486135bbe98c7d2131c3ead7916d71bce9 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Fri, 7 Aug 2026 18:36:38 +0800 Subject: [PATCH 03/15] feat: abstract remaining gcode commands in devicemanager --- src/slic3r/GUI/DeviceManager.cpp | 97 +++++++++------------------ src/slic3r/Utils/BBLPrinterAgent.cpp | 99 ++++++++++++++++++++++++++++ src/slic3r/Utils/BBLPrinterAgent.hpp | 7 ++ src/slic3r/Utils/IPrinterAgent.hpp | 13 ++++ src/slic3r/Utils/NetworkAgent.cpp | 43 ++++++++++++ src/slic3r/Utils/NetworkAgent.hpp | 7 ++ 6 files changed, 200 insertions(+), 66 deletions(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index cb29fbe11a..8a576cfc77 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -1438,26 +1438,29 @@ int MachineObject::command_upgrade_module(std::string url, std::string module_ty int MachineObject::command_xyz_abs() { - return this->publish_gcode("G90 \n"); + if (!m_agent) return -1; + int rtn = m_agent->command_xyz_abs(get_dev_id(), MachineObject::m_sequence_id++, is_lan_mode_printer()); + if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) + show_unsupported_dlg(rtn); + return rtn; } int MachineObject::command_auto_leveling() { - return this->publish_gcode("G29 \n"); + if (!m_agent) return -1; + int rtn = m_agent->command_auto_leveling(get_dev_id(), MachineObject::m_sequence_id++, is_lan_mode_printer()); + if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) + show_unsupported_dlg(rtn); + return rtn; } int MachineObject::command_go_home() { - if (m_support_mqtt_homing) - { - json j; - j["print"]["command"] = "back_to_center"; - j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j); - } - - // gcode command - return this->is_in_printing() ? this->publish_gcode("G28 X\n") : this->publish_gcode("G28 \n"); + if (!m_agent) return -1; + int rtn = m_agent->command_go_home(get_dev_id(), this->is_in_printing(), m_support_mqtt_homing, MachineObject::m_sequence_id++, is_lan_mode_printer()); + if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) + show_unsupported_dlg(rtn); + return rtn; } int MachineObject::command_task_partskip(std::vector part_ids) @@ -1579,23 +1582,20 @@ int MachineObject::command_stop_buzzer() int MachineObject::command_set_bed(int temp) { - if (m_support_mqtt_bet_ctrl) - { - json j; - j["print"]["command"] = "set_bed_temp"; - j["print"]["temp"] = temp; - j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j); - } - - std::string gcode_str = (boost::format("M140 S%1%\n") % temp).str(); - return this->publish_gcode(gcode_str); + if (!m_agent) return -1; + int rtn = m_agent->command_set_bed(get_dev_id(), temp, m_support_mqtt_bet_ctrl, MachineObject::m_sequence_id++, is_lan_mode_printer()); + if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) + show_unsupported_dlg(rtn); + return rtn; } int MachineObject::command_set_nozzle(int temp) { - std::string gcode_str = (boost::format("M104 S%1%\n") % temp).str(); - return this->publish_gcode(gcode_str); + if (!m_agent) return -1; + int rtn = m_agent->command_set_nozzle(get_dev_id(), temp, MachineObject::m_sequence_id++, is_lan_mode_printer()); + if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) + show_unsupported_dlg(rtn); + return rtn; } int MachineObject::command_set_nozzle_new(int nozzle_id, int temp) @@ -1925,47 +1925,12 @@ int MachineObject::command_ams_air_print_detect(bool air_print_detect) int MachineObject::command_axis_control(std::string axis, double unit, double input_val, int speed) { - if (m_support_mqtt_axis_control) - { - int dir = input_val > 0 ? 1 : -1; - // i3-arch printers move the bed for Y/Z, so the on-screen direction is - // reversed — same negation the g-code fallback below applies. - if (!is_core_xy() && (axis.compare("Y") == 0 || axis.compare("Z") == 0)) { - dir = -dir; - } - - json j; - j["print"]["command"] = "xyz_ctrl"; - j["print"]["axis"] = axis; - j["print"]["dir"] = dir; - j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0; - j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j); - } - - double value = input_val; - if (!is_core_xy()) { - if ( axis.compare("Y") == 0 - || axis.compare("Z") == 0) { - value = -1.0 * input_val; - } - } - - char cmd[256]; - if (axis.compare("X") == 0 - || axis.compare("Y") == 0 - || axis.compare("Z") == 0) { - sprintf(cmd, "M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %s%0.1f F%d\nM1002 pop_ref_mode\nM211 R\n", axis.c_str(), value * unit, speed); - } - else if (axis.compare("E") == 0) { - sprintf(cmd, "M83 \nG0 %s%0.1f F%d\n", axis.c_str(), value * unit, speed); - } - else { - return -1; - } - - - return this->publish_gcode(cmd); + if (!m_agent) return -1; + int rtn = m_agent->command_axis_control(get_dev_id(), axis, unit, input_val, speed, is_core_xy(), + m_support_mqtt_axis_control, MachineObject::m_sequence_id++, is_lan_mode_printer()); + if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) + show_unsupported_dlg(rtn); + return rtn; } int MachineObject::command_extruder_control(int nozzle_id, double val) diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 9d422552fe..88323e868c 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace Slic3r { @@ -70,6 +71,104 @@ int BBLPrinterAgent::command_ams_select_tray(std::string dev_id, std::string tra return publish(dev_id, j, lan_mode); } +int BBLPrinterAgent::command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode) +{ + nlohmann::json j; + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = "G90 \n"; + j["print"]["sequence_id"] = std::to_string(sequence_id); + return publish(dev_id, j, lan_mode); +} + +int BBLPrinterAgent::command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode) +{ + nlohmann::json j; + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = "G29 \n"; + j["print"]["sequence_id"] = std::to_string(sequence_id); + return publish(dev_id, j, lan_mode); +} + +int BBLPrinterAgent::command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing, int sequence_id, bool lan_mode) +{ + nlohmann::json j; + j["print"]["sequence_id"] = std::to_string(sequence_id); + if (supports_mqtt_homing) { + j["print"]["command"] = "back_to_center"; + return publish(dev_id, j, lan_mode); + } + + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = is_printing ? "G28 X\n" : "G28 \n"; + return publish(dev_id, j, lan_mode); +} + +int BBLPrinterAgent::command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl, int sequence_id, bool lan_mode) +{ + nlohmann::json j; + j["print"]["sequence_id"] = std::to_string(sequence_id); + if (supports_mqtt_bed_ctrl) { + j["print"]["command"] = "set_bed_temp"; + j["print"]["temp"] = temp; + return publish(dev_id, j, lan_mode); + } + + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = (boost::format("M140 S%1%\n") % temp).str(); + return publish(dev_id, j, lan_mode); +} + +int BBLPrinterAgent::command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode) +{ + nlohmann::json j; + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = (boost::format("M104 S%1%\n") % temp).str(); + j["print"]["sequence_id"] = std::to_string(sequence_id); + return publish(dev_id, j, lan_mode); +} + +int BBLPrinterAgent::command_axis_control(std::string dev_id, std::string axis, double unit, double input_val, int speed, + bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode) +{ + nlohmann::json j; + j["print"]["sequence_id"] = std::to_string(sequence_id); + + if (supports_mqtt_axis_control) { + int dir = input_val > 0 ? 1 : -1; + // i3-arch printers move the bed for Y/Z, so the on-screen direction is + // reversed -- same negation the g-code fallback below applies. + if (!is_core_xy && (axis == "Y" || axis == "Z")) { + dir = -dir; + } + + j["print"]["command"] = "xyz_ctrl"; + j["print"]["axis"] = axis; + j["print"]["dir"] = dir; + j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0; + return publish(dev_id, j, lan_mode); + } + + double value = input_val; + if (!is_core_xy && (axis == "Y" || axis == "Z")) { + value = -1.0 * input_val; + } + + std::string value_str = (boost::format("%.1f") % (value * unit)).str(); + std::string gcode; + if (axis == "X" || axis == "Y" || axis == "Z") { + gcode = (boost::format("M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %1%%2% F%3%\nM1002 pop_ref_mode\nM211 R\n") + % axis % value_str % speed).str(); + } else if (axis == "E") { + gcode = (boost::format("M83 \nG0 %1%%2% F%3%\n") % axis % value_str % speed).str(); + } else { + return -1; + } + + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = gcode; + return publish(dev_id, j, lan_mode); +} + int BBLPrinterAgent::publish(const std::string& dev_id, const nlohmann::json& j, bool lan_mode) { const int rtn = lan_mode ? send_message_to_printer(dev_id, j.dump(), 0, 0) : send_message(dev_id, j.dump(), 0, 0); diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index a04cd00175..df51c7eb51 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -35,6 +35,13 @@ public: int command_ams_refresh_rfid(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override; int command_ams_calibrate(std::string dev_id, int ams_id, int sequence_id, bool lan_mode) override; int command_ams_select_tray(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override; + int command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode) override; + int command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode) override; + int command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing, int sequence_id, bool lan_mode) override; + int command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl, int sequence_id, bool lan_mode) override; + int command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode) override; + int command_axis_control(std::string dev_id, std::string axis, double unit, double input_val, int speed, + bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode) 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; diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 85a1ffb8fc..fc05a23ba3 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -93,6 +93,19 @@ public: { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } virtual int command_ams_select_tray(std::string, std::string, int, bool) { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing, int sequence_id, bool lan_mode) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl, int sequence_id, bool lan_mode) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int command_axis_control(std::string dev_id, std::string axis, double unit, double input_val, int speed, + bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } /** * Establish a direct LAN connection to a printer. diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index b169fca052..ba719c256a 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -788,6 +788,49 @@ int NetworkAgent::command_ams_select_tray(std::string dev_id, std::string tray_i return -1; } +int NetworkAgent::command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode) +{ + if (m_printer_agent) + return m_printer_agent->command_xyz_abs(dev_id, sequence_id, lan_mode); + return -1; +} + +int NetworkAgent::command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode) +{ + if (m_printer_agent) + return m_printer_agent->command_auto_leveling(dev_id, sequence_id, lan_mode); + return -1; +} + +int NetworkAgent::command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing, int sequence_id, bool lan_mode) +{ + if (m_printer_agent) + return m_printer_agent->command_go_home(dev_id, is_printing, supports_mqtt_homing, sequence_id, lan_mode); + return -1; +} + +int NetworkAgent::command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl, int sequence_id, bool lan_mode) +{ + if (m_printer_agent) + return m_printer_agent->command_set_bed(dev_id, temp, supports_mqtt_bed_ctrl, sequence_id, lan_mode); + return -1; +} + +int NetworkAgent::command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode) +{ + if (m_printer_agent) + return m_printer_agent->command_set_nozzle(dev_id, temp, sequence_id, lan_mode); + return -1; +} + +int NetworkAgent::command_axis_control(std::string dev_id, std::string axis, double unit, double input_val, int speed, + bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode) +{ + if (m_printer_agent) + return m_printer_agent->command_axis_control(dev_id, axis, unit, input_val, speed, is_core_xy, supports_mqtt_axis_control, sequence_id, lan_mode); + return -1; +} + int NetworkAgent::connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) { if (m_printer_agent) diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 317a357135..a82ad3f63c 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -145,6 +145,13 @@ public: int command_ams_refresh_rfid(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode); int command_ams_calibrate(std::string dev_id, int ams_id, int sequence_id, bool lan_mode); int command_ams_select_tray(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode); + int command_xyz_abs(std::string dev_id, int sequence_id, bool lan_mode); + int command_auto_leveling(std::string dev_id, int sequence_id, bool lan_mode); + int command_go_home(std::string dev_id, bool is_printing, bool supports_mqtt_homing, int sequence_id, bool lan_mode); + int command_set_bed(std::string dev_id, int temp, bool supports_mqtt_bed_ctrl, int sequence_id, bool lan_mode); + int command_set_nozzle(std::string dev_id, int temp, int sequence_id, bool lan_mode); + int command_axis_control(std::string dev_id, std::string axis, double unit, double input_val, int speed, + bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode); int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl); int disconnect_printer(); int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag); From 742cb712d85bc24899ef10fb8f2d138974a615da Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Tue, 11 Aug 2026 15:00:34 +0800 Subject: [PATCH 04/15] refactor: abstract bambu specific protocol to printer agent --- src/slic3r/GUI/Jobs/PrintJob.cpp | 64 +++++++++------------------- src/slic3r/GUI/Jobs/SendJob.cpp | 4 +- src/slic3r/GUI/MediaFilePanel.cpp | 4 +- src/slic3r/Utils/BBLPrinterAgent.cpp | 14 +++++- src/slic3r/Utils/BBLPrinterAgent.hpp | 2 + src/slic3r/Utils/IPrinterAgent.hpp | 13 ++++++ src/slic3r/Utils/NetworkAgent.cpp | 14 ++++++ src/slic3r/Utils/NetworkAgent.hpp | 2 + 8 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index d79deb955b..2dd4cbc88d 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -12,9 +12,6 @@ #include "slic3r/GUI/DeviceCore/DevManager.h" #include "slic3r/GUI/DeviceCore/DevUtil.h" -#include "slic3r/Utils/FileTransferUtils.hpp" -#include "slic3r/Utils/BBLNetworkPlugin.hpp" - namespace Slic3r { namespace GUI { @@ -204,45 +201,35 @@ void PrintJob::process(Ctl &ctl) params.dev_ip = m_dev_ip; params.use_ssl_for_ftp = m_local_use_ssl_for_ftp; params.use_ssl_for_mqtt = m_local_use_ssl; - params.username = "bblp"; + params.username = m_agent->default_lan_username(); params.password = m_access_code; + // Allow disabling the eMMC print path via AppConfig. Plugin 02.03.00.62's + // eMMC tunnel code hangs indefinitely at the upload phase with some + // printers (e.g., Bambu H2D), so we default to disabled. Users with + // working eMMC support can opt-in by setting disable_emmc_print = 0. + bool disable_emmc = true; + if (wxGetApp().app_config) { + auto v = wxGetApp().app_config->get("disable_emmc_print"); + if (v == "0" || v == "false") + disable_emmc = false; + } + params.try_emmc_print = this->could_emmc_print && !disable_emmc; // check access code and ip address if (this->connection_type == "lan" && m_print_type == "from_normal") { - bool emmc_ok = false; - bool ftp_ok = false; - if (could_emmc_print) { - std::string devIP = m_dev_ip; - std::string accessCode = m_access_code; - std::string url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; - try { - std::unique_ptr tunnel = std::make_unique(module(), url); - emmc_ok = tunnel->sync_start_connect(); - } catch (const std::exception &e) { - BOOST_LOG_TRIVIAL(warning) << "eMMC tunnel unavailable, falling back to FTP: " << e.what(); - emmc_ok = false; - } - } - { - params.dev_id = m_dev_id; - params.project_name = "verify_job"; - params.filename = job_data._temp_path.string(); - params.connection_type = this->connection_type; + params.dev_id = m_dev_id; + params.project_name = "verify_job"; + params.filename = job_data._temp_path.string(); + params.connection_type = this->connection_type; - result = m_agent->start_send_gcode_to_sdcard(params, nullptr, nullptr, nullptr); + result = m_agent->start_send_gcode_to_sdcard(params, nullptr, nullptr, nullptr); - ftp_ok = result == 0; - } - if (!emmc_ok && !ftp_ok) { - bool legacy_mode = BBLNetworkPlugin::instance().use_legacy_network(); + if (result != 0) { BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed:" - << " emmc_ok=" << emmc_ok - << ", ftp_ok=" << ftp_ok - << ", ftp_result=" << result + << " result=" << result << ", dev_ip=" << m_dev_ip << ", dev_id=" << m_dev_id - << ", password_length=" << m_access_code.size() - << ", legacy_mode=" << (legacy_mode ? "true" : "false"); + << ", password_length=" << m_access_code.size(); m_enter_ip_address_fun_fail(); m_job_finished = true; return; @@ -277,17 +264,6 @@ void PrintJob::process(Ctl &ctl) params.auto_offset_cali = this->auto_offset_cali; params.extruder_cali_manual_mode = this->extruder_cali_manual_mode; params.task_ext_change_assist = this->task_ext_change_assist; - // Allow disabling the eMMC print path via AppConfig. Plugin 02.03.00.62's - // eMMC tunnel code hangs indefinitely at the upload phase with some - // printers (e.g., Bambu H2D), so we default to disabled. Users with - // working eMMC support can opt-in by setting disable_emmc_print = 0. - bool disable_emmc = true; - if (wxGetApp().app_config) { - auto v = wxGetApp().app_config->get("disable_emmc_print"); - if (v == "0" || v == "false") - disable_emmc = false; - } - params.try_emmc_print = this->could_emmc_print && !disable_emmc; if (m_print_type == "from_sdcard_view") { params.dst_file = m_dst_path; diff --git a/src/slic3r/GUI/Jobs/SendJob.cpp b/src/slic3r/GUI/Jobs/SendJob.cpp index bb52367188..c4790cf87d 100644 --- a/src/slic3r/GUI/Jobs/SendJob.cpp +++ b/src/slic3r/GUI/Jobs/SendJob.cpp @@ -124,7 +124,7 @@ void SendJob::process(Ctl &ctl) if (m_is_check_mode) { PrintParams verify_params; verify_params.dev_ip = m_dev_ip; - verify_params.username = "bblp"; + verify_params.username = agent->default_lan_username(); verify_params.password = m_access_code; verify_params.use_ssl_for_ftp = m_local_use_ssl_for_ftp; verify_params.use_ssl_for_mqtt = m_local_use_ssl; @@ -209,7 +209,7 @@ void SendJob::process(Ctl &ctl) // local print access params.dev_ip = m_dev_ip; - params.username = "bblp"; + params.username = agent->default_lan_username(); params.password = m_access_code; params.use_ssl_for_ftp = m_local_use_ssl_for_ftp; params.use_ssl_for_mqtt = m_local_use_ssl; diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 36316f8ff5..a37974fbb4 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -466,8 +466,8 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) m_waiting_support = false; NetworkAgent *agent = wxGetApp().getAgent(); std::string agent_version = agent ? agent->get_version() : ""; - if ((m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { - std::string url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; + if (agent && (m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { + std::string url = agent->get_local_camera_url(m_lan_ip, m_lan_user, m_lan_passwd); url += "&device=" + m_machine; url += "&net_ver=" + agent_version; url += "&dev_ver=" + m_dev_ver; diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 88323e868c..b72ab883bb 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -246,6 +246,11 @@ int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string jso return -1; } +std::string BBLPrinterAgent::get_local_camera_url(std::string dev_ip, std::string username, std::string password) +{ + return "bambu:///local/" + dev_ip + ".?port=6000&user=" + username + "&passwd=" + password; +} + // ============================================================================ // Certificates // ============================================================================ @@ -509,8 +514,15 @@ int BBLPrinterAgent::start_local_print_with_record(PrintParams params, OnUpdateS int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) { - return dispatch_start( + int result = dispatch_start( BBLNetworkPlugin::instance().get_start_send_gcode_to_sdcard(), params, update_fn, cancel_fn, wait_fn); + if (result != 0) { + BOOST_LOG_TRIVIAL(error) << "start_send_gcode_to_sdcard failed: result=" << result + << ", try_emmc_print=" << params.try_emmc_print + << ", legacy_mode=" << BBLNetworkPlugin::instance().use_legacy_network() + << ", dev_ip=" << params.dev_ip << ", dev_id=" << params.dev_id; + } + return result; } int BBLPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index df51c7eb51..37f655c2fe 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -45,6 +45,8 @@ public: 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; + std::string get_local_camera_url(std::string dev_ip, std::string username, std::string password) override; + std::string default_lan_username() const override { return "bblp"; } // Certificates int check_cert() override; diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index fc05a23ba3..0e88e34d4a 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -107,6 +107,19 @@ public: bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode) { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + /** + * Build a ready-to-use local (LAN) camera stream URL for this agent's protocol. + * Returns an empty string if the agent has no local camera stream support. + */ + virtual std::string get_local_camera_url(std::string dev_ip, std::string username, std::string password) + { return {}; } + + /** + * Default LAN account username for this agent's protocol, if it has a fixed one. + * Returns an empty string if the agent has no fixed default (e.g. caller must supply one). + */ + virtual std::string default_lan_username() const { return {}; } + /** * Establish a direct LAN connection to a printer. */ diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index ba719c256a..ce1639541f 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -852,6 +852,20 @@ int NetworkAgent::send_message_to_printer(std::string dev_id, std::string json_s return -1; } +std::string NetworkAgent::get_local_camera_url(std::string dev_ip, std::string username, std::string password) +{ + if (m_printer_agent) + return m_printer_agent->get_local_camera_url(dev_ip, username, password); + return {}; +} + +std::string NetworkAgent::default_lan_username() const +{ + if (m_printer_agent) + return m_printer_agent->default_lan_username(); + return {}; +} + int NetworkAgent::check_cert() { if (m_printer_agent) diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index a82ad3f63c..cc03362f88 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -155,6 +155,8 @@ public: int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl); int disconnect_printer(); int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag); + std::string get_local_camera_url(std::string dev_ip, std::string username, std::string password); + std::string default_lan_username() const; int check_cert(); void install_device_cert(std::string dev_id, bool lan_only); bool start_discovery(bool start, bool sending); From 7cb1805272c5378524afb17a2959ce978b8674c8 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 12 Aug 2026 13:50:47 +0800 Subject: [PATCH 05/15] refactor: push bbl workflows to bbl printer agent --- src/slic3r/GUI/DeviceCore/DevManager.cpp | 2 + src/slic3r/GUI/DeviceManager.cpp | 73 ++++++ src/slic3r/GUI/DeviceManager.hpp | 53 ++-- src/slic3r/GUI/Jobs/PrintJob.cpp | 55 ++-- src/slic3r/GUI/MediaFilePanel.cpp | 35 +-- src/slic3r/GUI/MediaPlayCtrl.cpp | 140 +++++----- src/slic3r/GUI/PartSkipDialog.cpp | 68 ++--- src/slic3r/GUI/PartSkipDialog.hpp | 7 +- src/slic3r/GUI/ReleaseNote.cpp | 5 + src/slic3r/GUI/SendToPrinter.cpp | 174 ++++++++---- src/slic3r/GUI/SendToPrinter.hpp | 10 +- src/slic3r/Utils/BBLCloudServiceAgent.cpp | 39 ++- src/slic3r/Utils/BBLCloudServiceAgent.hpp | 2 +- src/slic3r/Utils/BBLPrinterAgent.cpp | 291 ++++++++++++++++++++- src/slic3r/Utils/BBLPrinterAgent.hpp | 82 +++++- src/slic3r/Utils/FileTransferUtils.cpp | 193 +------------- src/slic3r/Utils/FileTransferUtils.hpp | 108 +------- src/slic3r/Utils/ICloudServiceAgent.hpp | 6 +- src/slic3r/Utils/IPrinterAgent.hpp | 117 ++++++++- src/slic3r/Utils/NetworkAgent.cpp | 25 +- src/slic3r/Utils/NetworkAgent.hpp | 59 ++++- src/slic3r/Utils/OrcaCloudServiceAgent.cpp | 6 +- src/slic3r/Utils/OrcaCloudServiceAgent.hpp | 2 +- 23 files changed, 967 insertions(+), 585 deletions(-) diff --git a/src/slic3r/GUI/DeviceCore/DevManager.cpp b/src/slic3r/GUI/DeviceCore/DevManager.cpp index edc958ec53..8904235faa 100644 --- a/src/slic3r/GUI/DeviceCore/DevManager.cpp +++ b/src/slic3r/GUI/DeviceCore/DevManager.cpp @@ -13,6 +13,8 @@ #include "libslic3r/Time.hpp" +#include "IPrinterAgent.hpp" + using namespace nlohmann; namespace { diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 8a576cfc77..cce7170039 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -50,6 +50,7 @@ #include "DeviceCore/DevStatus.h" #include "DeviceCore/DevUpgrade.h" +#include "IPrinterAgent.hpp" #define CALI_DEBUG #define MINUTE_30 1800000 //ms @@ -2854,6 +2855,78 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ j = j_pre; } +#pragma region CAP_SUBSCRIPTIONS + if (j_pre.contains("capabilities")) { + auto& caps = j_pre["capabilities"]; + // if (caps.contains("extruder_count")) { + // GetExtderSystem()->GetTotalExtderCount(); + // } + if (caps.contains("supports_extruder_control")) + is_enable_np = caps.value("supports_extruder_control", 0); + if (caps.contains("supports_part_skip")) + is_support_partskip = caps.value("supports_part_skip", 0); + if (caps.contains("has_door_sensor")) + is_support_door_open_check = caps.value("has_door_sensor", 0); + if (caps.contains("supports_auto_recovery")) + is_support_auto_recovery_step_loss = caps.value("supports_auto_recovery", 0); + if (caps.contains("supports_prompt_sound")) + is_support_prompt_sound = caps.value("supports_prompt_sound", 0); + if (caps.contains("supports_spaghetti_detection")) + is_support_spaghetti_detection = caps.value("supports_spaghetti_detection", 0); + if (caps.contains("supports_purge_chute_pileup_detection")) + is_support_purgechutepileup_detection = caps.value("supports_purge_chute_pileup_detection", 0); + if (caps.contains("supports_nozzle_clumping_detection")) + is_support_nozzleclumping_detection = caps.value("supports_nozzle_clumping_detection", 0); + if (caps.contains("supports_build_plate_marker_detection")) + is_support_build_plate_marker_detect = caps.value("supports_build_plate_marker_detection", 0); + if (caps.contains("supports_ams_humidity")) + is_support_ams_humidity = caps.value("supports_ams_humidity", 0); + if (caps.contains("supports_pa_calibration_manual")) + is_support_pa_calibration = caps.value("supports_pa_calibration_manual", 0); + if (caps.contains("supports_flow_rate_calibration_manual")) + is_support_flow_calibration = caps.value("supports_flow_rate_calibration_manual", 0); + + if (caps.contains("supports_hotend_rack")) + m_nozzle_system->SetSupportNozzleRack(caps.value("supports_hotend_rack", 0)); + if (caps.contains("has_camera")) + has_ipcam = caps.value("has_camera", 0); + + // Printing with no filament loaded + if (caps.contains("is_support_ams_air_print_detection")) + is_support_air_print_detection = caps.value("is_support_ams_air_print_detection", 0); + + if (caps.contains("is_support_airprinting_detection")) + is_support_airprinting_detection = caps.value("is_support_airprinting_detection", 0); + if (caps.contains("camera_resolutions")) + camera_resolution_supported = caps.value("camera_resolutions", std::vector{}); + // if (caps.contains("ams_unit_count")) + // DevFilaSystemParser::ParseV1_0(caps["ams_unit_count"], this, m_fila_system.get(), true); + + // Remaining documented "capabilities" keys are intentionally not wired here yet. + // + // Already cached elsewhere, but not as a single direct is_support_* bool member + // (a differently-named member, a count, a method on another class, or two competing + // members), so leave these to their existing plumbing: + // extruder_count -> DevExtderSystem::m_total_extder_count + // ams_unit_count -> DevFilaSystem::GetAmsCount() + // + // No existing member at all -- would need a new is_support_* field and a design + // decision on naming/semantics before adding: + // has_lidar + // supports_plate_align_detection + // supports_fod_detection + // supports_displacement_detection + // supports_ai_monitoring -> only an enabled-state member exists (xcam_ai_monitoring) + // supports_first_layer_inspection -> only an enabled-state member exists (xcam_first_layer_inspector) + // + // Genuinely complex (array/object values, or no authoritative single source to derive from): + // supported_nozzle_types, supports_nozzle_blob_detection, has_ams, ams_slot_count, + // supports_ams_rfid, supports_multiple_bed_types, supports_filament_mapping, + // supports_pa_calibration_auto, supports_flow_rate_calibration_auto, + // supports_max_volumetric_speed_calibration, supports_ota_update + } +#pragma endregion + uint64_t t_utc = j.value("t_utc", 0ULL); if (t_utc > 0) { last_utc_time = std::chrono::system_clock::time_point(t_utc * 1ms); diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 33635fbe6e..cdb3b29a6f 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -100,6 +100,32 @@ struct DevPrintTaskRatingInfo; // given nozzle diameter (mm), bucketed per nozzle size to mirror the printer firmware. bool is_stringing_prone_filament(const std::string& filament_id, float nozzle_diameter); +enum LiveviewLocal { + LVL_None, + LVL_Disable, + LVL_Local, + LVL_Rtsps, + LVL_Rtsp +}; + +enum LiveviewRemote { + LVR_None, + LVR_Tutk, + LVR_Agora, + LVR_TutkAgora +}; + +enum FileLocal { + FL_None, + FL_Local +}; + +enum FileRemote { + FR_None, + FR_Tutk, + FR_Agora, + FR_TutkAgora +}; class MachineObject { @@ -538,29 +564,10 @@ public: time_t xcam_first_layer_hold_start = 0; std::string local_rtsp_url; std::string tutk_state; - enum LiveviewLocal { - LVL_None, - LVL_Disable, - LVL_Local, - LVL_Rtsps, - LVL_Rtsp - } liveview_local{ LVL_None }; - enum LiveviewRemote { - LVR_None, - LVR_Tutk, - LVR_Agora, - LVR_TutkAgora - } liveview_remote{ LVR_None }; - enum FileLocal { - FL_None, - FL_Local - } file_local{ FL_None }; - enum FileRemote { - FR_None, - FR_Tutk, - FR_Agora, - FR_TutkAgora - } file_remote{ FR_None }; + LiveviewLocal liveview_local{ LiveviewLocal::LVL_None }; + LiveviewRemote liveview_remote{ LiveviewRemote::LVR_None}; + FileLocal file_local{ FileLocal::FL_None }; + FileRemote file_remote{ FileRemote::FR_None }; enum PlateMakerDectect : int { diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index 2dd4cbc88d..59cc7912ad 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -12,6 +12,8 @@ #include "slic3r/GUI/DeviceCore/DevManager.h" #include "slic3r/GUI/DeviceCore/DevUtil.h" +#include "IPrinterAgent.hpp" + namespace Slic3r { namespace GUI { @@ -203,41 +205,6 @@ void PrintJob::process(Ctl &ctl) params.use_ssl_for_mqtt = m_local_use_ssl; params.username = m_agent->default_lan_username(); params.password = m_access_code; - // Allow disabling the eMMC print path via AppConfig. Plugin 02.03.00.62's - // eMMC tunnel code hangs indefinitely at the upload phase with some - // printers (e.g., Bambu H2D), so we default to disabled. Users with - // working eMMC support can opt-in by setting disable_emmc_print = 0. - bool disable_emmc = true; - if (wxGetApp().app_config) { - auto v = wxGetApp().app_config->get("disable_emmc_print"); - if (v == "0" || v == "false") - disable_emmc = false; - } - params.try_emmc_print = this->could_emmc_print && !disable_emmc; - - // check access code and ip address - if (this->connection_type == "lan" && m_print_type == "from_normal") { - params.dev_id = m_dev_id; - params.project_name = "verify_job"; - params.filename = job_data._temp_path.string(); - params.connection_type = this->connection_type; - - result = m_agent->start_send_gcode_to_sdcard(params, nullptr, nullptr, nullptr); - - if (result != 0) { - BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed:" - << " result=" << result - << ", dev_ip=" << m_dev_ip - << ", dev_id=" << m_dev_id - << ", password_length=" << m_access_code.size(); - m_enter_ip_address_fun_fail(); - m_job_finished = true; - return; - } - - params.project_name = ""; - params.filename = ""; - } params.dev_id = m_dev_id; params.ftp_folder = m_ftp_folder; @@ -264,6 +231,17 @@ void PrintJob::process(Ctl &ctl) params.auto_offset_cali = this->auto_offset_cali; params.extruder_cali_manual_mode = this->extruder_cali_manual_mode; params.task_ext_change_assist = this->task_ext_change_assist; + // Allow disabling the eMMC print path via AppConfig. Plugin 02.03.00.62's + // eMMC tunnel code hangs indefinitely at the upload phase with some + // printers (e.g., Bambu H2D), so we default to disabled. Users with + // working eMMC support can opt-in by setting disable_emmc_print = 0. + bool disable_emmc = true; + if (wxGetApp().app_config) { + auto v = wxGetApp().app_config->get("disable_emmc_print"); + if (v == "0" || v == "false") + disable_emmc = false; + } + params.try_emmc_print = this->could_emmc_print && !disable_emmc; if (m_print_type == "from_sdcard_view") { params.dst_file = m_dst_path; @@ -618,6 +596,13 @@ void PrintJob::process(Ctl &ctl) } if (result < 0) { + if (result == ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED) { + if (m_enter_ip_address_fun_fail) + m_enter_ip_address_fun_fail(); + m_job_finished = true; + return; + } + curr_percent = -1; // The printer is still fetching its encryption flag (a transient state), so ask the diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index a37974fbb4..ee7b7143d3 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -11,6 +11,7 @@ #include "Widgets/ProgressDialog.hpp" #include #include +#include #include "DeviceCore/DevStorage.h" #ifdef __WXMSW__ @@ -206,7 +207,7 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) Bind(wxEVT_SHOW, onShowHide); parent->GetParent()->Bind(wxEVT_SHOW, onShowHide); - m_lan_user = "bblp"; + m_lan_user = wxGetApp().getAgent()->default_lan_username(); } MediaFilePanel::~MediaFilePanel() @@ -465,14 +466,9 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: " << m_local_proto << m_remote_proto; m_waiting_support = false; NetworkAgent *agent = wxGetApp().getAgent(); - std::string agent_version = agent ? agent->get_version() : ""; if (agent && (m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { - std::string url = agent->get_local_camera_url(m_lan_ip, m_lan_user, m_lan_passwd); - url += "&device=" + m_machine; - url += "&net_ver=" + agent_version; - url += "&dev_ver=" + m_dev_ver; - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); + std::string url = agent->get_local_camera_url({m_lan_ip, m_lan_user, m_lan_passwd, LVL_None, + m_machine, agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); fs->SetUrl(url); return; } @@ -494,33 +490,22 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) if (agent) { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, wfs, m = m_machine, v = agent->get_version(), dv = m_dev_ver](std::string url) { - if (boost::algorithm::starts_with(url, "bambu:///")) { - url += "&device=" + m; - url += "&net_ver=" + v; - url += "&dev_ver=" + dv; - url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); - } + [this, wfs, m = m_machine](CameraURLResult result) { + std::string url = std::move(result.url); BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; - if (boost::algorithm::starts_with(url, "bambu:///")) { + if (result.is_success) { fs->SetUrl(url); } else { m_image_grid->SetStatus(m_bmp_failed, _L("Connection Failed. Please check the network and try again")); - std::string res = "3"; - if (boost::ends_with(url, "]")) { - size_t n = url.find_last_of('['); - if (n != std::string::npos) - res = url.substr(n + 1, url.length() - n - 2); - } + std::string res = result.error_code >= 0 ? std::to_string(result.error_code) : "3"; fs->SetUrl(res); } }); - }, wxGetApp().get_printer_cloud_provider()); + }, wxGetApp().get_printer_cloud_provider(), CameraURLParams{"", "", "", LVL_None, m_machine, agent->get_version(), m_dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); } } diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 90e956be69..b255bdac32 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #undef pid_t #include #ifdef __WIN32__ @@ -160,12 +161,12 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) if (DevPrinterConfigUtil::get_printer_series_str(obj->printer_type) == "series_o" && BBLNetworkPlugin::instance().use_legacy_network()) { // Legacy plugin cannot support remote play for H2D, force using local mode - m_remote_proto = MachineObject::LVR_None; + m_remote_proto = LiveviewRemote::LVR_None; } } else { m_camera_exists = false; m_lan_mode = false; - m_lan_proto = MachineObject::LVL_None; + m_lan_proto = LiveviewLocal::LVL_None; m_lan_ip.clear(); m_lan_passwd.clear(); m_dev_ver.clear(); @@ -247,8 +248,8 @@ void refresh_agora_url(char const* device, char const* dev_ver, char const* chan device2 += dev_ver; device2 += "|\"agora\"|"; device2 += channel; - wxGetApp().getAgent()->get_camera_url(device2, [context, callback](std::string url) { - callback(context, url.c_str()); + wxGetApp().getAgent()->get_camera_url(device2, [context, callback](CameraURLResult result) { + callback(context, result.url.c_str()); }, wxGetApp().get_printer_cloud_provider()); } @@ -283,20 +284,20 @@ void MediaPlayCtrl::Play() BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::Play: " << m_lan_proto << m_remote_proto << m_disable_lan; NetworkAgent *agent = wxGetApp().getAgent(); std::string agent_version = agent ? agent->get_version() : ""; - if (m_lan_proto > MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { + if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { m_disable_lan = m_remote_proto && !m_lan_mode; // try remote next time - std::string url; - if (m_lan_proto == MachineObject::LVL_Local) - url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; - else if (m_lan_proto == MachineObject::LVL_Rtsps) - url = "bambu:///rtsps___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsps"; - else if (m_lan_proto == MachineObject::LVL_Rtsp) - url = "bambu:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp"; - url += "&device=" + m_machine; - url += "&net_ver=" + agent_version; - url += "&dev_ver=" + m_dev_ver; - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); + std::string url = agent->get_local_camera_url({ + m_lan_ip, + m_lan_user, + m_lan_passwd, + LiveviewLocal(m_lan_proto), + into_u8(m_machine), + agent_version, + m_dev_ver, + "", + wxGetApp().app_config->get("slicer_uuid"), + SLIC3R_VERSION + }); BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); m_url = url; load(); @@ -312,8 +313,8 @@ void MediaPlayCtrl::Play() // !m_lan_mode && !m_remote_proto && m_lan_proto == LVL_Disable (*) // !m_lan_mode && !m_remote_proto && m_lan_proto == LVL_None (x) - if (m_lan_proto <= MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto)) { - Stop(m_lan_proto == MachineObject::LVL_None + if (m_lan_proto <= LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto)) { + Stop(m_lan_proto == LiveviewLocal::LVL_None ? _L("A problem occurred. Please update the printer firmware and try again.") : _L("LAN Only Liveview is off. Please turn on the liveview on printer screen.")); return; @@ -336,46 +337,39 @@ void MediaPlayCtrl::Play() if (agent) { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, m = m_machine, v = agent_version, dv = m_dev_ver, token = std::weak_ptr(m_token)](std::string url) { - if (token.expired()) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": token has been expired"; - return; - } - - if (boost::algorithm::starts_with(url, "bambu:///")) { - url += "&device=" + into_u8(m); - url += "&net_ver=" + v; - url += "&dev_ver=" + dv; - url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); - } - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, - {"?uid=", "authkey=", "passwd=", "license=", "token="}); - CallAfter([this, m, url] { - if (m != m_machine) { - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for machine: " << m; + agent->get_camera_url( + m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], + [this, m = m_machine, token = std::weak_ptr(m_token)](CameraURLResult result) { + std::string url = std::move(result.url); + const bool success = result.is_success; + const int error_code = result.error_code; + if (token.expired()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": token has been expired"; return; } - if (m_last_state == MEDIASTATE_INITIALIZING) { - if (url.empty() || !boost::algorithm::starts_with(url, "bambu:///")) { - m_failed_code = 3; - if (boost::ends_with(url, "]")) { - size_t n = url.find_last_of('['); - if (n != std::string::npos) - m_failed_code = std::atoi(url.substr(n + 1, url.length() - n - 2).c_str()); - } - Stop(_L("Connection Failed. Please check the network and try again"), from_u8(url)); - } else { - m_url = url; - load(); + + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, {"?uid=", "authkey=", "passwd=", "license=", "token="}); + CallAfter([this, m, url, success, error_code] { + if (m != m_machine) { + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for machine: " << m; + return; } - } else { - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for state: " << m_last_state; - } - }); - }, wxGetApp().get_printer_cloud_provider()); + if (m_last_state == MEDIASTATE_INITIALIZING) { + if (!success) { + m_failed_code = error_code >= 0 ? error_code : 3; + Stop(_L("Connection Failed. Please check the network and try again"), from_u8(url)); + } else { + m_url = url; + load(); + } + } else { + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for state: " << m_last_state; + } + }); + }, + wxGetApp().get_printer_cloud_provider(), + CameraURLParams{"", "", "", LVL_None, into_u8(m_machine), agent_version, m_dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); } } @@ -528,16 +522,11 @@ void MediaPlayCtrl::ToggleStream() wxGetApp().app_config->set("not_show_vcamera_stop_prev", "1"); if (res == wxID_CANCEL) return; } - if (m_lan_proto > MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { - std::string url; - if (m_lan_proto == MachineObject::LVL_Local) - url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; - else if (m_lan_proto == MachineObject::LVL_Rtsps) - url = "bambu:///rtsps___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsps"; - else if (m_lan_proto == MachineObject::LVL_Rtsp) - url = "bambu:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp"; - url += "&device=" + into_u8(m_machine); - url += "&dev_ver=" + m_dev_ver; + if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent) return; + std::string url = agent->get_local_camera_url({m_lan_ip, m_lan_user, m_lan_passwd, LiveviewLocal(m_lan_proto), + into_u8(m_machine), agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); std::string file_url = data_dir() + "/cameratools/url.txt"; boost::nowide::ofstream file(file_url); @@ -551,20 +540,14 @@ void MediaPlayCtrl::ToggleStream() if (!agent) return; std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, m = m_machine, v = agent->get_version(), dv = m_dev_ver](std::string url) { - if (boost::algorithm::starts_with(url, "bambu:///")) { - url += "&device=" + m; - url += "&net_ver=" + v; - url += "&dev_ver=" + dv; - url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); - } + [this, m = m_machine](CameraURLResult result) { + std::string url = std::move(result.url); + const bool success = result.is_success; BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(url, {"?uid=", "authkey=", "passwd=", "license=", "token="}); - CallAfter([this, m, url] { + CallAfter([this, m, url, success] { if (m != m_machine) return; - if (url.empty() || !boost::algorithm::starts_with(url, "bambu:///")) { + if (!success) { MessageDialog(this->GetParent(), wxString::Format(_L("Virtual camera initialize failed (%s)!"), url.empty() ? _L("Network unreachable") : from_u8(url)), _L("Information"), wxICON_INFORMATION) .ShowModal(); @@ -577,7 +560,8 @@ void MediaPlayCtrl::ToggleStream() file.close(); m_streaming = true; }); - }, wxGetApp().get_printer_cloud_provider()); + }, wxGetApp().get_printer_cloud_provider(), CameraURLParams{"", "", "", LVL_None, into_u8(m_machine), agent->get_version(), m_dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); } void MediaPlayCtrl::msw_rescale() { diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index b9dd7d5007..54c66fb80e 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -433,58 +433,38 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) } std::string dev_ver = obj->get_ota_version(); std::string dev_id = obj->get_dev_id(); - // int remote_proto = obj->get_file_remote(); - NetworkAgent *agent = wxGetApp().getAgent(); - std::string agent_version = agent ? agent->get_version() : ""; + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent) { + fs->SetUrl("3"); + return; + } auto url_state = m_url_state; if (obj->is_lan_mode_printer()) { url_state = URL_TCP; } - if (agent) { - switch (url_state) { - case URL_TCP: { - std::string devIP = obj->get_dev_ip(); - std::string accessCode = obj->get_access_code(); - std::string tcp_url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; - CallAfter([=] { + FileTransferURLParams params; + params.url_state = url_state; + params.ip_address = obj->get_dev_ip(); + params.username = agent->default_lan_username(); + params.password = obj->get_access_code(); + params.device_id = dev_id; + params.network_version = agent->get_version(); + params.device_version = dev_ver; + params.refresh_url = boost::lexical_cast(&refresh_agora_url); + params.client_id = wxGetApp().app_config->get("slicer_uuid"); + params.client_version = SLIC3R_VERSION; + + agent->get_file_transfer_url( + dev_id, + [this, wfs](FileTransferURLResult result) { + CallAfter([wfs, result = std::move(result)]() mutable { boost::shared_ptr fs(wfs.lock()); if (!fs) return; - if (boost::algorithm::starts_with(tcp_url, "bambu:///")) { - fs->SetUrl(tcp_url); - } else { - fs->SetUrl("3"); - } + fs->SetUrl(result.is_success ? result.url : "3"); }); - break; - } - case URL_TUTK: { - std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[3], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) - { - if (boost::algorithm::starts_with(url, "bambu:///")) { - url += "&device=" + m; - url += "&net_ver=" + v; - url += "&dev_ver=" + dv; - url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); - } - CallAfter([=] { - boost::shared_ptr fs(wfs.lock()); - if (!fs) return; - if (boost::algorithm::starts_with(url, "bambu:///")) { - fs->SetUrl(url); - } else { - fs->SetUrl("3"); - } - }); - }); - break; - } - default: break; - } - } + }, + std::move(params)); } // controller void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index 95eba9f24d..d8c7282710 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -29,11 +29,6 @@ namespace Slic3r { namespace GUI { class SkipPartCanvas; -enum URL_STATE { - URL_TCP, - URL_TUTK, -}; - class PartSkipConfirmDialog : public DPIDialog { private: @@ -160,4 +155,4 @@ private: void OnApplyDialog(wxCommandEvent &event); }; -}} // namespace Slic3r::GUI \ No newline at end of file +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/ReleaseNote.cpp b/src/slic3r/GUI/ReleaseNote.cpp index 7b2d091176..ef5f0f5bee 100644 --- a/src/slic3r/GUI/ReleaseNote.cpp +++ b/src/slic3r/GUI/ReleaseNote.cpp @@ -1801,6 +1801,8 @@ void InputIpAddressDialog::on_ok(wxMouseEvent& evt) m_trouble_shoot->Hide(); std::string str_ip = m_input_ip->GetTextCtrl()->GetValue().ToStdString(); std::string str_access_code = m_input_access_code->GetTextCtrl()->GetValue().ToStdString(); + if (str_access_code.empty()) + str_access_code = "88888888"; std::string str_name = m_input_printer_name->GetTextCtrl()->GetValue().Strip(wxString::both).ToStdString(); // Serial number should not contain lower case letters, and bambu_network plugin crashes // if user entered the wrong serial number, so we call `Upper()` here. @@ -1835,6 +1837,8 @@ void InputIpAddressDialog::on_send_retry() Fit(); wxString ip = m_input_ip->GetTextCtrl()->GetValue(); wxString str_access_code = m_input_access_code->GetTextCtrl()->GetValue(); + if (str_access_code.empty()) + str_access_code = "88888888"; // check support function if (!m_obj) return; @@ -2058,6 +2062,7 @@ void InputIpAddressDialog::on_text(wxCommandEvent &evt) if (str_access_code.empty()) { str_access_code = "88888888"; + m_input_access_code->GetTextCtrl()->SetValue(str_access_code); } auto str_name = m_input_printer_name->GetTextCtrl()->GetValue().Strip(wxString::both); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index 17da4b66a0..acbf911660 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -1,6 +1,7 @@ #include "SendToPrinter.hpp" #include "I18N.hpp" +#include "IPrinterAgent.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Thread.hpp" #include "GUI.hpp" @@ -25,7 +26,6 @@ #include "DeviceCore/DevManager.h" #include "DeviceCore/DevStorage.h" -#include "slic3r/Utils/FileTransferUtils.hpp" namespace Slic3r { @@ -1715,64 +1715,93 @@ void SendToPrinterDialog::GetConnection() if (m_tcp_try_connect) { std::string devIP = obj->get_dev_ip(); std::string accessCode = obj->get_access_code(); - std::string url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tcp"; - m_filetransfer_tunnel = std::make_unique(module(), url); - m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { - CallAfter([this, is_success, err_code, error_msg]() { - OnConnection(is_success, err_code, error_msg); - }); - }); - m_filetransfer_tunnel->start_connect(); + + if (agent->get_printer_agent()) { + try { + m_filetransfer_tunnel = agent->get_printer_agent()->create_file_transfer_tunnel(devIP, accessCode); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create TCP file-transfer tunnel: " << e.what(); + } + + if (m_filetransfer_tunnel && m_filetransfer_tunnel->check_valid()) { + m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { + CallAfter([this, is_success, err_code, error_msg]() { + OnConnection(is_success, err_code, error_msg); + }); + }); + m_filetransfer_tunnel->start_connect(); + } else { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + } + } else { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + } } else if (m_tutk_try_connect) { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], [this, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { - if (boost::algorithm::starts_with(url, "bambu:///")) { - url += "&device=" + m; - url += "&net_ver=" + v; - url += "&dev_ver=" + dv; - url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); - } - - if (m_url_timer && m_url_timer->IsRunning()) - { - m_url_timer->Stop(); - } - - #if !BBL_RELEASE_TO_PUBLIC - BOOST_LOG_TRIVIAL(info) << "SendToPrinter::camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); - #endif - - - if (boost::algorithm::starts_with(url, "bambu:///")) - { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tutk"; - m_filetransfer_tunnel = std::make_unique(module(), url); - m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { - CallAfter([this, is_success, err_code, error_msg]() { OnConnection(is_success, err_code, error_msg); }); - }); - m_filetransfer_tunnel->start_connect(); - } - else - { - std::string res = ""; - if (!url.empty() && boost::ends_with(url, "]")) - { - size_t n = url.find_last_of('['); - if (n != std::string::npos) - res = url.substr(n + 1, url.length() - n - 2); + agent->get_camera_url( + obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], + [this, pa = agent->get_printer_agent()](CameraURLResult result) { + std::string url = std::move(result.url); + if (m_url_timer && m_url_timer->IsRunning()) { + m_url_timer->Stop(); } - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : Tutk url error: ress = " << res; - } - }); + +#if !BBL_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "SendToPrinter::camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); +#endif + + if (result.is_success) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tutk"; + if (!pa) { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + return; + } + + try { + m_filetransfer_tunnel = pa->create_file_transfer_tunnel_from_url(url); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create TUTK file-transfer tunnel: " << e.what(); + } + if (!m_filetransfer_tunnel || !m_filetransfer_tunnel->check_valid()) { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + return; + } + + m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { + CallAfter([this, is_success, err_code, error_msg]() { OnConnection(is_success, err_code, error_msg); }); + }); + m_filetransfer_tunnel->start_connect(); + } else { + std::string res = result.error_code >= 0 ? std::to_string(result.error_code) : ""; + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : Tutk url error: ress = " << res; + show_file_transfer_error(PrintDialogStatus::PrintStatusPublicInitFailed, + _L("Connection failed. Please check your network and try again.")); + } + }, + wxGetApp().get_printer_cloud_provider(), + CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); } } } +void SendToPrinterDialog::show_file_transfer_error(PrintDialogStatus status, wxString message) +{ + if (m_url_timer && m_url_timer->IsRunning()) + m_url_timer->Stop(); + m_connection_status = ConnectionStatus::CONNECTION_FAILED; + GetConnection(); + show_status(status); + update_print_status_msg(message, false, true); +} + void SendToPrinterDialog::OnConnection(bool is_success, int error_code, std::string error_msg) { if (is_success) { @@ -1854,8 +1883,24 @@ void SendToPrinterDialog::ResetTunnelAndJob() void SendToPrinterDialog::CreateMediaAbilityJob() { + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent || !agent->get_printer_agent()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no printer agent available"; + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + return; + } nlohmann::json media_ability = {{"cmd_type", 7}}; - m_filetransfer_mediability_job = std::make_unique(module(), std::string(media_ability.dump())); + try { + m_filetransfer_mediability_job = agent->get_printer_agent()->create_file_transfer_job(std::string(media_ability.dump())); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create media-ability job: " << e.what(); + } + if (!m_filetransfer_mediability_job || !m_filetransfer_mediability_job->check_valid()) { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + return; + } m_filetransfer_mediability_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { //this pl CallAfter([this, res, resp_ec, json_res] { @@ -1897,10 +1942,11 @@ void SendToPrinterDialog::CreateMediaAbilityJob() }); }); // Guard against a null transfer tunnel before dereferencing. - if (m_filetransfer_tunnel) { + if (m_filetransfer_tunnel && m_filetransfer_tunnel->check_valid()) { m_filetransfer_mediability_job->start_on(*m_filetransfer_tunnel); } else { - BOOST_LOG_TRIVIAL(info) << "CreateMediaAbilityJob: file transfer tunnel is null"; + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); } } @@ -1913,8 +1959,25 @@ void SendToPrinterDialog::CreateUploadFileJob(const std::string &path, const std upload_params["dest_name"] = name; // filenme no path upload_params["file_path"] = path; + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent || !agent->get_printer_agent()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no printer agent available"; + show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, + _L("The selected printer does not support file transfer.")); + return; + } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Begin CreateUploadFileJob"; - m_filetransfer_uploadfile_job = std::make_unique(module(), std::string(upload_params.dump())); + try { + m_filetransfer_uploadfile_job = agent->get_printer_agent()->create_file_transfer_job(std::string(upload_params.dump())); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create upload job: " << e.what(); + } + if (!m_filetransfer_uploadfile_job || !m_filetransfer_uploadfile_job->check_valid()) { + show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, + _L("The selected printer does not support file transfer.")); + return; + } m_filetransfer_uploadfile_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { // CallAfter([this, res, resp_ec, json_res, bin_res] { UploadFileRessultCallback(res, resp_ec,json_res, bin_res); @@ -1942,10 +2005,11 @@ void SendToPrinterDialog::CreateUploadFileJob(const std::string &path, const std }); }); // Guard against a null transfer tunnel before dereferencing. - if (m_filetransfer_tunnel) { + if (m_filetransfer_tunnel && m_filetransfer_tunnel->check_valid()) { m_filetransfer_uploadfile_job->start_on(*m_filetransfer_tunnel); } else { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": file transfer tunnel is null"; + show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, + _L("The selected printer does not support file transfer.")); } } diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp index 14493a1f20..717910ae61 100644 --- a/src/slic3r/GUI/SendToPrinter.hpp +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -24,6 +24,7 @@ #include #include +#include "IPrinterAgent.hpp" #include "SelectMachine.hpp" #include "GUI_Utils.hpp" #include "wxExtensions.hpp" @@ -43,8 +44,6 @@ namespace Slic3r { -class FileTransferTunnel; -class FileTransferJob; namespace GUI { @@ -171,9 +170,9 @@ private: enum ConnectionStatus { NOT_START, CONNECTING, CONNECTED, CONNECTION_FAILED, DISCONNECTED }; ConnectionStatus m_connection_status{ConnectionStatus::NOT_START}; - std::unique_ptr m_filetransfer_tunnel; - std::unique_ptr m_filetransfer_mediability_job; - std::unique_ptr m_filetransfer_uploadfile_job; + std::unique_ptr m_filetransfer_tunnel; + std::unique_ptr m_filetransfer_mediability_job; + std::unique_ptr m_filetransfer_uploadfile_job; wxDateTime m_last_refresh_time; public: @@ -223,6 +222,7 @@ public: private: void ResetConnectMethod(); void ResetTunnelAndJob(); + void show_file_transfer_error(PrintDialogStatus status, wxString message); void OnConnection(bool is_success, int error_code, std::string error_msg); void CreateMediaAbilityJob(); void CreateUploadFileJob(const std::string &path, const std::string &name); diff --git a/src/slic3r/Utils/BBLCloudServiceAgent.cpp b/src/slic3r/Utils/BBLCloudServiceAgent.cpp index 846e4ce509..df0164c52e 100644 --- a/src/slic3r/Utils/BBLCloudServiceAgent.cpp +++ b/src/slic3r/Utils/BBLCloudServiceAgent.cpp @@ -1,5 +1,6 @@ #include "BBLCloudServiceAgent.hpp" #include "BBLNetworkPlugin.hpp" +#include "NetworkAgent.hpp" #include #include "Http.hpp" @@ -606,13 +607,47 @@ int BBLCloudServiceAgent::modify_printer_name(std::string dev_id, std::string de // Model Mall & Publishing // ============================================================================ -int BBLCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback) +int BBLCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) { 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); + auto make_result = [](std::string url) { + CameraURLResult result; + result.url = std::move(url); + result.is_success = result.url.rfind("bambu:///", 0) == 0; + if (result.is_success) { + result.error_code = 0; + } else if (!result.url.empty() && result.url.back() == ']') { + const auto start = result.url.rfind('['); + if (start != std::string::npos && start + 1 < result.url.size() - 1) { + try { + result.error_code = std::stoi(result.url.substr(start + 1, result.url.size() - start - 2)); + } catch (...) { + } + } + } + return result; + }; + if (params.apply_meta) { + auto decorated_callback = [callback = std::move(callback), params = std::move(params), make_result](std::string url) { + CameraURLResult result = make_result(std::move(url)); + if (result.is_success) { + result.url += "&device=" + params.device; + result.url += "&net_ver=" + params.network_version; + result.url += "&dev_ver=" + params.device_version; + result.url += "&refresh_url=" + params.refresh_url; + result.url += "&cli_id=" + params.client_id; + result.url += "&cli_ver=" + params.client_version; + } + callback(std::move(result)); + }; + return func(agent, std::move(dev_id), std::move(decorated_callback)); + } + return func(agent, std::move(dev_id), [callback = std::move(callback), make_result](std::string url) { + callback(make_result(std::move(url))); + }); } return -1; } diff --git a/src/slic3r/Utils/BBLCloudServiceAgent.hpp b/src/slic3r/Utils/BBLCloudServiceAgent.hpp index 9f03f0b839..59de3e41d3 100644 --- a/src/slic3r/Utils/BBLCloudServiceAgent.hpp +++ b/src/slic3r/Utils/BBLCloudServiceAgent.hpp @@ -89,7 +89,7 @@ public: 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 callback) override; + int get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) override; int get_design_staffpick(int offset, int limit, std::function 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; diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index b72ab883bb..591750005d 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -1,14 +1,208 @@ #include "BBLPrinterAgent.hpp" #include "BBLNetworkPlugin.hpp" +#include "FileTransferUtils.hpp" +#include "IPrinterAgent.hpp" #include "NetworkAgentFactory.hpp" +#include "NetworkAgent.hpp" #include #include +#include #include #include +#include +#include namespace Slic3r { +// ============================================================================ +// File Transfer (Bambu eMMC tunnel ABI) +// ============================================================================ + +BBLFileTransferTunnel::BBLFileTransferTunnel(const std::string &url) : IFileTransferTunnel(url) +{ + FileTransferModule &m = module(); + m_ = &m; + // Guard against missing symbols in older Bambu networking plugins. + // These symbols were added in a newer plugin ABI; if the installed + // plugin predates them, ft_tunnel_create/ft_tunnel_set_status_cb + // will be null and calling them crashes. + if (!m_->ft_tunnel_create || !m_->ft_tunnel_set_status_cb) { + throw std::runtime_error("Bambu networking plugin is too old: missing ft_tunnel_* symbols. " + "Please update the networking plugin."); + } + FT_TunnelHandle *h{}; + if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { + throw std::runtime_error("ft_tunnel_create failed"); + } + h_ = h; + + // C API: ft_status_cb(void* user, int old_status, int new_status, int err, const char* msg) + auto tramp = [](void *user, int old_status, int new_status, int err_code, const char *msg) noexcept { + auto *self = reinterpret_cast(user); + self->status_ = new_status; + if (!self->status_cb_) return; + try { + self->status_cb_(old_status, new_status, err_code, std::string(msg ? msg : "")); + } catch (...) {} + }; + if (m_->ft_tunnel_set_status_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_set_status_cb failed"); } +} + +void BBLFileTransferTunnel::start_connect() +{ + // C API: ft_conn_cb(void* user, int ok, int err, const char* msg) + auto tramp = [](void *user, int ok, int ec, const char *msg) noexcept { + auto *pcb = reinterpret_cast(user); + if (!pcb) return; + try { + (*pcb)(ok == 0, ec, std::string(msg ? msg : "")); + } catch (...) {} + }; + if (m_->ft_tunnel_start_connect(h_, tramp, &conn_cb_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_connect failed"); } +} + +bool BBLFileTransferTunnel::sync_start_connect() +{ + return m_->ft_tunnel_sync_connect(h_) == FT_OK; +} + +void BBLFileTransferTunnel::shutdown() +{ + if (m_->ft_tunnel_shutdown) (void) m_->ft_tunnel_shutdown(h_); +} + +BBLFileTransferJob::BBLFileTransferJob(const std::string ¶ms_json) : IFileTransferJob(params_json) +{ + m_ = &module(); + FT_JobHandle *h{}; + if (m_->ft_job_create(params_json.c_str(), &h) != 0 || !h) { + throw std::runtime_error("ft_job_create failed"); + } + h_ = h; + + // C API: ft_job_result_cb(void* user, int tunnel_err, ft_job_result result) + auto tramp = [](void *user, ft_job_result r) noexcept { + auto *self = reinterpret_cast(user); + if (!self) return; + + try { + self->finished_ = true; + self->solve_result(r); + if (self->result_cb_) self->result_cb_(self->res_, self->resp_ec_, self->res_json_, self->res_bin_); + } catch (...) { + // swallow + } + + try { + if (auto *mod = self ? self->m_ : nullptr) { + if (mod->ft_job_result_destroy) + mod->ft_job_result_destroy(&r); + else if (mod->ft_free) { + if (r.json) mod->ft_free((void *) r.json); + if (r.bin) mod->ft_free((void *) r.bin); + } + } + } catch (...) {} + }; + + if (m_->ft_job_set_result_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_result_cb failed"); } +} + +bool BBLFileTransferJob::get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) +{ + if (!h_) throw std::runtime_error("job handle invalid"); + ft_job_result result; + if (m_->ft_job_get_result(h_, timeout_ms, &result) == ft_err::FT_EXCEPTION) return false; + solve_result(result); + m_->ft_job_result_destroy(&result); + ec = res_; + resp_ec = res_; + json = res_json_; + bin = res_bin_; + return true; +} + +void BBLFileTransferJob::start_on(IFileTransferTunnel &t) +{ + if (!h_) throw std::runtime_error("job handle invalid"); + auto *handle = reinterpret_cast(t.native()); + if (m_->ft_tunnel_start_job(handle, h_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_job failed"); } +} + +void BBLFileTransferJob::on_msg(MsgCb cb) +{ + IFileTransferJob::on_msg(std::move(cb)); + if (!h_) return; + + // C API: ft_job_msg_cb(void* user, ft_job_msg msg) + auto tramp = [](void *user, ft_job_msg m) noexcept { + auto *self = reinterpret_cast(user); + if (!self) return; + try { + if (self->msg_cb_) { self->msg_cb_(m.kind, std::string(m.json ? m.json : "")); } + } catch (...) {} + + try { + if (auto *mod = self->m_) { + if (mod->ft_job_msg_destroy) + mod->ft_job_msg_destroy(&m); + else if (mod->ft_free && m.json) + mod->ft_free((void *) m.json); + } + } catch (...) {} + }; + + if (m_->ft_job_set_msg_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_msg_cb failed"); } +} + +bool BBLFileTransferJob::try_get_msg(int &kind, std::string &json) +{ + if (!h_) return false; + ft_job_msg m{}; + int rc = m_->ft_job_try_get_msg(h_, &m); + if (rc != 0) return false; + + kind = m.kind; + json.assign(m.json ? m.json : ""); + + if (m_->ft_job_msg_destroy) + m_->ft_job_msg_destroy(&m); + else if (m_->ft_free && m.json) + m_->ft_free((void *) m.json); + + return true; +} + +bool BBLFileTransferJob::get_msg(uint32_t timeout_ms, int &kind, std::string &json) +{ + if (!h_) return false; + ft_job_msg m{}; + int rc = m_->ft_job_get_msg(h_, timeout_ms, &m); + if (rc != 0) return false; + + kind = m.kind; + json.assign(m.json ? m.json : ""); + + if (m_->ft_job_msg_destroy) + m_->ft_job_msg_destroy(&m); + else if (m_->ft_free && m.json) + m_->ft_free((void *) m.json); + + return true; +} + +void BBLFileTransferJob::solve_result(ft_job_result result) +{ + res_ = result.ec; + resp_ec_ = result.resp_ec; + + res_bin_.clear(); + if (result.bin && result.bin_size) res_bin_.assign(reinterpret_cast(result.bin), + reinterpret_cast(result.bin) + result.bin_size); + res_json_.assign(result.json ? result.json : ""); +} + BBLPrinterAgent::BBLPrinterAgent() = default; BBLPrinterAgent::~BBLPrinterAgent() = default; @@ -246,9 +440,70 @@ int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string jso return -1; } -std::string BBLPrinterAgent::get_local_camera_url(std::string dev_ip, std::string username, std::string password) +std::string BBLPrinterAgent::get_local_camera_url(CameraURLParams params) { - return "bambu:///local/" + dev_ip + ".?port=6000&user=" + username + "&passwd=" + password; + std::string url; + if (params.protocol == LVL_Local) + url = "bambu:///local/" + params.ip_address + ".?port=6000&user=" + params.user + "&passwd=" + params.password; + else if (params.protocol == LVL_Rtsps) + url = "bambu:///rtsps___" + params.user + ":" + params.password + "@" + params.ip_address + "/streaming/live/1?proto=rtsps"; + else if (params.protocol == LVL_Rtsp) + url = "bambu:///rtsp___" + params.user + ":" + params.password + "@" + params.ip_address + "/streaming/live/1?proto=rtsp"; + else + url = "bambu:///local/" + params.ip_address + ".?port=6000&user=" + params.user + "&passwd=" + params.password; + + url += "&device=" + params.device; + url += "&net_ver=" + params.network_version; + url += "&dev_ver=" + params.device_version; + url += "&cli_id=" + params.client_id; + url += "&cli_ver=" + params.client_version; + return url; +} + +std::string BBLPrinterAgent::get_local_file_transfer_url(const FileTransferURLParams& params) +{ + // Keep the historical PartSkipDialog URL unchanged. It is a file-transfer + // tunnel URL, not a camera URL, so it intentionally has no camera metadata + // suffix and no dot before the query string. + return "bambu:///local/" + params.ip_address + "?port=6000&user=" + params.username + "&passwd=" + params.password; +} + +int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::function callback, + FileTransferURLParams params) +{ + if (params.url_state == URL_TCP) { + FileTransferURLResult result; + result.url = get_local_file_transfer_url(params); + result.is_success = !result.url.empty(); + result.error_code = result.is_success ? 0 : -1; + if (callback) + callback(std::move(result)); + return result.is_success ? 0 : -1; + } + + if (!m_cloud_agent) { + if (callback) + callback({}); + return -1; + } + + const std::string protocols = "\"tutk\",\"agora\""; + return m_cloud_agent->get_camera_url( + std::move(dev_id) + "|" + params.device_version + "|" + protocols, + std::move(callback), + CameraURLParams{ + "", + "", + "", + LVL_None, + params.device_id, + params.network_version, + params.device_version, + params.refresh_url, + params.client_id, + params.client_version, + true + }); } // ============================================================================ @@ -512,6 +767,15 @@ int BBLPrinterAgent::start_local_print_with_record(PrintParams params, OnUpdateS BBLNetworkPlugin::instance().get_start_local_print_with_record(), params, update_fn, cancel_fn, wait_fn); } +int BBLPrinterAgent::verify_local_print_access(PrintParams params) +{ + params.project_name = "verify_job"; + params.filename = Slic3r::resources_dir() + "/check_access_code.txt"; + params.try_emmc_print = false; + + return start_send_gcode_to_sdcard(params, nullptr, nullptr, nullptr); +} + int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) { int result = dispatch_start( @@ -527,6 +791,16 @@ int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStat int BBLPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { + if (params.connection_type == "lan" && params.print_type == "from_normal") { + const int verify_result = verify_local_print_access(params); + if (verify_result != 0) { + BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed: result=" << verify_result + << ", dev_ip=" << params.dev_ip << ", dev_id=" << params.dev_id + << ", password_length=" << params.password.size(); + return ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED; + } + } + return dispatch_start( BBLNetworkPlugin::instance().get_start_local_print(), params, update_fn, cancel_fn); } @@ -639,4 +913,17 @@ FilamentSyncMode BBLPrinterAgent::get_filament_sync_mode() const return FilamentSyncMode::subscription; } +std::unique_ptr BBLPrinterAgent::create_file_transfer_tunnel(std::string& dev_ip, std::string& access_code) { + std::string url = "bambu:///local/" + dev_ip + "?port=6000&user=" + default_lan_username() + "&passwd=" + access_code; + return std::make_unique(url); +} + +std::unique_ptr BBLPrinterAgent::create_file_transfer_tunnel_from_url(std::string url) { + return std::make_unique(url); +} + +std::unique_ptr BBLPrinterAgent::create_file_transfer_job(std::string params_json) { + return std::make_unique(params_json); +} + } // namespace Slic3r diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index 37f655c2fe..8d767faa1c 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -3,12 +3,83 @@ #include "IPrinterAgent.hpp" #include "ICloudServiceAgent.hpp" +#include "FileTransferUtils.hpp" #include #include #include namespace Slic3r { +/** + * BBLFileTransferTunnel - Bambu eMMC tunnel, backed by the Bambu network + * plugin's ft_tunnel_* ABI (see FileTransferUtils.hpp). Only BBLPrinterAgent + * constructs these; callers only ever see them through IFileTransferTunnel. + */ +class BBLFileTransferTunnel : public IFileTransferTunnel +{ +public: + BBLFileTransferTunnel(const std::string &url); + ~BBLFileTransferTunnel() override { reset(); } + + void start_connect() override; + bool sync_start_connect() override; + void shutdown() override; + bool check_valid() const override { return h_ != nullptr; } + void *native() const noexcept override { return h_; } + +private: + void reset() noexcept + { + if (h_) { + m_->ft_tunnel_release(h_); + h_ = nullptr; + } + } + + FileTransferModule *m_{}; + FT_TunnelHandle *h_{}; +}; + +/** + * BBLFileTransferJob - a single ft_job_* operation (media-ability query, + * file upload, ...) run on a BBLFileTransferTunnel. Same ABI-wrapper role + * as BBLFileTransferTunnel; only BBLPrinterAgent constructs these. + */ +class BBLFileTransferJob : public IFileTransferJob +{ +public: + explicit BBLFileTransferJob(const std::string ¶ms_json); + ~BBLFileTransferJob() override { reset(); } + + bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) override; + void start_on(IFileTransferTunnel &t) override; + // why: unlike on_result() (fires from a trampoline registered once in the ctor), + // ft_job_set_msg_cb is only wired up here, lazily, on first real subscriber - + // that ABI call has to happen in the concrete class, not the vendor-neutral base. + void on_msg(MsgCb cb) override; + bool try_get_msg(int &kind, std::string &json) override; + bool get_msg(uint32_t timeout_ms, int &kind, std::string &json) override; + void *native() const noexcept override { return h_; } + bool check_valid() const override { return h_ != nullptr; } + void cancel() override + { + if (m_->ft_job_cancel && h_) m_->ft_job_cancel(h_); + } + +private: + void reset() noexcept + { + if (h_) { + m_->ft_job_release(h_); + h_ = nullptr; + } + } + void solve_result(ft_job_result result); + + FileTransferModule *m_{}; + FT_JobHandle *h_{}; +}; + /** * BBLPrinterAgent - BBL DLL wrapper implementation of IPrinterAgent. * @@ -45,7 +116,10 @@ public: 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; - std::string get_local_camera_url(std::string dev_ip, std::string username, std::string password) override; + std::string get_local_camera_url(CameraURLParams params) override; + std::string get_local_file_transfer_url(const FileTransferURLParams& params) override; + int get_file_transfer_url(std::string dev_id, std::function callback, + FileTransferURLParams params) override; std::string default_lan_username() const override { return "bblp"; } // Certificates @@ -100,7 +174,13 @@ public: int set_queue_on_main_fn(QueueOnMainFn fn) override; FilamentSyncMode get_filament_sync_mode() const override; + std::unique_ptr create_file_transfer_tunnel(std::string& dev_ip, std::string& access_code) override; + std::unique_ptr create_file_transfer_tunnel_from_url(std::string url) override; + std::unique_ptr create_file_transfer_job(std::string params_json) override; + private: + int verify_local_print_access(PrintParams params); + // why: the lan/cloud DECISION stays machine-side; keep this mechanical branch in sync with publish_json. int publish(const std::string& dev_id, const nlohmann::json& j, bool lan_mode); diff --git a/src/slic3r/Utils/FileTransferUtils.cpp b/src/slic3r/Utils/FileTransferUtils.cpp index ebca741af5..8601f6712e 100644 --- a/src/slic3r/Utils/FileTransferUtils.cpp +++ b/src/slic3r/Utils/FileTransferUtils.cpp @@ -1,8 +1,4 @@ -#include -#include #include "FileTransferUtils.hpp" -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/DeviceCore/DevManager.h" namespace Slic3r { @@ -37,191 +33,4 @@ FileTransferModule::FileTransferModule(ModuleHandle networking_module, int requi ft_job_get_msg = sym_lookup(networking_, "ft_job_get_msg"); } -FileTransferTunnel::FileTransferTunnel(FileTransferModule &m, const std::string &url) : m_(&m) -{ - // Guard against missing symbols in older Bambu networking plugins. - // These symbols were added in a newer plugin ABI; if the installed - // plugin predates them, ft_tunnel_create/ft_tunnel_set_status_cb - // will be null and calling them crashes. - if (!m_->ft_tunnel_create || !m_->ft_tunnel_set_status_cb) { - throw std::runtime_error("Bambu networking plugin is too old: missing ft_tunnel_* symbols. " - "Please update the networking plugin."); - } - FT_TunnelHandle *h{}; - if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { - throw std::runtime_error("ft_tunnel_create failed"); - } - h_ = h; - - // C API: ft_status_cb(void* user, int old_status, int new_status, int err, const char* msg) - auto tramp = [](void *user, int old_status, int new_status, int err_code, const char *msg) noexcept { - auto *self = reinterpret_cast(user); - self->status_ = new_status; - if (!self->status_cb_) return; - try { - self->status_cb_(old_status, new_status, err_code, std::string(msg ? msg : "")); - } catch (...) {} - }; - if (m_->ft_tunnel_set_status_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_set_status_cb failed"); } -} - -void FileTransferTunnel::start_connect() -{ - // C API: ft_conn_cb(void* user, int ok, int err, const char* msg) - auto tramp = [](void *user, int ok, int ec, const char *msg) noexcept { - auto *pcb = reinterpret_cast(user); - if (!pcb) return; - try { - (*pcb)(ok == 0, ec, std::string(msg ? msg : "")); - } catch (...) {} - }; - if (m_->ft_tunnel_start_connect(h_, tramp, &conn_cb_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_connect failed"); } -} - -bool FileTransferTunnel::sync_start_connect() -{ - return m_->ft_tunnel_sync_connect(h_) == FT_OK; -} - -void FileTransferTunnel::on_connection(ConnectionCb cb) { conn_cb_ = std::move(cb); } -void FileTransferTunnel::on_status(TunnelStatusCb cb) { status_cb_ = std::move(cb); } - -void FileTransferTunnel::shutdown() -{ - if (m_->ft_tunnel_shutdown) (void) m_->ft_tunnel_shutdown(h_); -} - -FileTransferJob::FileTransferJob(FileTransferModule &m, const std::string ¶ms_json) : m_(&m) -{ - FT_JobHandle *h{}; - if (m_->ft_job_create(params_json.c_str(), &h) != 0 || !h) { - - } - h_ = h; - - // C API: ft_job_result_cb(void* user, int tunnel_err, ft_job_result result) - auto tramp = [](void *user, ft_job_result r) noexcept { - auto *self = reinterpret_cast(user); - if (!self) return; - - try { - self->finished_ = true; - self->solve_result(r); - - if (self->result_cb_) self->result_cb_(self->res_, self->resp_ec_, self->res_json_, self->res_bin_); - self->m_->ft_job_result_destroy(&r); - } catch (...) { - // swallow - } - - try { - if (auto *mod = self ? self->m_ : nullptr) { - if (mod->ft_job_result_destroy) - mod->ft_job_result_destroy(&r); - else if (mod->ft_free) { - if (r.json) mod->ft_free((void *) r.json); - if (r.bin) mod->ft_free((void *) r.bin); - } - } - } catch (...) {} - }; - - if (m_->ft_job_set_result_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_result_cb failed"); } -} - -void FileTransferJob::on_result(ResultCb cb) { result_cb_ = std::move(cb); } - -bool FileTransferJob::get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) -{ - if (!h_) throw std::runtime_error("job handle invalid"); - ft_job_result result; - if (m_->ft_job_get_result(h_, timeout_ms, &result) == ft_err::FT_EXCEPTION) return false; - solve_result(result); - m_->ft_job_result_destroy(&result); - ec = res_; - resp_ec = res_; - json = res_json_; - bin = res_bin_; - return true; -} - -void FileTransferJob::start_on(FileTransferTunnel &t) -{ - if (!h_) throw std::runtime_error("job handle invalid"); - if (m_->ft_tunnel_start_job(t.native(), h_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_job failed"); } -} - -void FileTransferJob::on_msg(MsgCb cb) -{ - msg_cb_ = std::move(cb); - if (!h_) return; - - // C API: ft_job_msg_cb(void* user, ft_job_msg msg) - auto tramp = [](void *user, ft_job_msg m) noexcept { - auto *self = reinterpret_cast(user); - if (!self) return; - try { - if (self->msg_cb_) { self->msg_cb_(m.kind, std::string(m.json ? m.json : "")); } - } catch (...) {} - - try { - if (auto *mod = self->m_) { - if (mod->ft_job_msg_destroy) - mod->ft_job_msg_destroy(&m); - else if (mod->ft_free && m.json) - mod->ft_free((void *) m.json); - } - } catch (...) {} - }; - - if (m_->ft_job_set_msg_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_msg_cb failed"); } -} - -bool FileTransferJob::try_get_msg(int &kind, std::string &json) -{ - if (!h_) return false; - ft_job_msg m{}; - int rc = m_->ft_job_try_get_msg(h_, &m); - if (rc != 0) return false; - - kind = m.kind; - json.assign(m.json ? m.json : ""); - - if (m_->ft_job_msg_destroy) - m_->ft_job_msg_destroy(&m); - else if (m_->ft_free && m.json) - m_->ft_free((void *) m.json); - - return true; -} - -bool FileTransferJob::get_msg(uint32_t timeout_ms, int &kind, std::string &json) -{ - if (!h_) return false; - ft_job_msg m{}; - int rc = m_->ft_job_get_msg(h_, timeout_ms, &m); - if (rc != 0) return false; - - kind = m.kind; - json.assign(m.json ? m.json : ""); - - if (m_->ft_job_msg_destroy) - m_->ft_job_msg_destroy(&m); - else if (m_->ft_free && m.json) - m_->ft_free((void *) m.json); - - return true; -} - -void FileTransferJob::solve_result(ft_job_result result) -{ - res_ = result.ec; - resp_ec_ = result.resp_ec; - - res_bin_.clear(); - if (result.bin && result.bin_size) res_bin_.assign(reinterpret_cast(result.bin), - reinterpret_cast(result.bin) + result.bin_size); - res_json_.assign(result.json ? result.json : ""); -} - -} // namespace Slic3r \ No newline at end of file +} // namespace Slic3r diff --git a/src/slic3r/Utils/FileTransferUtils.hpp b/src/slic3r/Utils/FileTransferUtils.hpp index 037a1bef0c..2e0d162ddb 100644 --- a/src/slic3r/Utils/FileTransferUtils.hpp +++ b/src/slic3r/Utils/FileTransferUtils.hpp @@ -1,13 +1,7 @@ #pragma once #include -#include -#include -#include #include #include -#include -#include -#include #include #ifdef _WIN32 @@ -134,103 +128,11 @@ struct FileTransferModule FileTransferModule &operator=(const FileTransferModule &) = delete; }; -class FileTransferTunnel -{ -public: - using ConnectionCb = std::function; - using TunnelStatusCb = std::function; - - explicit FileTransferTunnel(FileTransferModule &m, const std::string &url); - ~FileTransferTunnel() { reset(); } - - FileTransferTunnel(const FileTransferTunnel &) = delete; - FileTransferTunnel &operator=(const FileTransferTunnel &) = delete; - FileTransferTunnel(FileTransferTunnel &&) = delete; - FileTransferTunnel &operator=(FileTransferTunnel &&) = delete; - - void start_connect(); - bool sync_start_connect(); - void on_connection(ConnectionCb cb); - void on_status(TunnelStatusCb cb); - - void shutdown(); - - int get_status() const { return status_; } - bool check_valid() const { return h_ != nullptr; } - FT_TunnelHandle *native() const noexcept { return h_; } - -private: - void reset() noexcept - { - if (h_) { - m_->ft_tunnel_release(h_); - h_ = nullptr; - } - } - - int status_{}; - FileTransferModule *m_{}; - FT_TunnelHandle *h_{}; - ConnectionCb conn_cb_{}; - TunnelStatusCb status_cb_{}; -}; - -class FileTransferJob -{ -public: - using ResultCb = std::function bin_res)>; - using MsgCb = std::function; - - explicit FileTransferJob(FileTransferModule &m, const std::string ¶ms_json); - ~FileTransferJob() { reset(); } - - FileTransferJob(const FileTransferJob &) = delete; - FileTransferJob &operator=(const FileTransferJob &) = delete; - FileTransferJob(FileTransferJob &&) = delete; - FileTransferJob &operator=(FileTransferJob &&) = delete; - - void on_result(ResultCb cb); - - bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms); - - void start_on(FileTransferTunnel &t); - - void on_msg(MsgCb cb); - - bool try_get_msg(int &kind, std::string &json); - - bool get_msg(uint32_t timeout_ms, int &kind, std::string &json); - - FT_JobHandle *native() const noexcept { return h_; } - bool check_valid() const { return h_ != nullptr; } - bool finished() const { return finished_; } - - void cancel() - { - if (m_->ft_job_cancel && h_) m_->ft_job_cancel(h_); - } - -private: - void reset() noexcept - { - if (h_) { - m_->ft_job_release(h_); - h_ = nullptr; - } - } - - void solve_result(ft_job_result result); - - FileTransferModule *m_{}; - FT_JobHandle *h_{}; - ResultCb result_cb_{}; - MsgCb msg_cb_{}; - bool finished_ = false; - int res_ = 0; - int resp_ec_ = 0; - std::string res_json_; - std::vector res_bin_; -}; +// FileTransferTunnel/FileTransferJob (the OOP wrapper around the ft_tunnel_*/ +// ft_job_* ABI below) live in BBLPrinterAgent.hpp as BBLFileTransferTunnel/ +// BBLFileTransferJob, implementing IFileTransferTunnel/IFileTransferJob +// (IPrinterAgent.hpp) - this header stays the low-level symbol-table layer +// only, same role as bambu_networking.hpp's function pointer typedefs. namespace detail { inline FileTransferModule *g_mod = nullptr; diff --git a/src/slic3r/Utils/ICloudServiceAgent.hpp b/src/slic3r/Utils/ICloudServiceAgent.hpp index 556c253641..e446afab09 100644 --- a/src/slic3r/Utils/ICloudServiceAgent.hpp +++ b/src/slic3r/Utils/ICloudServiceAgent.hpp @@ -47,6 +47,9 @@ struct CloudEvent { using AppOnServerConnectedFn = std::function; using AppOnHttpErrorFn = std::function; +struct CameraURLParams; +struct CameraURLResult; + class ICloudServiceAgent { public: virtual ~ICloudServiceAgent() = default; @@ -328,7 +331,8 @@ public: /** * Request live camera streaming URL. */ - virtual int get_camera_url(std::string dev_id, std::function callback) = 0; + virtual int get_camera_url(std::string dev_id, std::function callback, + CameraURLParams params) = 0; /** * Fetch staff-picked designs from model mall. diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 0e88e34d4a..d869e56ba6 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -2,6 +2,7 @@ #define __I_PRINTER_AGENT_HPP__ #include "bambu_networking.hpp" +#include // why: these extend the BAMBU_NETWORK_* return space rather than opening a new one - the value // flows through the same int domain callers already compare against BAMBU_NETWORK_SUCCESS. // They live here and not in bambu_networking.hpp because that file is a vendor header replaced @@ -9,8 +10,14 @@ // -70xx is free: the vendor occupies -1..-25 and -10xx through -60xx. #define ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED -7010 // no translation exists for this command #define ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE -7020 // a translation exists; this printer lacks the capability +#define ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED -7030 // printer access preflight failed before printing #include #include +#include +#include +#include + +#include "NetworkAgent.hpp" namespace Slic3r { @@ -43,6 +50,77 @@ enum class FilamentSyncMode { pull ///< On-demand fetch via REST API (blocking call) }; +class IFileTransferTunnel +{ +public: + using ConnectionCb = std::function; + using TunnelStatusCb = std::function; + + explicit IFileTransferTunnel(const std::string& url) : url_(url) {} + virtual ~IFileTransferTunnel() = default; + + IFileTransferTunnel(const IFileTransferTunnel&) = delete; + IFileTransferTunnel& operator=(const IFileTransferTunnel&) = delete; + IFileTransferTunnel(IFileTransferTunnel&&) = delete; + IFileTransferTunnel& operator=(IFileTransferTunnel&&) = delete; + + virtual void start_connect() = 0; + virtual bool sync_start_connect() = 0; + virtual void on_connection(ConnectionCb cb) { conn_cb_ = std::move(cb); } + virtual void on_status(TunnelStatusCb cb) { status_cb_ = std::move(cb); } + + virtual void shutdown() = 0; + + virtual int get_status() const { return status_; } + virtual bool check_valid() const = 0; + + // why: IFileTransferJob::start_on() only ever sees a tunnel through this interface, + // but needs the concrete backend handle to hand to its own start-job call - native() + // is the type-erased escape hatch, same pattern IFileTransferJob::native() already uses. + virtual void *native() const noexcept { return nullptr; } + +protected: + std::string url_; + int status_{}; + ConnectionCb conn_cb_{}; + TunnelStatusCb status_cb_{}; +}; + +class IFileTransferJob { +public: + using ResultCb = std::function bin_res)>; + using MsgCb = std::function; + + explicit IFileTransferJob(const std::string ¶ms_json) : params_json_(params_json) {} + virtual ~IFileTransferJob() = default; + + IFileTransferJob(const IFileTransferJob &) = delete; + IFileTransferJob &operator=(const IFileTransferJob &) = delete; + IFileTransferJob(IFileTransferJob &&) = delete; + IFileTransferJob &operator=(IFileTransferJob &&) = delete; + + virtual void on_result(ResultCb cb) { result_cb_ = std::move(cb); } + virtual bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) = 0; + virtual void start_on(IFileTransferTunnel &t) = 0; + virtual void on_msg(MsgCb cb) { msg_cb_ = std::move(cb); } + virtual bool try_get_msg(int &kind, std::string &json) = 0; + virtual bool get_msg(uint32_t timeout_ms, int &kind, std::string &json) = 0; + virtual void *native() const noexcept { return nullptr; } + virtual bool check_valid() const = 0; + virtual bool finished() const { return finished_; } + virtual void cancel() = 0; + +protected: + std::string params_json_; + ResultCb result_cb_{}; + MsgCb msg_cb_{}; + bool finished_ = false; + int res_ = 0; + int resp_ec_ = 0; + std::string res_json_; + std::vector res_bin_; +}; + /** * IPrinterAgent - Interface for printer operations. * @@ -111,8 +189,16 @@ public: * Build a ready-to-use local (LAN) camera stream URL for this agent's protocol. * Returns an empty string if the agent has no local camera stream support. */ - virtual std::string get_local_camera_url(std::string dev_ip, std::string username, std::string password) - { return {}; } + virtual std::string get_local_camera_url(CameraURLParams params) { return ""; } + + /** + * Build a ready-to-use local (LAN) file transfer URL for this agent's protocol. + * Returns an empty string if the agent has no local file transfer support. + */ + virtual std::string get_local_file_transfer_url(const FileTransferURLParams& params) { return ""; } + + virtual int get_file_transfer_url(std::string, std::function, FileTransferURLParams) + { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } /** * Default LAN account username for this agent's protocol, if it has a fixed one. @@ -326,6 +412,33 @@ public: * Populates the MachineObject's DevFilaSystem with fetched filament data. */ virtual bool fetch_filament_info(std::string dev_id) { return false; } + + /** + * Build a local eMMC transfer tunnel for this agent's protocol, if it has one. + * Returns nullptr if the agent has no local file-transfer tunnel concept + * (matches the inert-default pattern used by get_local_camera_url() etc. above - + * this stays non-pure so adding it doesn't force every IPrinterAgent implementation, + * including the Python plugin capability bridge, to override a Bambu-only concept). + */ + virtual std::unique_ptr create_file_transfer_tunnel(std::string& dev_ip, std::string& access_code) + { return nullptr; } + + /** + * Wrap an already-resolved transfer URL (e.g. a cloud-relay/TUTK URL obtained via + * a camera-url lookup) in a local transfer tunnel. Same inert-default reasoning as + * create_file_transfer_tunnel() above; use that one instead when building a tunnel + * straight from dev_ip/access_code. + */ + virtual std::unique_ptr create_file_transfer_tunnel_from_url(std::string url) + { return nullptr; } + + /** + * Build a file-transfer job (media-ability query, upload, ...) to run on a tunnel + * from create_file_transfer_tunnel()/create_file_transfer_tunnel_from_url(). Same + * inert-default reasoning as the tunnel factories above. + */ + virtual std::unique_ptr create_file_transfer_job(std::string params_json) + { return nullptr; } }; } // namespace Slic3r diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index ce1639541f..7598408e25 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -4,6 +4,7 @@ #include #include +#include "IPrinterAgent.hpp" #include "libslic3r/Utils.hpp" #include "NetworkAgent.hpp" #include "BBLNetworkPlugin.hpp" @@ -507,11 +508,12 @@ int NetworkAgent::modify_printer_name(std::string dev_id, std::string dev_name, return -1; } -int NetworkAgent::get_camera_url(std::string dev_id, std::function callback, const std::string& provider) +int NetworkAgent::get_camera_url(std::string dev_id, std::function callback, + const std::string& provider, CameraURLParams params) { const auto cloud_agent = get_cloud_agent(provider); if (cloud_agent) - return cloud_agent->get_camera_url(std::move(dev_id), std::move(callback)); + return cloud_agent->get_camera_url(std::move(dev_id), std::move(callback), std::move(params)); return -1; } @@ -852,13 +854,28 @@ int NetworkAgent::send_message_to_printer(std::string dev_id, std::string json_s return -1; } -std::string NetworkAgent::get_local_camera_url(std::string dev_ip, std::string username, std::string password) +std::string NetworkAgent::get_local_camera_url(CameraURLParams params) { if (m_printer_agent) - return m_printer_agent->get_local_camera_url(dev_ip, username, password); + return m_printer_agent->get_local_camera_url(params); return {}; } +std::string NetworkAgent::get_local_file_transfer_url(const FileTransferURLParams& params) +{ + if (m_printer_agent) + return m_printer_agent->get_local_file_transfer_url(params); + return {}; +} + +int NetworkAgent::get_file_transfer_url(std::string dev_id, std::function callback, + FileTransferURLParams params) +{ + if (m_printer_agent) + return m_printer_agent->get_file_transfer_url(std::move(dev_id), std::move(callback), std::move(params)); + return -1; +} + std::string NetworkAgent::default_lan_username() const { if (m_printer_agent) diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index cc03362f88..0f3a055e03 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -2,9 +2,11 @@ #define __NETWORK_Agent_HPP__ #include "bambu_networking.hpp" + #include "libslic3r/ProjectTask.hpp" #include "ICloudServiceAgent.hpp" -#include "IPrinterAgent.hpp" +#include "slic3r/GUI/DeviceManager.hpp" + #include #include #include @@ -12,6 +14,53 @@ namespace Slic3r { +class IPrinterAgent; +enum class FilamentSyncMode; + +enum URL_STATE { + URL_TCP, + URL_TUTK, +}; + +struct CameraURLParams { + std::string ip_address; + std::string user; + std::string password; + LiveviewLocal protocol; + std::string device; + std::string network_version; + std::string device_version; + std::string refresh_url; + std::string client_id; + std::string client_version; + bool apply_meta{false}; +}; + +struct FileTransferURLParams { + URL_STATE url_state{URL_TCP}; + std::string ip_address; + std::string username; + std::string password; + std::string device_id; + std::string network_version; + std::string device_version; + std::string refresh_url; + std::string client_id; + std::string client_version; +}; + +struct FileTransferURLResult { + bool is_success{false}; + std::string url; + int error_code{-1}; +}; + +struct CameraURLResult { + bool is_success{false}; + std::string url; + int error_code{-1}; +}; + // Forward declaration class BBLNetworkPlugin; @@ -108,7 +157,8 @@ public: int get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json, const std::string& provider = ORCA_CLOUD_PROVIDER); int query_bind_status(std::vector query_list, unsigned int* http_code, std::string* http_body, const std::string& provider = ORCA_CLOUD_PROVIDER); int modify_printer_name(std::string dev_id, std::string dev_name, const std::string& provider = ORCA_CLOUD_PROVIDER); - int get_camera_url(std::string dev_id, std::function callback, const std::string& provider = ORCA_CLOUD_PROVIDER); + int get_camera_url(std::string dev_id, std::function callback, + const std::string& provider = ORCA_CLOUD_PROVIDER, CameraURLParams params = {}); int get_design_staffpick(int offset, int limit, std::function callback, const std::string& provider = ORCA_CLOUD_PROVIDER); int start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out, const std::string& provider = ORCA_CLOUD_PROVIDER); int get_model_publish_url(std::string* url, const std::string& provider = ORCA_CLOUD_PROVIDER); @@ -155,7 +205,10 @@ public: int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl); int disconnect_printer(); int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag); - std::string get_local_camera_url(std::string dev_ip, std::string username, std::string password); + std::string get_local_camera_url(CameraURLParams params); + std::string get_local_file_transfer_url(const FileTransferURLParams& params); + int get_file_transfer_url(std::string dev_id, std::function callback, + FileTransferURLParams params = {}); std::string default_lan_username() const; int check_cert(); void install_device_cert(std::string dev_id, bool lan_only); diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp index a372ab5b7c..4b06c8b0e5 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp @@ -1,4 +1,5 @@ #include "OrcaCloudServiceAgent.hpp" +#include "NetworkAgent.hpp" #include "Http.hpp" #include "libslic3r/Utils.hpp" #include "slic3r/GUI/GUI_App.hpp" @@ -2698,11 +2699,12 @@ int OrcaCloudServiceAgent::modify_printer_name(std::string dev_id, std::string d return BAMBU_NETWORK_SUCCESS; } -int OrcaCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback) +int OrcaCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) { + (void) params; BOOST_LOG_TRIVIAL(debug) << "OrcaCloudServiceAgent: get_camera_url (stub)"; if (callback) - callback(""); + callback({}); return BAMBU_NETWORK_SUCCESS; } diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp index 3ae86ec27c..696ba261c6 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp @@ -240,7 +240,7 @@ public: // ======================================================================== // ICloudServiceAgent Interface Implementation - Model Mall & Publishing // ======================================================================== - int get_camera_url(std::string dev_id, std::function callback) override; + int get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) override; int get_design_staffpick(int offset, int limit, std::function 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; From 44793f7a21ed073f12a8be9534dfed1cf3192d34 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 12 Aug 2026 13:51:54 +0800 Subject: [PATCH 06/15] remove unused --- src/slic3r/GUI/DeviceManager.cpp | 72 -------------------------------- 1 file changed, 72 deletions(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index cce7170039..f824fd3ad1 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -2855,78 +2855,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ j = j_pre; } -#pragma region CAP_SUBSCRIPTIONS - if (j_pre.contains("capabilities")) { - auto& caps = j_pre["capabilities"]; - // if (caps.contains("extruder_count")) { - // GetExtderSystem()->GetTotalExtderCount(); - // } - if (caps.contains("supports_extruder_control")) - is_enable_np = caps.value("supports_extruder_control", 0); - if (caps.contains("supports_part_skip")) - is_support_partskip = caps.value("supports_part_skip", 0); - if (caps.contains("has_door_sensor")) - is_support_door_open_check = caps.value("has_door_sensor", 0); - if (caps.contains("supports_auto_recovery")) - is_support_auto_recovery_step_loss = caps.value("supports_auto_recovery", 0); - if (caps.contains("supports_prompt_sound")) - is_support_prompt_sound = caps.value("supports_prompt_sound", 0); - if (caps.contains("supports_spaghetti_detection")) - is_support_spaghetti_detection = caps.value("supports_spaghetti_detection", 0); - if (caps.contains("supports_purge_chute_pileup_detection")) - is_support_purgechutepileup_detection = caps.value("supports_purge_chute_pileup_detection", 0); - if (caps.contains("supports_nozzle_clumping_detection")) - is_support_nozzleclumping_detection = caps.value("supports_nozzle_clumping_detection", 0); - if (caps.contains("supports_build_plate_marker_detection")) - is_support_build_plate_marker_detect = caps.value("supports_build_plate_marker_detection", 0); - if (caps.contains("supports_ams_humidity")) - is_support_ams_humidity = caps.value("supports_ams_humidity", 0); - if (caps.contains("supports_pa_calibration_manual")) - is_support_pa_calibration = caps.value("supports_pa_calibration_manual", 0); - if (caps.contains("supports_flow_rate_calibration_manual")) - is_support_flow_calibration = caps.value("supports_flow_rate_calibration_manual", 0); - - if (caps.contains("supports_hotend_rack")) - m_nozzle_system->SetSupportNozzleRack(caps.value("supports_hotend_rack", 0)); - if (caps.contains("has_camera")) - has_ipcam = caps.value("has_camera", 0); - - // Printing with no filament loaded - if (caps.contains("is_support_ams_air_print_detection")) - is_support_air_print_detection = caps.value("is_support_ams_air_print_detection", 0); - - if (caps.contains("is_support_airprinting_detection")) - is_support_airprinting_detection = caps.value("is_support_airprinting_detection", 0); - if (caps.contains("camera_resolutions")) - camera_resolution_supported = caps.value("camera_resolutions", std::vector{}); - // if (caps.contains("ams_unit_count")) - // DevFilaSystemParser::ParseV1_0(caps["ams_unit_count"], this, m_fila_system.get(), true); - - // Remaining documented "capabilities" keys are intentionally not wired here yet. - // - // Already cached elsewhere, but not as a single direct is_support_* bool member - // (a differently-named member, a count, a method on another class, or two competing - // members), so leave these to their existing plumbing: - // extruder_count -> DevExtderSystem::m_total_extder_count - // ams_unit_count -> DevFilaSystem::GetAmsCount() - // - // No existing member at all -- would need a new is_support_* field and a design - // decision on naming/semantics before adding: - // has_lidar - // supports_plate_align_detection - // supports_fod_detection - // supports_displacement_detection - // supports_ai_monitoring -> only an enabled-state member exists (xcam_ai_monitoring) - // supports_first_layer_inspection -> only an enabled-state member exists (xcam_first_layer_inspector) - // - // Genuinely complex (array/object values, or no authoritative single source to derive from): - // supported_nozzle_types, supports_nozzle_blob_detection, has_ams, ams_slot_count, - // supports_ams_rfid, supports_multiple_bed_types, supports_filament_mapping, - // supports_pa_calibration_auto, supports_flow_rate_calibration_auto, - // supports_max_volumetric_speed_calibration, supports_ota_update - } -#pragma endregion - uint64_t t_utc = j.value("t_utc", 0ULL); if (t_utc > 0) { last_utc_time = std::chrono::system_clock::time_point(t_utc * 1ms); From 108923bdaa4bb3c310b167a3c1b37a8adf16d7fd Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 12 Aug 2026 14:02:04 +0800 Subject: [PATCH 07/15] fix: default impl --- src/slic3r/GUI/PartSkipDialog.hpp | 2 +- src/slic3r/Utils/IPrinterAgent.hpp | 8 ++++++-- src/slic3r/Utils/NetworkAgent.cpp | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index d8c7282710..4137a476fc 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -155,4 +155,4 @@ private: void OnApplyDialog(wxCommandEvent &event); }; -}} // namespace Slic3r::GUI +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index d869e56ba6..08e72aa906 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -197,8 +197,12 @@ public: */ virtual std::string get_local_file_transfer_url(const FileTransferURLParams& params) { return ""; } - virtual int get_file_transfer_url(std::string, std::function, FileTransferURLParams) - { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } + virtual int get_file_transfer_url(std::string, std::function callback, FileTransferURLParams) + { + if (callback) + callback({}); + return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; + } /** * Default LAN account username for this agent's protocol, if it has a fixed one. diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index 7598408e25..62a2e21f51 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -873,6 +873,8 @@ int NetworkAgent::get_file_transfer_url(std::string dev_id, std::functionget_file_transfer_url(std::move(dev_id), std::move(callback), std::move(params)); + if (callback) + callback({}); return -1; } From da187eaaf9dbf5af0887f11c818614b1a5c239b8 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 12 Aug 2026 15:29:50 +0800 Subject: [PATCH 08/15] fix callback error --- src/slic3r/Utils/BBLPrinterAgent.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 591750005d..af6053f721 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -490,7 +490,15 @@ int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::functionget_camera_url( std::move(dev_id) + "|" + params.device_version + "|" + protocols, - std::move(callback), + [callback = std::move(callback)](CameraURLResult camera_result) { + if (!callback) + return; + FileTransferURLResult result; + result.is_success = camera_result.is_success; + result.url = std::move(camera_result.url); + result.error_code = camera_result.error_code; + callback(std::move(result)); + }, CameraURLParams{ "", "", From 7ba5718be6670e1d2a3fac991cbec35d4455334c Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 12 Aug 2026 18:41:51 +0800 Subject: [PATCH 09/15] fix: remove redundant cache --- src/slic3r/GUI/DeviceManager.cpp | 2 +- src/slic3r/GUI/MediaFilePanel.cpp | 3 +-- src/slic3r/GUI/MediaFilePanel.h | 1 - src/slic3r/GUI/MediaPlayCtrl.cpp | 20 ++++++++++++-------- src/slic3r/GUI/MediaPlayCtrl.h | 1 - src/slic3r/GUI/SendToPrinter.cpp | 1 - src/slic3r/Utils/BBLPrinterAgent.cpp | 21 +++++++++++++++------ src/slic3r/Utils/BBLPrinterAgent.hpp | 1 + src/slic3r/Utils/IPrinterAgent.hpp | 8 ++++++++ src/slic3r/Utils/NetworkAgent.cpp | 8 ++++++++ src/slic3r/Utils/NetworkAgent.hpp | 1 + 11 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index f824fd3ad1..9697408cb6 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -2569,7 +2569,7 @@ void MachineObject::set_print_state(std::string status) int MachineObject::connect(bool use_openssl) { if (get_dev_ip().empty()) return -1; - std::string username = "bblp"; + std::string username = m_agent ? m_agent->default_lan_username() : std::string(); std::string password = get_access_code(); if (m_agent) { diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index ee7b7143d3..0601b96949 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -207,7 +207,6 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) Bind(wxEVT_SHOW, onShowHide); parent->GetParent()->Bind(wxEVT_SHOW, onShowHide); - m_lan_user = wxGetApp().getAgent()->default_lan_username(); } MediaFilePanel::~MediaFilePanel() @@ -467,7 +466,7 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) m_waiting_support = false; NetworkAgent *agent = wxGetApp().getAgent(); if (agent && (m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { - std::string url = agent->get_local_camera_url({m_lan_ip, m_lan_user, m_lan_passwd, LVL_None, + std::string url = agent->get_local_camera_url({m_lan_ip, agent->default_lan_username(), m_lan_passwd, LVL_None, m_machine, agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); fs->SetUrl(url); return; diff --git a/src/slic3r/GUI/MediaFilePanel.h b/src/slic3r/GUI/MediaFilePanel.h index 72fbc96a13..41596c38c1 100644 --- a/src/slic3r/GUI/MediaFilePanel.h +++ b/src/slic3r/GUI/MediaFilePanel.h @@ -80,7 +80,6 @@ private: std::string m_machine; std::string m_lan_ip; - std::string m_lan_user; std::string m_lan_passwd; std::string m_dev_ver; bool m_lan_mode = false; diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index b255bdac32..0a796981d1 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -7,7 +7,6 @@ #include "I18N.hpp" #include "MsgDialog.hpp" #include "DownloadProgressDialog.hpp" -#include "slic3r/Utils/BBLNetworkPlugin.hpp" #include @@ -127,8 +126,6 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w parent->Bind(wxEVT_SHOW, &MediaPlayCtrl::on_show_hide, this); parent->GetParent()->GetParent()->Bind(wxEVT_SHOW, &MediaPlayCtrl::on_show_hide, this); - m_lan_user = "bblp"; - m_lan_passwd = "bblp"; } MediaPlayCtrl::~MediaPlayCtrl() @@ -159,8 +156,10 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) m_device_busy = obj->is_camera_busy_off(); m_tutk_state = obj->tutk_state; - if (DevPrinterConfigUtil::get_printer_series_str(obj->printer_type) == "series_o" && BBLNetworkPlugin::instance().use_legacy_network()) { - // Legacy plugin cannot support remote play for H2D, force using local mode + auto *agent = wxGetApp().getAgent(); + if (agent && !agent->supports_remote_liveview(obj->printer_type)) { + // The selected printer agent may force local mode for incompatible + // plugin/printer combinations. m_remote_proto = LiveviewRemote::LVR_None; } } else { @@ -283,12 +282,17 @@ void MediaPlayCtrl::Play() BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::Play: " << m_lan_proto << m_remote_proto << m_disable_lan; NetworkAgent *agent = wxGetApp().getAgent(); - std::string agent_version = agent ? agent->get_version() : ""; + if (!agent) { + Stop(_L("Please confirm if the printer is connected.")); + return; + } + std::string agent_version = agent->get_version(); + const std::string lan_user = agent->default_lan_username(); if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { m_disable_lan = m_remote_proto && !m_lan_mode; // try remote next time std::string url = agent->get_local_camera_url({ m_lan_ip, - m_lan_user, + lan_user, m_lan_passwd, LiveviewLocal(m_lan_proto), into_u8(m_machine), @@ -525,7 +529,7 @@ void MediaPlayCtrl::ToggleStream() if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { NetworkAgent *agent = wxGetApp().getAgent(); if (!agent) return; - std::string url = agent->get_local_camera_url({m_lan_ip, m_lan_user, m_lan_passwd, LiveviewLocal(m_lan_proto), + std::string url = agent->get_local_camera_url({m_lan_ip, agent->default_lan_username(), m_lan_passwd, LiveviewLocal(m_lan_proto), into_u8(m_machine), agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); std::string file_url = data_dir() + "/cameratools/url.txt"; diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h index f5e5dcddfc..5a7c53f695 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.h +++ b/src/slic3r/GUI/MediaPlayCtrl.h @@ -80,7 +80,6 @@ private: std::string m_machine; int m_lan_proto = 0; std::string m_lan_ip; - std::string m_lan_user; std::string m_lan_passwd; std::string m_dev_ver; std::string m_tutk_state; diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index acbf911660..10620dbabf 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -1797,7 +1797,6 @@ void SendToPrinterDialog::show_file_transfer_error(PrintDialogStatus status, wxS if (m_url_timer && m_url_timer->IsRunning()) m_url_timer->Stop(); m_connection_status = ConnectionStatus::CONNECTION_FAILED; - GetConnection(); show_status(status); update_print_status_msg(message, false, true); } diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index af6053f721..9a49d86b8a 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -468,6 +468,15 @@ std::string BBLPrinterAgent::get_local_file_transfer_url(const FileTransferURLPa return "bambu:///local/" + params.ip_address + "?port=6000&user=" + params.username + "&passwd=" + params.password; } +bool BBLPrinterAgent::supports_remote_liveview(const std::string& printer_type) const +{ + // The legacy Bambu networking plugin cannot provide remote live view for + // the O-series printers. Keep this compatibility rule in the Bambu agent + // instead of exposing plugin/version details to GUI code. + return !(DevPrinterConfigUtil::get_printer_series_str(printer_type) == "series_o" && + BBLNetworkPlugin::instance().use_legacy_network()); +} + int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::function callback, FileTransferURLParams params) { @@ -490,14 +499,14 @@ int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::functionget_camera_url( std::move(dev_id) + "|" + params.device_version + "|" + protocols, - [callback = std::move(callback)](CameraURLResult camera_result) { + [callback = std::move(callback)](CameraURLResult result) { if (!callback) return; - FileTransferURLResult result; - result.is_success = camera_result.is_success; - result.url = std::move(camera_result.url); - result.error_code = camera_result.error_code; - callback(std::move(result)); + FileTransferURLResult transfer_result; + transfer_result.is_success = result.is_success; + transfer_result.url = std::move(result.url); + transfer_result.error_code = result.error_code; + callback(std::move(transfer_result)); }, CameraURLParams{ "", diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index 8d767faa1c..63d796a806 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -118,6 +118,7 @@ public: int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) override; std::string get_local_camera_url(CameraURLParams params) override; std::string get_local_file_transfer_url(const FileTransferURLParams& params) override; + bool supports_remote_liveview(const std::string& printer_type) const override; int get_file_transfer_url(std::string dev_id, std::function callback, FileTransferURLParams params) override; std::string default_lan_username() const override { return "bblp"; } diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 08e72aa906..3b4a72e00b 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -197,6 +197,14 @@ public: */ virtual std::string get_local_file_transfer_url(const FileTransferURLParams& params) { return ""; } + /** + * Whether remote live view is available for the selected printer and agent + * protocol. Implementations may use their plugin/version compatibility + * rules; the neutral default keeps existing agents permissive. + */ + virtual bool supports_remote_liveview(const std::string& printer_type) const + { (void) printer_type; return true; } + virtual int get_file_transfer_url(std::string, std::function callback, FileTransferURLParams) { if (callback) diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index 62a2e21f51..f93a0cf882 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -868,6 +868,14 @@ std::string NetworkAgent::get_local_file_transfer_url(const FileTransferURLParam return {}; } +bool NetworkAgent::supports_remote_liveview(const std::string& printer_type) const +{ + // Preserve the historical permissive behavior while the printer agent is + // being selected. A missing agent must not turn a supported remote + // protocol into LVNone before the Bambu agent has been installed. + return !m_printer_agent || m_printer_agent->supports_remote_liveview(printer_type); +} + int NetworkAgent::get_file_transfer_url(std::string dev_id, std::function callback, FileTransferURLParams params) { diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 0f3a055e03..75cca1e314 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -207,6 +207,7 @@ public: int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag); std::string get_local_camera_url(CameraURLParams params); std::string get_local_file_transfer_url(const FileTransferURLParams& params); + bool supports_remote_liveview(const std::string& printer_type) const; int get_file_transfer_url(std::string dev_id, std::function callback, FileTransferURLParams params = {}); std::string default_lan_username() const; From a34d056de22b427485230bcb00a45f62ab9e1090 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Fri, 14 Aug 2026 15:31:42 +0800 Subject: [PATCH 10/15] specify api for getting file transfer url --- src/slic3r/GUI/MediaFilePanel.cpp | 28 ++- src/slic3r/GUI/SendToPrinter.cpp | 360 ++++++++------------------- src/slic3r/GUI/SendToPrinter.hpp | 4 - src/slic3r/Utils/BBLPrinterAgent.cpp | 206 ++++++++++++++- src/slic3r/Utils/BBLPrinterAgent.hpp | 19 +- src/slic3r/Utils/IPrinterAgent.hpp | 67 +++-- 6 files changed, 376 insertions(+), 308 deletions(-) diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 0601b96949..6b460ce01d 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -466,9 +466,18 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) m_waiting_support = false; NetworkAgent *agent = wxGetApp().getAgent(); if (agent && (m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { - std::string url = agent->get_local_camera_url({m_lan_ip, agent->default_lan_username(), m_lan_passwd, LVL_None, - m_machine, agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); - fs->SetUrl(url); + agent->get_file_transfer_url( + m_machine, + [this, wfs](FileTransferURLResult result) { + CallAfter([this, wfs, result = std::move(result)] { + auto fs = wfs.lock(); + if (!fs || fs != m_image_grid->GetFileSystem()) + return; + fs->SetUrl(result.is_success ? result.url : std::to_string(result.error_code)); + }); + }, + {URL_TCP, m_lan_ip, agent->default_lan_username(), m_lan_passwd, + m_machine, agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); return; } if (!m_remote_proto && m_local_proto) { // not support tutk @@ -487,11 +496,11 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) return; } if (agent) { - std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, wfs, m = m_machine](CameraURLResult result) { + agent->get_file_transfer_url( + m_machine, + [this, wfs, m = m_machine](FileTransferURLResult result) { std::string url = std::move(result.url); - BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: file_system_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; @@ -503,8 +512,9 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) fs->SetUrl(res); } }); - }, wxGetApp().get_printer_cloud_provider(), CameraURLParams{"", "", "", LVL_None, m_machine, agent->get_version(), m_dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + }, + {URL_TUTK, "", "", "", m_machine, agent->get_version(), m_dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); } } diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index 10620dbabf..80167850e6 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -845,11 +845,8 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) m_task_timer.reset(); } - if (m_filetransfer_uploadfile_job) { - m_filetransfer_uploadfile_job->cancel(); - m_filetransfer_uploadfile_job.reset(); - m_filetransfer_uploadfile_job = nullptr; - } + if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) + agent->get_printer_agent()->cancel_file_transfer(); m_is_canceled = true; wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL); @@ -868,16 +865,16 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) if (wxGetApp().plater()->using_exported_file()) { m_plater->set_print_job_plate_idx(m_print_plate_idx); result = 0; + } else { + result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool& cancel) { + if (this->m_is_canceled) + return; + bool cancelled = false; + wxString msg = _L("Preparing print job"); + m_status_bar->update_status(msg, cancelled, 10, true); + m_export_3mf_cancel = cancel = cancelled; + }); } - else { - result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool &cancel) { - if (this->m_is_canceled) return; - bool cancelled = false; - wxString msg = _L("Preparing print job"); - m_status_bar->update_status(msg, cancelled, 10, true); - m_export_3mf_cancel = cancel = cancelled; - }); - } if (m_is_canceled || m_export_3mf_cancel) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": send progress 10"; @@ -941,7 +938,8 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) this->Bind(wxEVT_TIMER, [this](auto e){ show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); - m_filetransfer_uploadfile_job->cancel(); + if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) + agent->get_printer_agent()->cancel_file_transfer(); update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true); },m_task_timer->GetId()); m_task_timer->StartOnce(timeout_period); @@ -1297,10 +1295,8 @@ void SendToPrinterDialog::update_show_status() else m_if_has_sdcard = true; - if (m_filetransfer_tunnel) { - m_filetransfer_tunnel.reset(); - m_filetransfer_tunnel = nullptr; - } + if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) + agent->get_printer_agent()->cancel_file_transfer(); GetConnection(); } @@ -1659,7 +1655,6 @@ bool SendToPrinterDialog::Show(bool show) return DPIDialog::Show(show); } -extern wxString hide_passwd(wxString url, std::vector const &passwords); extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); void SendToPrinterDialog::GetConnection() @@ -1670,126 +1665,71 @@ void SendToPrinterDialog::GetConnection() if (obj == nullptr) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : obj is empty"; m_connection_status = ConnectionStatus::NOT_START; + return; } int remote_proto = obj->get_file_remote(); if (!remote_proto) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : remote_proto is not support"; m_connection_status = ConnectionStatus::NOT_START; + return; } if (obj->is_camera_busy_off()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : camera is busy"; m_connection_status = ConnectionStatus::NOT_START; + return; } - NetworkAgent *agent = wxGetApp().getAgent(); - std::string agent_version = agent ? agent->get_version() : ""; - std::string dev_ver = obj->get_ota_version(); - std::string dev_id = obj->get_dev_id(); - - if (m_url_timer && m_url_timer->IsRunning()) - { - m_url_timer->Stop(); - } - - m_url_timer.reset(new wxTimer()); - m_url_timer->SetOwner(this); - this->Bind( - wxEVT_TIMER, - [this](wxTimerEvent &e) { - BOOST_LOG_TRIVIAL(info) << "Timer callback triggered!"; - m_connection_status = ConnectionStatus::CONNECTION_FAILED; - m_ftp_try_connect = true; - if (m_filetransfer_tunnel) - { - m_filetransfer_tunnel.reset(); - m_filetransfer_tunnel = nullptr; - } - - }, - m_url_timer->GetId()); - m_url_timer->StartOnce(8000); - - if (agent) { - if (m_tcp_try_connect) { - std::string devIP = obj->get_dev_ip(); - std::string accessCode = obj->get_access_code(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tcp"; - - if (agent->get_printer_agent()) { - try { - m_filetransfer_tunnel = agent->get_printer_agent()->create_file_transfer_tunnel(devIP, accessCode); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create TCP file-transfer tunnel: " << e.what(); - } - - if (m_filetransfer_tunnel && m_filetransfer_tunnel->check_valid()) { - m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { - CallAfter([this, is_success, err_code, error_msg]() { - OnConnection(is_success, err_code, error_msg); - }); - }); - m_filetransfer_tunnel->start_connect(); - } else { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - } - } else { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - } - } - else if (m_tutk_try_connect) - { - std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url( - obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], - [this, pa = agent->get_printer_agent()](CameraURLResult result) { - std::string url = std::move(result.url); - if (m_url_timer && m_url_timer->IsRunning()) { - m_url_timer->Stop(); - } - -#if !BBL_RELEASE_TO_PUBLIC - BOOST_LOG_TRIVIAL(info) << "SendToPrinter::camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); -#endif - - if (result.is_success) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tutk"; - if (!pa) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; - } - - try { - m_filetransfer_tunnel = pa->create_file_transfer_tunnel_from_url(url); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create TUTK file-transfer tunnel: " << e.what(); - } - if (!m_filetransfer_tunnel || !m_filetransfer_tunnel->check_valid()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; - } - - m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { - CallAfter([this, is_success, err_code, error_msg]() { OnConnection(is_success, err_code, error_msg); }); - }); - m_filetransfer_tunnel->start_connect(); - } else { - std::string res = result.error_code >= 0 ? std::to_string(result.error_code) : ""; - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : Tutk url error: ress = " << res; - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicInitFailed, - _L("Connection failed. Please check your network and try again.")); - } - }, - wxGetApp().get_printer_cloud_provider(), - CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); - } + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent || !agent->get_printer_agent()) { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + return; } + + if (m_url_timer && m_url_timer->IsRunning()) { + m_url_timer->Stop(); + } + + m_url_timer.reset(new wxTimer()); + m_url_timer->SetOwner(this); + this->Bind( + wxEVT_TIMER, + [this](wxTimerEvent& e) { + BOOST_LOG_TRIVIAL(info) << "Timer callback triggered!"; + m_connection_status = ConnectionStatus::CONNECTION_FAILED; + m_ftp_try_connect = true; + if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) + agent->get_printer_agent()->cancel_file_transfer(); + }, + m_url_timer->GetId()); + m_url_timer->StartOnce(8000); + + IPrinterAgent::FileTransferRequest request; + request.device_id = obj->get_dev_id(); + request.device_ip = obj->get_dev_ip(); + request.access_code = obj->get_access_code(); + request.network_version = agent->get_version(); + request.device_version = obj->get_ota_version(); + request.refresh_url = boost::lexical_cast(&refresh_agora_url); + request.client_id = wxGetApp().app_config->get("slicer_uuid"); + request.client_version = SLIC3R_VERSION; + request.lan_mode = obj->connection_type() == "lan"; + + IPrinterAgent::FileTransferCallbacks callbacks; + callbacks.on_connection = [this](bool is_success, int error_code, std::string error_msg) { + CallAfter([this, is_success, error_code, error_msg = std::move(error_msg)] { + OnConnection(is_success, error_code, std::move(error_msg)); + }); + }; + callbacks.file_transfer_error = [this] { + CallAfter([this] { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + }); + }; + agent->get_printer_agent()->prepare_file_transfer(request, std::move(callbacks)); } void SendToPrinterDialog::show_file_transfer_error(PrintDialogStatus status, wxString message) @@ -1813,41 +1753,11 @@ void SendToPrinterDialog::OnConnection(bool is_success, int error_code, std::str { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Connect failed, error_code is:" << error_code << "error_msg is :" << error_msg; m_connection_status = ConnectionStatus::CONNECTION_FAILED; - ChangeConnectMethod(); - if (!m_tcp_try_connect && !m_tutk_try_connect) { - show_status(PrintDialogStatus::PrintStatusPublicInitFailed); - return; - } - m_filetransfer_tunnel.reset(); - m_filetransfer_tunnel = nullptr; - GetConnection(); - } -} - -void SendToPrinterDialog::ChangeConnectMethod() -{ - DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); - if (!dev) return; - MachineObject *obj = dev->get_my_machine(m_printer_last_select); - if (!obj) return; - - bool is_lan = (obj->connection_type() == "lan"); - - m_tcp_try_connect = false; - - if (is_lan) { - m_ftp_try_connect = true; + m_tcp_try_connect = false; m_tutk_try_connect = false; - } else { - if (m_connect_try_times == 0) { - m_ftp_try_connect = false; - m_tutk_try_connect = true; - } else { - m_ftp_try_connect = true; - m_tutk_try_connect = false; - } + m_ftp_try_connect = true; + show_status(PrintDialogStatus::PrintStatusPublicInitFailed); } - m_connect_try_times++; } void SendToPrinterDialog::ResetConnectMethod() @@ -1861,48 +1771,21 @@ void SendToPrinterDialog::ResetConnectMethod() void SendToPrinterDialog::ResetTunnelAndJob() { - if (m_filetransfer_uploadfile_job) - { - m_filetransfer_uploadfile_job->cancel(); - m_filetransfer_uploadfile_job.reset(); - m_filetransfer_uploadfile_job = nullptr; - } - if (m_filetransfer_mediability_job) - { - m_filetransfer_mediability_job->cancel(); - m_filetransfer_mediability_job.reset(); - m_filetransfer_mediability_job = nullptr; - } - if (m_filetransfer_tunnel) - { - m_filetransfer_tunnel.reset(); - m_filetransfer_tunnel = nullptr; - } + if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) + agent->get_printer_agent()->cancel_file_transfer(); } void SendToPrinterDialog::CreateMediaAbilityJob() { - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no printer agent available"; - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; - } - nlohmann::json media_ability = {{"cmd_type", 7}}; - try { - m_filetransfer_mediability_job = agent->get_printer_agent()->create_file_transfer_job(std::string(media_ability.dump())); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create media-ability job: " << e.what(); - } - if (!m_filetransfer_mediability_job || !m_filetransfer_mediability_job->check_valid()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; - } - m_filetransfer_mediability_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { - //this pl - CallAfter([this, res, resp_ec, json_res] { + NetworkAgent *agent = wxGetApp().getAgent(); + if (!agent || !agent->get_printer_agent()) { + show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, + _L("The selected printer does not support file transfer.")); + return; + } + IPrinterAgent::FileTransferCallbacks callbacks; + callbacks.on_destinations = [this](int res, int resp_ec, std::string json_res) { + CallAfter([this, res, resp_ec, json_res = std::move(json_res)] { if (res == 0) // 0 is success { show_status(PrintDialogStatus::PrintStatusReadingFinished); @@ -1938,78 +1821,41 @@ void SendToPrinterDialog::CreateMediaAbilityJob() show_status(PrintDialogStatus::PrintStatusPublicInitFailed); update_print_status_msg(ParseErrorCode(resp_ec), false, true); } - }); - }); - // Guard against a null transfer tunnel before dereferencing. - if (m_filetransfer_tunnel && m_filetransfer_tunnel->check_valid()) { - m_filetransfer_mediability_job->start_on(*m_filetransfer_tunnel); - } else { + }); + }; + callbacks.file_transfer_error = [this] { + CallAfter([this] { show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, _L("The selected printer does not support file transfer.")); - } + }); + }; + agent->get_printer_agent()->get_file_destinations(std::move(callbacks)); } void SendToPrinterDialog::CreateUploadFileJob(const std::string &path, const std::string &name) { - nlohmann::json upload_params = { - {"cmd_type", 5}, - }; - upload_params["dest_storage"] = m_selected_storage; - upload_params["dest_name"] = name; // filenme no path - upload_params["file_path"] = path; - NetworkAgent *agent = wxGetApp().getAgent(); if (!agent || !agent->get_printer_agent()) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no printer agent available"; show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, _L("The selected printer does not support file transfer.")); return; } - - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Begin CreateUploadFileJob"; - try { - m_filetransfer_uploadfile_job = agent->get_printer_agent()->create_file_transfer_job(std::string(upload_params.dump())); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create upload job: " << e.what(); - } - if (!m_filetransfer_uploadfile_job || !m_filetransfer_uploadfile_job->check_valid()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, - _L("The selected printer does not support file transfer.")); - return; - } - m_filetransfer_uploadfile_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { // - CallAfter([this, res, resp_ec, json_res, bin_res] { - UploadFileRessultCallback(res, resp_ec,json_res, bin_res); + IPrinterAgent::FileTransferCallbacks callbacks; + callbacks.on_progress = [this](int progress) { + CallAfter([this, progress] { UploadFileProgressCallback(progress); }); + }; + callbacks.on_result = [this](int res, int resp_ec, std::string json_res, std::vector bin_res) { + CallAfter([this, res, resp_ec, json_res = std::move(json_res), bin_res = std::move(bin_res)] { + UploadFileRessultCallback(res, resp_ec, std::move(json_res), std::move(bin_res)); }); - }); - - m_filetransfer_uploadfile_job->on_msg([this](int kind, std::string json_res) { - CallAfter([this, kind, json_res] { - if (kind == 0) { - try - { - auto js = nlohmann::json::parse(json_res); - int progress = js["progress"].get(); - UploadFileProgressCallback(progress); - } - catch (const nlohmann::json::exception& e) - { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what(); - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << "parse_json failed! "; - } - } + }; + callbacks.file_transfer_error = [this] { + CallAfter([this] { + show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, + _L("The selected printer does not support file transfer.")); }); - }); - // Guard against a null transfer tunnel before dereferencing. - if (m_filetransfer_tunnel && m_filetransfer_tunnel->check_valid()) { - m_filetransfer_uploadfile_job->start_on(*m_filetransfer_tunnel); - } else { - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, - _L("The selected printer does not support file transfer.")); - } + }; + agent->get_printer_agent()->upload_file(path, name, m_selected_storage, std::move(callbacks)); } void SendToPrinterDialog::UploadFileProgressCallback(int progress) @@ -2028,6 +1874,8 @@ void SendToPrinterDialog::UploadFileProgressCallback(int progress) wxEVT_TIMER, [this](auto e) { show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); + if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) + agent->get_printer_agent()->cancel_file_transfer(); update_print_status_msg( _L("File upload timed out. Please check if the firmware version supports this operation or verify if the printer is functioning properly."), false, true); }, @@ -2056,8 +1904,6 @@ void SendToPrinterDialog::UploadFileRessultCallback(int res, int resp_ec, std::s update_print_status_msg(ParseErrorCode(resp_ec), false, true); else update_print_status_msg(_L("Sending failed, please try again!"), false, true); - m_filetransfer_uploadfile_job.reset(); - m_filetransfer_uploadfile_job = nullptr; } } diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp index 717910ae61..2d0073ce59 100644 --- a/src/slic3r/GUI/SendToPrinter.hpp +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -170,9 +170,6 @@ private: enum ConnectionStatus { NOT_START, CONNECTING, CONNECTED, CONNECTION_FAILED, DISCONNECTED }; ConnectionStatus m_connection_status{ConnectionStatus::NOT_START}; - std::unique_ptr m_filetransfer_tunnel; - std::unique_ptr m_filetransfer_mediability_job; - std::unique_ptr m_filetransfer_uploadfile_job; wxDateTime m_last_refresh_time; public: @@ -226,7 +223,6 @@ private: void OnConnection(bool is_success, int error_code, std::string error_msg); void CreateMediaAbilityJob(); void CreateUploadFileJob(const std::string &path, const std::string &name); - void ChangeConnectMethod(); void UploadFileProgressCallback(int progress); void UploadFileRessultCallback(int res, int resp_ec, std::string json_res, std::vector bin_res); void Reset(); diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 9a49d86b8a..3b81a6fbc5 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -207,6 +207,199 @@ BBLPrinterAgent::BBLPrinterAgent() = default; BBLPrinterAgent::~BBLPrinterAgent() = default; +void BBLPrinterAgent::prepare_file_transfer(const FileTransferRequest& request, FileTransferCallbacks cb) +{ + cancel_file_transfer(); + m_file_transfer_request = request; + m_file_transfer_callbacks = std::move(cb); + m_file_transfer_tcp = true; + m_file_transfer_try_count = 0; + start_file_transfer_attempt(++m_file_transfer_generation); +} + +void BBLPrinterAgent::start_file_transfer_attempt(uint64_t generation) +{ + FileTransferURLParams params; + params.url_state = m_file_transfer_tcp ? URL_TCP : URL_TUTK; + params.ip_address = m_file_transfer_request.device_ip; + params.username = default_lan_username(); + params.password = m_file_transfer_request.access_code; + params.device_id = m_file_transfer_request.device_id; + params.network_version = m_file_transfer_request.network_version; + params.device_version = m_file_transfer_request.device_version; + params.refresh_url = m_file_transfer_request.refresh_url; + params.client_id = m_file_transfer_request.client_id; + params.client_version = m_file_transfer_request.client_version; + + auto handle_url = [this, generation](FileTransferURLResult result) { + if (generation != m_file_transfer_generation) + return; + if (!result.is_success) { + handle_file_transfer_connection(generation, false, result.error_code, "file-transfer URL unavailable"); + return; + } + + try { + m_file_transfer_tunnel = std::make_unique(result.url); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create file-transfer tunnel: " << e.what(); + m_file_transfer_tunnel.reset(); + } + + if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { + handle_file_transfer_connection(generation, false, -1, "file-transfer tunnel unavailable"); + return; + } + + m_file_transfer_tunnel->on_connection([this, generation](bool is_success, int error_code, std::string error_msg) { + handle_file_transfer_connection(generation, is_success, error_code, std::move(error_msg)); + }); + m_file_transfer_tunnel->start_connect(); + }; + + if (m_file_transfer_tcp) { + get_file_transfer_url(m_file_transfer_request.device_id, std::move(handle_url), params); + return; + } + + if (!m_cloud_agent) { + handle_file_transfer_connection(generation, false, -1, "cloud file-transfer URL unavailable"); + return; + } + + const std::string protocols = "\"tutk\""; + m_cloud_agent->get_camera_url( + m_file_transfer_request.device_id + "|" + m_file_transfer_request.device_version + "|" + protocols, + [handle_url = std::move(handle_url)](CameraURLResult result) mutable { + FileTransferURLResult transfer_result; + transfer_result.is_success = result.is_success; + transfer_result.url = std::move(result.url); + transfer_result.error_code = result.error_code; + handle_url(std::move(transfer_result)); + }, + CameraURLParams{ + "", "", "", LVL_None, + m_file_transfer_request.device_id, + m_file_transfer_request.network_version, + m_file_transfer_request.device_version, + m_file_transfer_request.refresh_url, + m_file_transfer_request.client_id, + m_file_transfer_request.client_version, + true + }); +} + +void BBLPrinterAgent::handle_file_transfer_connection(uint64_t generation, bool is_success, int error_code, std::string error_msg) +{ + if (generation != m_file_transfer_generation) + return; + if (is_success) { + if (m_file_transfer_callbacks.on_connection) + m_file_transfer_callbacks.on_connection(true, error_code, std::move(error_msg)); + return; + } + + // Preserve the existing dialog fallback order: TCP, then TUTK for cloud + // printers, and finally the legacy FTP path handled by SendJob. + m_file_transfer_tunnel.reset(); + if (!m_file_transfer_request.lan_mode && m_file_transfer_tcp && m_file_transfer_try_count == 0) { + m_file_transfer_tcp = false; + ++m_file_transfer_try_count; + start_file_transfer_attempt(generation); + return; + } + + if (m_file_transfer_callbacks.on_connection) + m_file_transfer_callbacks.on_connection(false, error_code, std::move(error_msg)); +} + +void BBLPrinterAgent::get_file_destinations(FileTransferCallbacks cb) +{ + if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { + if (cb.file_transfer_error) + cb.file_transfer_error(); + return; + } + + nlohmann::json params = {{"cmd_type", 7}}; + try { + m_file_transfer_job = std::make_unique(params.dump()); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create media-ability job: " << e.what(); + m_file_transfer_job.reset(); + } + + if (!m_file_transfer_job || !m_file_transfer_job->check_valid()) { + if (cb.file_transfer_error) + cb.file_transfer_error(); + return; + } + + m_file_transfer_job->on_result([cb = std::move(cb)](int result, int response_error, std::string json_result, + std::vector) { + if (cb.on_destinations) + cb.on_destinations(result, response_error, std::move(json_result)); + }); + m_file_transfer_job->start_on(*m_file_transfer_tunnel); +} + +void BBLPrinterAgent::upload_file(const std::string& path, const std::string& name, const std::string& destination, + FileTransferCallbacks cb) +{ + if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { + if (cb.file_transfer_error) + cb.file_transfer_error(); + return; + } + + nlohmann::json params = { + {"cmd_type", 5}, + {"dest_storage", destination}, + {"dest_name", name}, + {"file_path", path} + }; + + try { + m_file_transfer_job = std::make_unique(params.dump()); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create upload job: " << e.what(); + m_file_transfer_job.reset(); + } + + if (!m_file_transfer_job || !m_file_transfer_job->check_valid()) { + if (cb.file_transfer_error) + cb.file_transfer_error(); + return; + } + + auto callbacks = std::make_shared(std::move(cb)); + m_file_transfer_job->on_result([callbacks](int result, int response_error, std::string json_result, + std::vector binary_result) { + if (callbacks->on_result) + callbacks->on_result(result, response_error, std::move(json_result), std::move(binary_result)); + }); + m_file_transfer_job->on_msg([callbacks](int kind, std::string json_result) { + if (kind == 0 && callbacks->on_progress) { + try { + callbacks->on_progress(nlohmann::json::parse(json_result).at("progress").get()); + } catch (...) { + BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to parse upload progress"; + } + } + }); + m_file_transfer_job->start_on(*m_file_transfer_tunnel); +} + +void BBLPrinterAgent::cancel_file_transfer() +{ + ++m_file_transfer_generation; + if (m_file_transfer_job) + m_file_transfer_job->cancel(); + m_file_transfer_job.reset(); + m_file_transfer_tunnel.reset(); + m_file_transfer_callbacks = {}; +} + void BBLPrinterAgent::set_cloud_agent(std::shared_ptr cloud) { m_cloud_agent = cloud; @@ -930,17 +1123,4 @@ FilamentSyncMode BBLPrinterAgent::get_filament_sync_mode() const return FilamentSyncMode::subscription; } -std::unique_ptr BBLPrinterAgent::create_file_transfer_tunnel(std::string& dev_ip, std::string& access_code) { - std::string url = "bambu:///local/" + dev_ip + "?port=6000&user=" + default_lan_username() + "&passwd=" + access_code; - return std::make_unique(url); -} - -std::unique_ptr BBLPrinterAgent::create_file_transfer_tunnel_from_url(std::string url) { - return std::make_unique(url); -} - -std::unique_ptr BBLPrinterAgent::create_file_transfer_job(std::string params_json) { - return std::make_unique(params_json); -} - } // namespace Slic3r diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index 63d796a806..685f422228 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -175,9 +175,12 @@ public: int set_queue_on_main_fn(QueueOnMainFn fn) override; FilamentSyncMode get_filament_sync_mode() const override; - std::unique_ptr create_file_transfer_tunnel(std::string& dev_ip, std::string& access_code) override; - std::unique_ptr create_file_transfer_tunnel_from_url(std::string url) override; - std::unique_ptr create_file_transfer_job(std::string params_json) override; + void prepare_file_transfer(const FileTransferRequest& request, FileTransferCallbacks cb) override; + void get_file_destinations(FileTransferCallbacks cb) override; + void upload_file(const std::string& path, const std::string& name, const std::string& destination, + FileTransferCallbacks cb) override; + void cancel_file_transfer() override; + private: int verify_local_print_access(PrintParams params); @@ -186,6 +189,16 @@ private: int publish(const std::string& dev_id, const nlohmann::json& j, bool lan_mode); std::shared_ptr m_cloud_agent; + std::unique_ptr m_file_transfer_tunnel; + std::unique_ptr m_file_transfer_job; + FileTransferCallbacks m_file_transfer_callbacks; + FileTransferRequest m_file_transfer_request; + bool m_file_transfer_tcp{true}; + int m_file_transfer_try_count{0}; + uint64_t m_file_transfer_generation{0}; + + void start_file_transfer_attempt(uint64_t generation); + void handle_file_transfer_connection(uint64_t generation, bool is_success, int error_code, std::string error_msg); }; } // namespace Slic3r diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 3b4a72e00b..51df990cdb 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -425,32 +425,55 @@ public: */ virtual bool fetch_filament_info(std::string dev_id) { return false; } - /** - * Build a local eMMC transfer tunnel for this agent's protocol, if it has one. - * Returns nullptr if the agent has no local file-transfer tunnel concept - * (matches the inert-default pattern used by get_local_camera_url() etc. above - - * this stays non-pure so adding it doesn't force every IPrinterAgent implementation, - * including the Python plugin capability bridge, to override a Bambu-only concept). - */ - virtual std::unique_ptr create_file_transfer_tunnel(std::string& dev_ip, std::string& access_code) - { return nullptr; } + struct FileTransferRequest + { + std::string device_id; + std::string device_ip; + std::string access_code; + std::string network_version; + std::string device_version; + std::string refresh_url; + std::string client_id; + std::string client_version; + bool lan_mode{false}; + }; + + struct FileTransferCallbacks + { + std::function on_connection; + std::function on_destinations; + std::function on_progress; + std::function binary_result)> on_result; + std::function file_transfer_error; + }; /** - * Wrap an already-resolved transfer URL (e.g. a cloud-relay/TUTK URL obtained via - * a camera-url lookup) in a local transfer tunnel. Same inert-default reasoning as - * create_file_transfer_tunnel() above; use that one instead when building a tunnel - * straight from dev_ip/access_code. + * Prepare the agent's file-transfer session. The transport is agent-owned; + * callers must not need to know whether it is a tunnel, HTTP connection, + * or another protocol. */ - virtual std::unique_ptr create_file_transfer_tunnel_from_url(std::string url) - { return nullptr; } + virtual void prepare_file_transfer(const FileTransferRequest&, FileTransferCallbacks cb) + { + if (cb.file_transfer_error) + cb.file_transfer_error(); + } - /** - * Build a file-transfer job (media-ability query, upload, ...) to run on a tunnel - * from create_file_transfer_tunnel()/create_file_transfer_tunnel_from_url(). Same - * inert-default reasoning as the tunnel factories above. - */ - virtual std::unique_ptr create_file_transfer_job(std::string params_json) - { return nullptr; } + /** Query the destinations available for the prepared transfer session. */ + virtual void get_file_destinations(FileTransferCallbacks cb) + { + if (cb.file_transfer_error) + cb.file_transfer_error(); + } + + /** Upload a file using the prepared transfer session. */ + virtual void upload_file(const std::string&, const std::string&, const std::string&, FileTransferCallbacks cb) + { + if (cb.file_transfer_error) + cb.file_transfer_error(); + } + + /** Cancel the current file-transfer operation and release its resources. */ + virtual void cancel_file_transfer() {} }; } // namespace Slic3r From 6558c52849c92374c8ffd0f666a4aca302d76a65 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Mon, 17 Aug 2026 14:19:41 +0800 Subject: [PATCH 11/15] revert file transfer abstraction --- src/slic3r/GUI/Jobs/PrintJob.cpp | 56 +++- src/slic3r/GUI/PartSkipDialog.hpp | 3 +- src/slic3r/GUI/SendToPrinter.cpp | 322 ++++++++++++------- src/slic3r/GUI/SendToPrinter.hpp | 8 +- src/slic3r/Utils/BBLPrinterAgent.cpp | 407 +------------------------ src/slic3r/Utils/BBLPrinterAgent.hpp | 90 ------ src/slic3r/Utils/FileTransferUtils.cpp | 191 ++++++++++++ src/slic3r/Utils/FileTransferUtils.hpp | 110 ++++++- src/slic3r/Utils/IPrinterAgent.hpp | 121 -------- 9 files changed, 556 insertions(+), 752 deletions(-) diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index 59cc7912ad..79bdecc1dd 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -12,7 +12,9 @@ #include "slic3r/GUI/DeviceCore/DevManager.h" #include "slic3r/GUI/DeviceCore/DevUtil.h" -#include "IPrinterAgent.hpp" +#include "slic3r/Utils/FileTransferUtils.hpp" +#include "slic3r/Utils/BBLNetworkPlugin.hpp" +#include "NetworkAgent.hpp" namespace Slic3r { namespace GUI { @@ -206,6 +208,51 @@ void PrintJob::process(Ctl &ctl) params.username = m_agent->default_lan_username(); params.password = m_access_code; + // check access code and ip address + if (this->connection_type == "lan" && m_print_type == "from_normal") { + bool emmc_ok = false; + bool ftp_ok = false; + if (could_emmc_print) { + std::string devIP = m_dev_ip; + std::string accessCode = m_access_code; + std::string url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + try { + std::unique_ptr tunnel = std::make_unique(module(), url); + emmc_ok = tunnel->sync_start_connect(); + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(warning) << "eMMC tunnel unavailable, falling back to FTP: " << e.what(); + emmc_ok = false; + } + } + { + params.dev_id = m_dev_id; + params.project_name = "verify_job"; + params.filename = job_data._temp_path.string(); + params.connection_type = this->connection_type; + + result = m_agent->start_send_gcode_to_sdcard(params, nullptr, nullptr, nullptr); + + ftp_ok = result == 0; + } + if (!emmc_ok && !ftp_ok) { + bool legacy_mode = BBLNetworkPlugin::instance().use_legacy_network(); + BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed:" + << " emmc_ok=" << emmc_ok + << ", ftp_ok=" << ftp_ok + << ", ftp_result=" << result + << ", dev_ip=" << m_dev_ip + << ", dev_id=" << m_dev_id + << ", password_length=" << m_access_code.size() + << ", legacy_mode=" << (legacy_mode ? "true" : "false"); + m_enter_ip_address_fun_fail(); + m_job_finished = true; + return; + } + + params.project_name = ""; + params.filename = ""; + } + params.dev_id = m_dev_id; params.ftp_folder = m_ftp_folder; params.filename = job_data._3mf_path.string(); @@ -596,13 +643,6 @@ void PrintJob::process(Ctl &ctl) } if (result < 0) { - if (result == ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED) { - if (m_enter_ip_address_fun_fail) - m_enter_ip_address_fun_fail(); - m_job_finished = true; - return; - } - curr_percent = -1; // The printer is still fetching its encryption flag (a transient state), so ask the diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index 4137a476fc..d204ba2043 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -14,6 +14,7 @@ #include #include +#include "NetworkAgent.hpp" #include "Widgets/Label.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/Button.hpp" @@ -117,7 +118,7 @@ private: std::map m_parts_name; std::vector m_partskip_ids; - enum URL_STATE m_url_state = URL_STATE::URL_TCP; + URL_STATE m_url_state = URL_STATE::URL_TCP; PartsInfo GetPartsInfo(); bool is_drag_mode(); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index 80167850e6..b970447cd8 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -1,7 +1,6 @@ #include "SendToPrinter.hpp" #include "I18N.hpp" -#include "IPrinterAgent.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Thread.hpp" #include "GUI.hpp" @@ -26,6 +25,7 @@ #include "DeviceCore/DevManager.h" #include "DeviceCore/DevStorage.h" +#include "slic3r/Utils/FileTransferUtils.hpp" namespace Slic3r { @@ -845,8 +845,11 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) m_task_timer.reset(); } - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + if (m_filetransfer_uploadfile_job) { + m_filetransfer_uploadfile_job->cancel(); + m_filetransfer_uploadfile_job.reset(); + m_filetransfer_uploadfile_job = nullptr; + } m_is_canceled = true; wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL); @@ -871,10 +874,10 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) return; bool cancelled = false; wxString msg = _L("Preparing print job"); - m_status_bar->update_status(msg, cancelled, 10, true); - m_export_3mf_cancel = cancel = cancelled; - }); - } + m_status_bar->update_status(msg, cancelled, 10, true); + m_export_3mf_cancel = cancel = cancelled; + }); + } if (m_is_canceled || m_export_3mf_cancel) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": send progress 10"; @@ -938,8 +941,7 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) this->Bind(wxEVT_TIMER, [this](auto e){ show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + m_filetransfer_uploadfile_job->cancel(); update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true); },m_task_timer->GetId()); m_task_timer->StartOnce(timeout_period); @@ -1295,8 +1297,10 @@ void SendToPrinterDialog::update_show_status() else m_if_has_sdcard = true; - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + if (m_filetransfer_tunnel) { + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + } GetConnection(); } @@ -1655,6 +1659,7 @@ bool SendToPrinterDialog::Show(bool show) return DPIDialog::Show(show); } +extern wxString hide_passwd(wxString url, std::vector const &passwords); extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); void SendToPrinterDialog::GetConnection() @@ -1665,80 +1670,102 @@ void SendToPrinterDialog::GetConnection() if (obj == nullptr) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : obj is empty"; m_connection_status = ConnectionStatus::NOT_START; - return; } int remote_proto = obj->get_file_remote(); if (!remote_proto) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : remote_proto is not support"; m_connection_status = ConnectionStatus::NOT_START; - return; } if (obj->is_camera_busy_off()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : camera is busy"; m_connection_status = ConnectionStatus::NOT_START; - return; } - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; + NetworkAgent *agent = wxGetApp().getAgent(); + std::string agent_version = agent ? agent->get_version() : ""; + std::string dev_ver = obj->get_ota_version(); + std::string dev_id = obj->get_dev_id(); + + if (m_url_timer && m_url_timer->IsRunning()) + { + m_url_timer->Stop(); + } + + m_url_timer.reset(new wxTimer()); + m_url_timer->SetOwner(this); + this->Bind( + wxEVT_TIMER, + [this](wxTimerEvent &e) { + BOOST_LOG_TRIVIAL(info) << "Timer callback triggered!"; + m_connection_status = ConnectionStatus::CONNECTION_FAILED; + m_ftp_try_connect = true; + if (m_filetransfer_tunnel) + { + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + } + + }, + m_url_timer->GetId()); + m_url_timer->StartOnce(8000); + + if (agent) { + if (m_tcp_try_connect) { + std::string devIP = obj->get_dev_ip(); + std::string accessCode = obj->get_access_code(); + std::string url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tcp"; + m_filetransfer_tunnel = std::make_unique(module(), url); + m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { + CallAfter([this, is_success, err_code, error_msg]() { + OnConnection(is_success, err_code, error_msg); + }); + }); + m_filetransfer_tunnel->start_connect(); + } + else if (m_tutk_try_connect) + { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], [this, m = dev_id](CameraURLResult result) { + std::string url = std::move(result.url); + + if (m_url_timer && m_url_timer->IsRunning()) + { + m_url_timer->Stop(); + } + + #if !BBL_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "SendToPrinter::camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + #endif + + + if (result.is_success) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tutk"; + m_filetransfer_tunnel = std::make_unique(module(), url); + m_filetransfer_tunnel->on_connection([this](bool is_success, int err_code, std::string error_msg) { + CallAfter([this, is_success, err_code, error_msg]() { OnConnection(is_success, err_code, error_msg); }); + }); + m_filetransfer_tunnel->start_connect(); + } + else + { + std::string res = ""; + if (!url.empty() && boost::ends_with(url, "]")) + { + size_t n = url.find_last_of('['); + if (n != std::string::npos) + res = url.substr(n + 1, url.length() - n - 2); + } + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : Tutk url error: ress = " << res; + } + }, wxGetApp().get_printer_cloud_provider(), + CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + } } - - if (m_url_timer && m_url_timer->IsRunning()) { - m_url_timer->Stop(); - } - - m_url_timer.reset(new wxTimer()); - m_url_timer->SetOwner(this); - this->Bind( - wxEVT_TIMER, - [this](wxTimerEvent& e) { - BOOST_LOG_TRIVIAL(info) << "Timer callback triggered!"; - m_connection_status = ConnectionStatus::CONNECTION_FAILED; - m_ftp_try_connect = true; - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); - }, - m_url_timer->GetId()); - m_url_timer->StartOnce(8000); - - IPrinterAgent::FileTransferRequest request; - request.device_id = obj->get_dev_id(); - request.device_ip = obj->get_dev_ip(); - request.access_code = obj->get_access_code(); - request.network_version = agent->get_version(); - request.device_version = obj->get_ota_version(); - request.refresh_url = boost::lexical_cast(&refresh_agora_url); - request.client_id = wxGetApp().app_config->get("slicer_uuid"); - request.client_version = SLIC3R_VERSION; - request.lan_mode = obj->connection_type() == "lan"; - - IPrinterAgent::FileTransferCallbacks callbacks; - callbacks.on_connection = [this](bool is_success, int error_code, std::string error_msg) { - CallAfter([this, is_success, error_code, error_msg = std::move(error_msg)] { - OnConnection(is_success, error_code, std::move(error_msg)); - }); - }; - callbacks.file_transfer_error = [this] { - CallAfter([this] { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - }); - }; - agent->get_printer_agent()->prepare_file_transfer(request, std::move(callbacks)); -} - -void SendToPrinterDialog::show_file_transfer_error(PrintDialogStatus status, wxString message) -{ - if (m_url_timer && m_url_timer->IsRunning()) - m_url_timer->Stop(); - m_connection_status = ConnectionStatus::CONNECTION_FAILED; - show_status(status); - update_print_status_msg(message, false, true); } void SendToPrinterDialog::OnConnection(bool is_success, int error_code, std::string error_msg) { @@ -1753,13 +1780,43 @@ void SendToPrinterDialog::OnConnection(bool is_success, int error_code, std::str { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Connect failed, error_code is:" << error_code << "error_msg is :" << error_msg; m_connection_status = ConnectionStatus::CONNECTION_FAILED; - m_tcp_try_connect = false; - m_tutk_try_connect = false; - m_ftp_try_connect = true; - show_status(PrintDialogStatus::PrintStatusPublicInitFailed); + ChangeConnectMethod(); + if (!m_tcp_try_connect && !m_tutk_try_connect) { + show_status(PrintDialogStatus::PrintStatusPublicInitFailed); + return; + } + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + GetConnection(); } } +void SendToPrinterDialog::ChangeConnectMethod() +{ + DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + MachineObject *obj = dev->get_my_machine(m_printer_last_select); + if (!obj) return; + + bool is_lan = (obj->connection_type() == "lan"); + + m_tcp_try_connect = false; + + if (is_lan) { + m_ftp_try_connect = true; + m_tutk_try_connect = false; + } else { + if (m_connect_try_times == 0) { + m_ftp_try_connect = false; + m_tutk_try_connect = true; + } else { + m_ftp_try_connect = true; + m_tutk_try_connect = false; + } + } + m_connect_try_times++; +} + void SendToPrinterDialog::ResetConnectMethod() { m_tcp_try_connect = true; @@ -1771,21 +1828,32 @@ void SendToPrinterDialog::ResetConnectMethod() void SendToPrinterDialog::ResetTunnelAndJob() { - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); + if (m_filetransfer_uploadfile_job) + { + m_filetransfer_uploadfile_job->cancel(); + m_filetransfer_uploadfile_job.reset(); + m_filetransfer_uploadfile_job = nullptr; + } + if (m_filetransfer_mediability_job) + { + m_filetransfer_mediability_job->cancel(); + m_filetransfer_mediability_job.reset(); + m_filetransfer_mediability_job = nullptr; + } + if (m_filetransfer_tunnel) + { + m_filetransfer_tunnel.reset(); + m_filetransfer_tunnel = nullptr; + } } void SendToPrinterDialog::CreateMediaAbilityJob() { - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - return; - } - IPrinterAgent::FileTransferCallbacks callbacks; - callbacks.on_destinations = [this](int res, int resp_ec, std::string json_res) { - CallAfter([this, res, resp_ec, json_res = std::move(json_res)] { + nlohmann::json media_ability = {{"cmd_type", 7}}; + m_filetransfer_mediability_job = std::make_unique(module(), std::string(media_ability.dump())); + m_filetransfer_mediability_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { + //this pl + CallAfter([this, res, resp_ec, json_res] { if (res == 0) // 0 is success { show_status(PrintDialogStatus::PrintStatusReadingFinished); @@ -1821,41 +1889,59 @@ void SendToPrinterDialog::CreateMediaAbilityJob() show_status(PrintDialogStatus::PrintStatusPublicInitFailed); update_print_status_msg(ParseErrorCode(resp_ec), false, true); } - }); - }; - callbacks.file_transfer_error = [this] { - CallAfter([this] { - show_file_transfer_error(PrintDialogStatus::PrintStatusNotSupportedSendToSDCard, - _L("The selected printer does not support file transfer.")); - }); - }; - agent->get_printer_agent()->get_file_destinations(std::move(callbacks)); + }); + }); + // Guard against a null transfer tunnel before dereferencing. + if (m_filetransfer_tunnel) { + m_filetransfer_mediability_job->start_on(*m_filetransfer_tunnel); + } else { + BOOST_LOG_TRIVIAL(info) << "CreateMediaAbilityJob: file transfer tunnel is null"; + } } void SendToPrinterDialog::CreateUploadFileJob(const std::string &path, const std::string &name) { - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent || !agent->get_printer_agent()) { - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, - _L("The selected printer does not support file transfer.")); - return; + nlohmann::json upload_params = { + {"cmd_type", 5}, + }; + upload_params["dest_storage"] = m_selected_storage; + upload_params["dest_name"] = name; // filenme no path + upload_params["file_path"] = path; + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Begin CreateUploadFileJob"; + m_filetransfer_uploadfile_job = std::make_unique(module(), std::string(upload_params.dump())); + m_filetransfer_uploadfile_job->on_result([this](int res, int resp_ec, std::string json_res, std::vector bin_res) { // + CallAfter([this, res, resp_ec, json_res, bin_res] { + UploadFileRessultCallback(res, resp_ec,json_res, bin_res); + }); + }); + + m_filetransfer_uploadfile_job->on_msg([this](int kind, std::string json_res) { + CallAfter([this, kind, json_res] { + if (kind == 0) { + try + { + auto js = nlohmann::json::parse(json_res); + int progress = js["progress"].get(); + UploadFileProgressCallback(progress); + } + catch (const nlohmann::json::exception& e) + { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what(); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << "parse_json failed! "; + } + } + }); + }); + // Guard against a null transfer tunnel before dereferencing. + if (m_filetransfer_tunnel) { + m_filetransfer_uploadfile_job->start_on(*m_filetransfer_tunnel); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": file transfer tunnel is null"; } - IPrinterAgent::FileTransferCallbacks callbacks; - callbacks.on_progress = [this](int progress) { - CallAfter([this, progress] { UploadFileProgressCallback(progress); }); - }; - callbacks.on_result = [this](int res, int resp_ec, std::string json_res, std::vector bin_res) { - CallAfter([this, res, resp_ec, json_res = std::move(json_res), bin_res = std::move(bin_res)] { - UploadFileRessultCallback(res, resp_ec, std::move(json_res), std::move(bin_res)); - }); - }; - callbacks.file_transfer_error = [this] { - CallAfter([this] { - show_file_transfer_error(PrintDialogStatus::PrintStatusPublicUploadFiled, - _L("The selected printer does not support file transfer.")); - }); - }; - agent->get_printer_agent()->upload_file(path, name, m_selected_storage, std::move(callbacks)); } void SendToPrinterDialog::UploadFileProgressCallback(int progress) @@ -1874,8 +1960,6 @@ void SendToPrinterDialog::UploadFileProgressCallback(int progress) wxEVT_TIMER, [this](auto e) { show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); - if (auto agent = wxGetApp().getAgent(); agent && agent->get_printer_agent()) - agent->get_printer_agent()->cancel_file_transfer(); update_print_status_msg( _L("File upload timed out. Please check if the firmware version supports this operation or verify if the printer is functioning properly."), false, true); }, @@ -1904,6 +1988,8 @@ void SendToPrinterDialog::UploadFileRessultCallback(int res, int resp_ec, std::s update_print_status_msg(ParseErrorCode(resp_ec), false, true); else update_print_status_msg(_L("Sending failed, please try again!"), false, true); + m_filetransfer_uploadfile_job.reset(); + m_filetransfer_uploadfile_job = nullptr; } } diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp index 2d0073ce59..14493a1f20 100644 --- a/src/slic3r/GUI/SendToPrinter.hpp +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -24,7 +24,6 @@ #include #include -#include "IPrinterAgent.hpp" #include "SelectMachine.hpp" #include "GUI_Utils.hpp" #include "wxExtensions.hpp" @@ -44,6 +43,8 @@ namespace Slic3r { +class FileTransferTunnel; +class FileTransferJob; namespace GUI { @@ -170,6 +171,9 @@ private: enum ConnectionStatus { NOT_START, CONNECTING, CONNECTED, CONNECTION_FAILED, DISCONNECTED }; ConnectionStatus m_connection_status{ConnectionStatus::NOT_START}; + std::unique_ptr m_filetransfer_tunnel; + std::unique_ptr m_filetransfer_mediability_job; + std::unique_ptr m_filetransfer_uploadfile_job; wxDateTime m_last_refresh_time; public: @@ -219,10 +223,10 @@ public: private: void ResetConnectMethod(); void ResetTunnelAndJob(); - void show_file_transfer_error(PrintDialogStatus status, wxString message); void OnConnection(bool is_success, int error_code, std::string error_msg); void CreateMediaAbilityJob(); void CreateUploadFileJob(const std::string &path, const std::string &name); + void ChangeConnectMethod(); void UploadFileProgressCallback(int progress); void UploadFileRessultCallback(int res, int resp_ec, std::string json_res, std::vector bin_res); void Reset(); diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 3b81a6fbc5..c1f5b5666a 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -1,6 +1,5 @@ #include "BBLPrinterAgent.hpp" #include "BBLNetworkPlugin.hpp" -#include "FileTransferUtils.hpp" #include "IPrinterAgent.hpp" #include "NetworkAgentFactory.hpp" #include "NetworkAgent.hpp" @@ -11,395 +10,13 @@ #include #include #include -#include namespace Slic3r { -// ============================================================================ -// File Transfer (Bambu eMMC tunnel ABI) -// ============================================================================ - -BBLFileTransferTunnel::BBLFileTransferTunnel(const std::string &url) : IFileTransferTunnel(url) -{ - FileTransferModule &m = module(); - m_ = &m; - // Guard against missing symbols in older Bambu networking plugins. - // These symbols were added in a newer plugin ABI; if the installed - // plugin predates them, ft_tunnel_create/ft_tunnel_set_status_cb - // will be null and calling them crashes. - if (!m_->ft_tunnel_create || !m_->ft_tunnel_set_status_cb) { - throw std::runtime_error("Bambu networking plugin is too old: missing ft_tunnel_* symbols. " - "Please update the networking plugin."); - } - FT_TunnelHandle *h{}; - if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { - throw std::runtime_error("ft_tunnel_create failed"); - } - h_ = h; - - // C API: ft_status_cb(void* user, int old_status, int new_status, int err, const char* msg) - auto tramp = [](void *user, int old_status, int new_status, int err_code, const char *msg) noexcept { - auto *self = reinterpret_cast(user); - self->status_ = new_status; - if (!self->status_cb_) return; - try { - self->status_cb_(old_status, new_status, err_code, std::string(msg ? msg : "")); - } catch (...) {} - }; - if (m_->ft_tunnel_set_status_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_set_status_cb failed"); } -} - -void BBLFileTransferTunnel::start_connect() -{ - // C API: ft_conn_cb(void* user, int ok, int err, const char* msg) - auto tramp = [](void *user, int ok, int ec, const char *msg) noexcept { - auto *pcb = reinterpret_cast(user); - if (!pcb) return; - try { - (*pcb)(ok == 0, ec, std::string(msg ? msg : "")); - } catch (...) {} - }; - if (m_->ft_tunnel_start_connect(h_, tramp, &conn_cb_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_connect failed"); } -} - -bool BBLFileTransferTunnel::sync_start_connect() -{ - return m_->ft_tunnel_sync_connect(h_) == FT_OK; -} - -void BBLFileTransferTunnel::shutdown() -{ - if (m_->ft_tunnel_shutdown) (void) m_->ft_tunnel_shutdown(h_); -} - -BBLFileTransferJob::BBLFileTransferJob(const std::string ¶ms_json) : IFileTransferJob(params_json) -{ - m_ = &module(); - FT_JobHandle *h{}; - if (m_->ft_job_create(params_json.c_str(), &h) != 0 || !h) { - throw std::runtime_error("ft_job_create failed"); - } - h_ = h; - - // C API: ft_job_result_cb(void* user, int tunnel_err, ft_job_result result) - auto tramp = [](void *user, ft_job_result r) noexcept { - auto *self = reinterpret_cast(user); - if (!self) return; - - try { - self->finished_ = true; - self->solve_result(r); - if (self->result_cb_) self->result_cb_(self->res_, self->resp_ec_, self->res_json_, self->res_bin_); - } catch (...) { - // swallow - } - - try { - if (auto *mod = self ? self->m_ : nullptr) { - if (mod->ft_job_result_destroy) - mod->ft_job_result_destroy(&r); - else if (mod->ft_free) { - if (r.json) mod->ft_free((void *) r.json); - if (r.bin) mod->ft_free((void *) r.bin); - } - } - } catch (...) {} - }; - - if (m_->ft_job_set_result_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_result_cb failed"); } -} - -bool BBLFileTransferJob::get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) -{ - if (!h_) throw std::runtime_error("job handle invalid"); - ft_job_result result; - if (m_->ft_job_get_result(h_, timeout_ms, &result) == ft_err::FT_EXCEPTION) return false; - solve_result(result); - m_->ft_job_result_destroy(&result); - ec = res_; - resp_ec = res_; - json = res_json_; - bin = res_bin_; - return true; -} - -void BBLFileTransferJob::start_on(IFileTransferTunnel &t) -{ - if (!h_) throw std::runtime_error("job handle invalid"); - auto *handle = reinterpret_cast(t.native()); - if (m_->ft_tunnel_start_job(handle, h_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_job failed"); } -} - -void BBLFileTransferJob::on_msg(MsgCb cb) -{ - IFileTransferJob::on_msg(std::move(cb)); - if (!h_) return; - - // C API: ft_job_msg_cb(void* user, ft_job_msg msg) - auto tramp = [](void *user, ft_job_msg m) noexcept { - auto *self = reinterpret_cast(user); - if (!self) return; - try { - if (self->msg_cb_) { self->msg_cb_(m.kind, std::string(m.json ? m.json : "")); } - } catch (...) {} - - try { - if (auto *mod = self->m_) { - if (mod->ft_job_msg_destroy) - mod->ft_job_msg_destroy(&m); - else if (mod->ft_free && m.json) - mod->ft_free((void *) m.json); - } - } catch (...) {} - }; - - if (m_->ft_job_set_msg_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_msg_cb failed"); } -} - -bool BBLFileTransferJob::try_get_msg(int &kind, std::string &json) -{ - if (!h_) return false; - ft_job_msg m{}; - int rc = m_->ft_job_try_get_msg(h_, &m); - if (rc != 0) return false; - - kind = m.kind; - json.assign(m.json ? m.json : ""); - - if (m_->ft_job_msg_destroy) - m_->ft_job_msg_destroy(&m); - else if (m_->ft_free && m.json) - m_->ft_free((void *) m.json); - - return true; -} - -bool BBLFileTransferJob::get_msg(uint32_t timeout_ms, int &kind, std::string &json) -{ - if (!h_) return false; - ft_job_msg m{}; - int rc = m_->ft_job_get_msg(h_, timeout_ms, &m); - if (rc != 0) return false; - - kind = m.kind; - json.assign(m.json ? m.json : ""); - - if (m_->ft_job_msg_destroy) - m_->ft_job_msg_destroy(&m); - else if (m_->ft_free && m.json) - m_->ft_free((void *) m.json); - - return true; -} - -void BBLFileTransferJob::solve_result(ft_job_result result) -{ - res_ = result.ec; - resp_ec_ = result.resp_ec; - - res_bin_.clear(); - if (result.bin && result.bin_size) res_bin_.assign(reinterpret_cast(result.bin), - reinterpret_cast(result.bin) + result.bin_size); - res_json_.assign(result.json ? result.json : ""); -} - BBLPrinterAgent::BBLPrinterAgent() = default; BBLPrinterAgent::~BBLPrinterAgent() = default; -void BBLPrinterAgent::prepare_file_transfer(const FileTransferRequest& request, FileTransferCallbacks cb) -{ - cancel_file_transfer(); - m_file_transfer_request = request; - m_file_transfer_callbacks = std::move(cb); - m_file_transfer_tcp = true; - m_file_transfer_try_count = 0; - start_file_transfer_attempt(++m_file_transfer_generation); -} - -void BBLPrinterAgent::start_file_transfer_attempt(uint64_t generation) -{ - FileTransferURLParams params; - params.url_state = m_file_transfer_tcp ? URL_TCP : URL_TUTK; - params.ip_address = m_file_transfer_request.device_ip; - params.username = default_lan_username(); - params.password = m_file_transfer_request.access_code; - params.device_id = m_file_transfer_request.device_id; - params.network_version = m_file_transfer_request.network_version; - params.device_version = m_file_transfer_request.device_version; - params.refresh_url = m_file_transfer_request.refresh_url; - params.client_id = m_file_transfer_request.client_id; - params.client_version = m_file_transfer_request.client_version; - - auto handle_url = [this, generation](FileTransferURLResult result) { - if (generation != m_file_transfer_generation) - return; - if (!result.is_success) { - handle_file_transfer_connection(generation, false, result.error_code, "file-transfer URL unavailable"); - return; - } - - try { - m_file_transfer_tunnel = std::make_unique(result.url); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create file-transfer tunnel: " << e.what(); - m_file_transfer_tunnel.reset(); - } - - if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { - handle_file_transfer_connection(generation, false, -1, "file-transfer tunnel unavailable"); - return; - } - - m_file_transfer_tunnel->on_connection([this, generation](bool is_success, int error_code, std::string error_msg) { - handle_file_transfer_connection(generation, is_success, error_code, std::move(error_msg)); - }); - m_file_transfer_tunnel->start_connect(); - }; - - if (m_file_transfer_tcp) { - get_file_transfer_url(m_file_transfer_request.device_id, std::move(handle_url), params); - return; - } - - if (!m_cloud_agent) { - handle_file_transfer_connection(generation, false, -1, "cloud file-transfer URL unavailable"); - return; - } - - const std::string protocols = "\"tutk\""; - m_cloud_agent->get_camera_url( - m_file_transfer_request.device_id + "|" + m_file_transfer_request.device_version + "|" + protocols, - [handle_url = std::move(handle_url)](CameraURLResult result) mutable { - FileTransferURLResult transfer_result; - transfer_result.is_success = result.is_success; - transfer_result.url = std::move(result.url); - transfer_result.error_code = result.error_code; - handle_url(std::move(transfer_result)); - }, - CameraURLParams{ - "", "", "", LVL_None, - m_file_transfer_request.device_id, - m_file_transfer_request.network_version, - m_file_transfer_request.device_version, - m_file_transfer_request.refresh_url, - m_file_transfer_request.client_id, - m_file_transfer_request.client_version, - true - }); -} - -void BBLPrinterAgent::handle_file_transfer_connection(uint64_t generation, bool is_success, int error_code, std::string error_msg) -{ - if (generation != m_file_transfer_generation) - return; - if (is_success) { - if (m_file_transfer_callbacks.on_connection) - m_file_transfer_callbacks.on_connection(true, error_code, std::move(error_msg)); - return; - } - - // Preserve the existing dialog fallback order: TCP, then TUTK for cloud - // printers, and finally the legacy FTP path handled by SendJob. - m_file_transfer_tunnel.reset(); - if (!m_file_transfer_request.lan_mode && m_file_transfer_tcp && m_file_transfer_try_count == 0) { - m_file_transfer_tcp = false; - ++m_file_transfer_try_count; - start_file_transfer_attempt(generation); - return; - } - - if (m_file_transfer_callbacks.on_connection) - m_file_transfer_callbacks.on_connection(false, error_code, std::move(error_msg)); -} - -void BBLPrinterAgent::get_file_destinations(FileTransferCallbacks cb) -{ - if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - nlohmann::json params = {{"cmd_type", 7}}; - try { - m_file_transfer_job = std::make_unique(params.dump()); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create media-ability job: " << e.what(); - m_file_transfer_job.reset(); - } - - if (!m_file_transfer_job || !m_file_transfer_job->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - m_file_transfer_job->on_result([cb = std::move(cb)](int result, int response_error, std::string json_result, - std::vector) { - if (cb.on_destinations) - cb.on_destinations(result, response_error, std::move(json_result)); - }); - m_file_transfer_job->start_on(*m_file_transfer_tunnel); -} - -void BBLPrinterAgent::upload_file(const std::string& path, const std::string& name, const std::string& destination, - FileTransferCallbacks cb) -{ - if (!m_file_transfer_tunnel || !m_file_transfer_tunnel->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - nlohmann::json params = { - {"cmd_type", 5}, - {"dest_storage", destination}, - {"dest_name", name}, - {"file_path", path} - }; - - try { - m_file_transfer_job = std::make_unique(params.dump()); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to create upload job: " << e.what(); - m_file_transfer_job.reset(); - } - - if (!m_file_transfer_job || !m_file_transfer_job->check_valid()) { - if (cb.file_transfer_error) - cb.file_transfer_error(); - return; - } - - auto callbacks = std::make_shared(std::move(cb)); - m_file_transfer_job->on_result([callbacks](int result, int response_error, std::string json_result, - std::vector binary_result) { - if (callbacks->on_result) - callbacks->on_result(result, response_error, std::move(json_result), std::move(binary_result)); - }); - m_file_transfer_job->on_msg([callbacks](int kind, std::string json_result) { - if (kind == 0 && callbacks->on_progress) { - try { - callbacks->on_progress(nlohmann::json::parse(json_result).at("progress").get()); - } catch (...) { - BOOST_LOG_TRIVIAL(error) << "BBLPrinterAgent: failed to parse upload progress"; - } - } - }); - m_file_transfer_job->start_on(*m_file_transfer_tunnel); -} - -void BBLPrinterAgent::cancel_file_transfer() -{ - ++m_file_transfer_generation; - if (m_file_transfer_job) - m_file_transfer_job->cancel(); - m_file_transfer_job.reset(); - m_file_transfer_tunnel.reset(); - m_file_transfer_callbacks = {}; -} - void BBLPrinterAgent::set_cloud_agent(std::shared_ptr cloud) { m_cloud_agent = cloud; @@ -702,10 +319,7 @@ int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::function( @@ -1001,16 +606,6 @@ int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStat int BBLPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { - if (params.connection_type == "lan" && params.print_type == "from_normal") { - const int verify_result = verify_local_print_access(params); - if (verify_result != 0) { - BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed: result=" << verify_result - << ", dev_ip=" << params.dev_ip << ", dev_id=" << params.dev_id - << ", password_length=" << params.password.size(); - return ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED; - } - } - return dispatch_start( BBLNetworkPlugin::instance().get_start_local_print(), params, update_fn, cancel_fn); } diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index 685f422228..94f7ea8466 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -3,83 +3,12 @@ #include "IPrinterAgent.hpp" #include "ICloudServiceAgent.hpp" -#include "FileTransferUtils.hpp" #include #include #include namespace Slic3r { -/** - * BBLFileTransferTunnel - Bambu eMMC tunnel, backed by the Bambu network - * plugin's ft_tunnel_* ABI (see FileTransferUtils.hpp). Only BBLPrinterAgent - * constructs these; callers only ever see them through IFileTransferTunnel. - */ -class BBLFileTransferTunnel : public IFileTransferTunnel -{ -public: - BBLFileTransferTunnel(const std::string &url); - ~BBLFileTransferTunnel() override { reset(); } - - void start_connect() override; - bool sync_start_connect() override; - void shutdown() override; - bool check_valid() const override { return h_ != nullptr; } - void *native() const noexcept override { return h_; } - -private: - void reset() noexcept - { - if (h_) { - m_->ft_tunnel_release(h_); - h_ = nullptr; - } - } - - FileTransferModule *m_{}; - FT_TunnelHandle *h_{}; -}; - -/** - * BBLFileTransferJob - a single ft_job_* operation (media-ability query, - * file upload, ...) run on a BBLFileTransferTunnel. Same ABI-wrapper role - * as BBLFileTransferTunnel; only BBLPrinterAgent constructs these. - */ -class BBLFileTransferJob : public IFileTransferJob -{ -public: - explicit BBLFileTransferJob(const std::string ¶ms_json); - ~BBLFileTransferJob() override { reset(); } - - bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) override; - void start_on(IFileTransferTunnel &t) override; - // why: unlike on_result() (fires from a trampoline registered once in the ctor), - // ft_job_set_msg_cb is only wired up here, lazily, on first real subscriber - - // that ABI call has to happen in the concrete class, not the vendor-neutral base. - void on_msg(MsgCb cb) override; - bool try_get_msg(int &kind, std::string &json) override; - bool get_msg(uint32_t timeout_ms, int &kind, std::string &json) override; - void *native() const noexcept override { return h_; } - bool check_valid() const override { return h_ != nullptr; } - void cancel() override - { - if (m_->ft_job_cancel && h_) m_->ft_job_cancel(h_); - } - -private: - void reset() noexcept - { - if (h_) { - m_->ft_job_release(h_); - h_ = nullptr; - } - } - void solve_result(ft_job_result result); - - FileTransferModule *m_{}; - FT_JobHandle *h_{}; -}; - /** * BBLPrinterAgent - BBL DLL wrapper implementation of IPrinterAgent. * @@ -175,30 +104,11 @@ public: int set_queue_on_main_fn(QueueOnMainFn fn) override; FilamentSyncMode get_filament_sync_mode() const override; - void prepare_file_transfer(const FileTransferRequest& request, FileTransferCallbacks cb) override; - void get_file_destinations(FileTransferCallbacks cb) override; - void upload_file(const std::string& path, const std::string& name, const std::string& destination, - FileTransferCallbacks cb) override; - void cancel_file_transfer() override; - - private: - int verify_local_print_access(PrintParams params); - // why: the lan/cloud DECISION stays machine-side; keep this mechanical branch in sync with publish_json. int publish(const std::string& dev_id, const nlohmann::json& j, bool lan_mode); std::shared_ptr m_cloud_agent; - std::unique_ptr m_file_transfer_tunnel; - std::unique_ptr m_file_transfer_job; - FileTransferCallbacks m_file_transfer_callbacks; - FileTransferRequest m_file_transfer_request; - bool m_file_transfer_tcp{true}; - int m_file_transfer_try_count{0}; - uint64_t m_file_transfer_generation{0}; - - void start_file_transfer_attempt(uint64_t generation); - void handle_file_transfer_connection(uint64_t generation, bool is_success, int error_code, std::string error_msg); }; } // namespace Slic3r diff --git a/src/slic3r/Utils/FileTransferUtils.cpp b/src/slic3r/Utils/FileTransferUtils.cpp index 8601f6712e..822ccdf09d 100644 --- a/src/slic3r/Utils/FileTransferUtils.cpp +++ b/src/slic3r/Utils/FileTransferUtils.cpp @@ -1,4 +1,8 @@ +#include +#include #include "FileTransferUtils.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/DeviceCore/DevManager.h" namespace Slic3r { @@ -33,4 +37,191 @@ FileTransferModule::FileTransferModule(ModuleHandle networking_module, int requi ft_job_get_msg = sym_lookup(networking_, "ft_job_get_msg"); } +FileTransferTunnel::FileTransferTunnel(FileTransferModule &m, const std::string &url) : m_(&m) +{ + // Guard against missing symbols in older Bambu networking plugins. + // These symbols were added in a newer plugin ABI; if the installed + // plugin predates them, ft_tunnel_create/ft_tunnel_set_status_cb + // will be null and calling them crashes. + if (!m_->ft_tunnel_create || !m_->ft_tunnel_set_status_cb) { + throw std::runtime_error("Bambu networking plugin is too old: missing ft_tunnel_* symbols. " + "Please update the networking plugin."); + } + FT_TunnelHandle *h{}; + if (m_->ft_tunnel_create(url.c_str(), &h) != 0 || !h) { + throw std::runtime_error("ft_tunnel_create failed"); + } + h_ = h; + + // C API: ft_status_cb(void* user, int old_status, int new_status, int err, const char* msg) + auto tramp = [](void *user, int old_status, int new_status, int err_code, const char *msg) noexcept { + auto *self = reinterpret_cast(user); + self->status_ = new_status; + if (!self->status_cb_) return; + try { + self->status_cb_(old_status, new_status, err_code, std::string(msg ? msg : "")); + } catch (...) {} + }; + if (m_->ft_tunnel_set_status_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_set_status_cb failed"); } +} + +void FileTransferTunnel::start_connect() +{ + // C API: ft_conn_cb(void* user, int ok, int err, const char* msg) + auto tramp = [](void *user, int ok, int ec, const char *msg) noexcept { + auto *pcb = reinterpret_cast(user); + if (!pcb) return; + try { + (*pcb)(ok == 0, ec, std::string(msg ? msg : "")); + } catch (...) {} + }; + if (m_->ft_tunnel_start_connect(h_, tramp, &conn_cb_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_connect failed"); } +} + +bool FileTransferTunnel::sync_start_connect() +{ + return m_->ft_tunnel_sync_connect(h_) == FT_OK; +} + +void FileTransferTunnel::on_connection(ConnectionCb cb) { conn_cb_ = std::move(cb); } +void FileTransferTunnel::on_status(TunnelStatusCb cb) { status_cb_ = std::move(cb); } + +void FileTransferTunnel::shutdown() +{ + if (m_->ft_tunnel_shutdown) (void) m_->ft_tunnel_shutdown(h_); +} + +FileTransferJob::FileTransferJob(FileTransferModule &m, const std::string ¶ms_json) : m_(&m) +{ + FT_JobHandle *h{}; + if (m_->ft_job_create(params_json.c_str(), &h) != 0 || !h) { + + } + h_ = h; + + // C API: ft_job_result_cb(void* user, int tunnel_err, ft_job_result result) + auto tramp = [](void *user, ft_job_result r) noexcept { + auto *self = reinterpret_cast(user); + if (!self) return; + + try { + self->finished_ = true; + self->solve_result(r); + + if (self->result_cb_) self->result_cb_(self->res_, self->resp_ec_, self->res_json_, self->res_bin_); + self->m_->ft_job_result_destroy(&r); + } catch (...) { + // swallow + } + + try { + if (auto *mod = self ? self->m_ : nullptr) { + if (mod->ft_job_result_destroy) + mod->ft_job_result_destroy(&r); + else if (mod->ft_free) { + if (r.json) mod->ft_free((void *) r.json); + if (r.bin) mod->ft_free((void *) r.bin); + } + } + } catch (...) {} + }; + + if (m_->ft_job_set_result_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_result_cb failed"); } +} + +void FileTransferJob::on_result(ResultCb cb) { result_cb_ = std::move(cb); } + +bool FileTransferJob::get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) +{ + if (!h_) throw std::runtime_error("job handle invalid"); + ft_job_result result; + if (m_->ft_job_get_result(h_, timeout_ms, &result) == ft_err::FT_EXCEPTION) return false; + solve_result(result); + m_->ft_job_result_destroy(&result); + ec = res_; + resp_ec = res_; + json = res_json_; + bin = res_bin_; + return true; +} + +void FileTransferJob::start_on(FileTransferTunnel &t) +{ + if (!h_) throw std::runtime_error("job handle invalid"); + if (m_->ft_tunnel_start_job(t.native(), h_) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_tunnel_start_job failed"); } +} + +void FileTransferJob::on_msg(MsgCb cb) +{ + msg_cb_ = std::move(cb); + if (!h_) return; + + // C API: ft_job_msg_cb(void* user, ft_job_msg msg) + auto tramp = [](void *user, ft_job_msg m) noexcept { + auto *self = reinterpret_cast(user); + if (!self) return; + try { + if (self->msg_cb_) { self->msg_cb_(m.kind, std::string(m.json ? m.json : "")); } + } catch (...) {} + + try { + if (auto *mod = self->m_) { + if (mod->ft_job_msg_destroy) + mod->ft_job_msg_destroy(&m); + else if (mod->ft_free && m.json) + mod->ft_free((void *) m.json); + } + } catch (...) {} + }; + + if (m_->ft_job_set_msg_cb(h_, tramp, this) == ft_err::FT_EXCEPTION) { throw std::runtime_error("ft_job_set_msg_cb failed"); } +} + +bool FileTransferJob::try_get_msg(int &kind, std::string &json) +{ + if (!h_) return false; + ft_job_msg m{}; + int rc = m_->ft_job_try_get_msg(h_, &m); + if (rc != 0) return false; + + kind = m.kind; + json.assign(m.json ? m.json : ""); + + if (m_->ft_job_msg_destroy) + m_->ft_job_msg_destroy(&m); + else if (m_->ft_free && m.json) + m_->ft_free((void *) m.json); + + return true; +} + +bool FileTransferJob::get_msg(uint32_t timeout_ms, int &kind, std::string &json) +{ + if (!h_) return false; + ft_job_msg m{}; + int rc = m_->ft_job_get_msg(h_, timeout_ms, &m); + if (rc != 0) return false; + + kind = m.kind; + json.assign(m.json ? m.json : ""); + + if (m_->ft_job_msg_destroy) + m_->ft_job_msg_destroy(&m); + else if (m_->ft_free && m.json) + m_->ft_free((void *) m.json); + + return true; +} + +void FileTransferJob::solve_result(ft_job_result result) +{ + res_ = result.ec; + resp_ec_ = result.resp_ec; + + res_bin_.clear(); + if (result.bin && result.bin_size) res_bin_.assign(reinterpret_cast(result.bin), + reinterpret_cast(result.bin) + result.bin_size); + res_json_.assign(result.json ? result.json : ""); +} + } // namespace Slic3r diff --git a/src/slic3r/Utils/FileTransferUtils.hpp b/src/slic3r/Utils/FileTransferUtils.hpp index 2e0d162ddb..ba91621a91 100644 --- a/src/slic3r/Utils/FileTransferUtils.hpp +++ b/src/slic3r/Utils/FileTransferUtils.hpp @@ -1,7 +1,13 @@ #pragma once #include +#include +#include +#include #include #include +#include +#include +#include #include #ifdef _WIN32 @@ -128,11 +134,103 @@ struct FileTransferModule FileTransferModule &operator=(const FileTransferModule &) = delete; }; -// FileTransferTunnel/FileTransferJob (the OOP wrapper around the ft_tunnel_*/ -// ft_job_* ABI below) live in BBLPrinterAgent.hpp as BBLFileTransferTunnel/ -// BBLFileTransferJob, implementing IFileTransferTunnel/IFileTransferJob -// (IPrinterAgent.hpp) - this header stays the low-level symbol-table layer -// only, same role as bambu_networking.hpp's function pointer typedefs. +class FileTransferTunnel +{ +public: + using ConnectionCb = std::function; + using TunnelStatusCb = std::function; + + explicit FileTransferTunnel(FileTransferModule &m, const std::string &url); + ~FileTransferTunnel() { reset(); } + + FileTransferTunnel(const FileTransferTunnel &) = delete; + FileTransferTunnel &operator=(const FileTransferTunnel &) = delete; + FileTransferTunnel(FileTransferTunnel &&) = delete; + FileTransferTunnel &operator=(FileTransferTunnel &&) = delete; + + void start_connect(); + bool sync_start_connect(); + void on_connection(ConnectionCb cb); + void on_status(TunnelStatusCb cb); + + void shutdown(); + + int get_status() const { return status_; } + bool check_valid() const { return h_ != nullptr; } + FT_TunnelHandle *native() const noexcept { return h_; } + +private: + void reset() noexcept + { + if (h_) { + m_->ft_tunnel_release(h_); + h_ = nullptr; + } + } + + int status_{}; + FileTransferModule *m_{}; + FT_TunnelHandle *h_{}; + ConnectionCb conn_cb_{}; + TunnelStatusCb status_cb_{}; +}; + +class FileTransferJob +{ +public: + using ResultCb = std::function bin_res)>; + using MsgCb = std::function; + + explicit FileTransferJob(FileTransferModule &m, const std::string ¶ms_json); + ~FileTransferJob() { reset(); } + + FileTransferJob(const FileTransferJob &) = delete; + FileTransferJob &operator=(const FileTransferJob &) = delete; + FileTransferJob(FileTransferJob &&) = delete; + FileTransferJob &operator=(FileTransferJob &&) = delete; + + void on_result(ResultCb cb); + + bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms); + + void start_on(FileTransferTunnel &t); + + void on_msg(MsgCb cb); + + bool try_get_msg(int &kind, std::string &json); + + bool get_msg(uint32_t timeout_ms, int &kind, std::string &json); + + FT_JobHandle *native() const noexcept { return h_; } + bool check_valid() const { return h_ != nullptr; } + bool finished() const { return finished_; } + + void cancel() + { + if (m_->ft_job_cancel && h_) m_->ft_job_cancel(h_); + } + +private: + void reset() noexcept + { + if (h_) { + m_->ft_job_release(h_); + h_ = nullptr; + } + } + + void solve_result(ft_job_result result); + + FileTransferModule *m_{}; + FT_JobHandle *h_{}; + ResultCb result_cb_{}; + MsgCb msg_cb_{}; + bool finished_ = false; + int res_ = 0; + int resp_ec_ = 0; + std::string res_json_; + std::vector res_bin_; +}; namespace detail { inline FileTransferModule *g_mod = nullptr; @@ -154,4 +252,4 @@ inline FileTransferModule &module() return *detail::g_mod; } -} // namespace Slic3r \ No newline at end of file +} // namespace Slic3r diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 51df990cdb..0938066970 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -10,7 +10,6 @@ // -70xx is free: the vendor occupies -1..-25 and -10xx through -60xx. #define ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED -7010 // no translation exists for this command #define ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE -7020 // a translation exists; this printer lacks the capability -#define ORCA_NETWORK_ERR_ACCESS_VERIFICATION_FAILED -7030 // printer access preflight failed before printing #include #include #include @@ -50,77 +49,6 @@ enum class FilamentSyncMode { pull ///< On-demand fetch via REST API (blocking call) }; -class IFileTransferTunnel -{ -public: - using ConnectionCb = std::function; - using TunnelStatusCb = std::function; - - explicit IFileTransferTunnel(const std::string& url) : url_(url) {} - virtual ~IFileTransferTunnel() = default; - - IFileTransferTunnel(const IFileTransferTunnel&) = delete; - IFileTransferTunnel& operator=(const IFileTransferTunnel&) = delete; - IFileTransferTunnel(IFileTransferTunnel&&) = delete; - IFileTransferTunnel& operator=(IFileTransferTunnel&&) = delete; - - virtual void start_connect() = 0; - virtual bool sync_start_connect() = 0; - virtual void on_connection(ConnectionCb cb) { conn_cb_ = std::move(cb); } - virtual void on_status(TunnelStatusCb cb) { status_cb_ = std::move(cb); } - - virtual void shutdown() = 0; - - virtual int get_status() const { return status_; } - virtual bool check_valid() const = 0; - - // why: IFileTransferJob::start_on() only ever sees a tunnel through this interface, - // but needs the concrete backend handle to hand to its own start-job call - native() - // is the type-erased escape hatch, same pattern IFileTransferJob::native() already uses. - virtual void *native() const noexcept { return nullptr; } - -protected: - std::string url_; - int status_{}; - ConnectionCb conn_cb_{}; - TunnelStatusCb status_cb_{}; -}; - -class IFileTransferJob { -public: - using ResultCb = std::function bin_res)>; - using MsgCb = std::function; - - explicit IFileTransferJob(const std::string ¶ms_json) : params_json_(params_json) {} - virtual ~IFileTransferJob() = default; - - IFileTransferJob(const IFileTransferJob &) = delete; - IFileTransferJob &operator=(const IFileTransferJob &) = delete; - IFileTransferJob(IFileTransferJob &&) = delete; - IFileTransferJob &operator=(IFileTransferJob &&) = delete; - - virtual void on_result(ResultCb cb) { result_cb_ = std::move(cb); } - virtual bool get_result(int &ec, int &resp_ec, std::string &json, std::vector &bin, uint32_t timeout_ms) = 0; - virtual void start_on(IFileTransferTunnel &t) = 0; - virtual void on_msg(MsgCb cb) { msg_cb_ = std::move(cb); } - virtual bool try_get_msg(int &kind, std::string &json) = 0; - virtual bool get_msg(uint32_t timeout_ms, int &kind, std::string &json) = 0; - virtual void *native() const noexcept { return nullptr; } - virtual bool check_valid() const = 0; - virtual bool finished() const { return finished_; } - virtual void cancel() = 0; - -protected: - std::string params_json_; - ResultCb result_cb_{}; - MsgCb msg_cb_{}; - bool finished_ = false; - int res_ = 0; - int resp_ec_ = 0; - std::string res_json_; - std::vector res_bin_; -}; - /** * IPrinterAgent - Interface for printer operations. * @@ -425,55 +353,6 @@ public: */ virtual bool fetch_filament_info(std::string dev_id) { return false; } - struct FileTransferRequest - { - std::string device_id; - std::string device_ip; - std::string access_code; - std::string network_version; - std::string device_version; - std::string refresh_url; - std::string client_id; - std::string client_version; - bool lan_mode{false}; - }; - - struct FileTransferCallbacks - { - std::function on_connection; - std::function on_destinations; - std::function on_progress; - std::function binary_result)> on_result; - std::function file_transfer_error; - }; - - /** - * Prepare the agent's file-transfer session. The transport is agent-owned; - * callers must not need to know whether it is a tunnel, HTTP connection, - * or another protocol. - */ - virtual void prepare_file_transfer(const FileTransferRequest&, FileTransferCallbacks cb) - { - if (cb.file_transfer_error) - cb.file_transfer_error(); - } - - /** Query the destinations available for the prepared transfer session. */ - virtual void get_file_destinations(FileTransferCallbacks cb) - { - if (cb.file_transfer_error) - cb.file_transfer_error(); - } - - /** Upload a file using the prepared transfer session. */ - virtual void upload_file(const std::string&, const std::string&, const std::string&, FileTransferCallbacks cb) - { - if (cb.file_transfer_error) - cb.file_transfer_error(); - } - - /** Cancel the current file-transfer operation and release its resources. */ - virtual void cancel_file_transfer() {} }; } // namespace Slic3r From eb96d127b01d03a42da12ff3f833f9a0ebff1672 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Wed, 19 Aug 2026 17:11:10 +0800 Subject: [PATCH 12/15] fix: resolve stubgen byte header conflict --- src/slic3r/plugin/PythonPluginBridge.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/slic3r/plugin/PythonPluginBridge.cpp b/src/slic3r/plugin/PythonPluginBridge.cpp index 328ebbace1..71ac3776c5 100644 --- a/src/slic3r/plugin/PythonPluginBridge.cpp +++ b/src/slic3r/plugin/PythonPluginBridge.cpp @@ -1,3 +1,15 @@ +#ifdef ORCA_PYTHON_STUBGEN_MODULE + #ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #endif +#endif + #include "PythonPluginBridge.hpp" #include From f16071f083ef17bcbb70f23b58460f70cf52d715 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Tue, 25 Aug 2026 13:54:40 +0800 Subject: [PATCH 13/15] fix: remove heavy includes from IPrinterAgent --- src/slic3r/GUI/DeviceManager.hpp | 28 +-------- src/slic3r/Utils/IPrinterAgent.hpp | 3 +- src/slic3r/Utils/NetworkAgent.hpp | 46 +------------- src/slic3r/Utils/PrinterNetworkTypes.hpp | 78 ++++++++++++++++++++++++ src/slic3r/plugin/PythonPluginBridge.cpp | 13 ---- 5 files changed, 81 insertions(+), 87 deletions(-) create mode 100644 src/slic3r/Utils/PrinterNetworkTypes.hpp diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index f920433652..62de0e87b8 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -18,6 +18,7 @@ #include "boost/bimap/bimap.hpp" #include "libslic3r/calib.hpp" #include "libslic3r/Utils.hpp" +#include "slic3r/Utils/PrinterNetworkTypes.hpp" #include "DeviceCore/DevDefs.h" #include "DeviceCore/DevConfigUtil.h" @@ -100,33 +101,6 @@ struct DevPrintTaskRatingInfo; // given nozzle diameter (mm), bucketed per nozzle size to mirror the printer firmware. bool is_stringing_prone_filament(const std::string& filament_id, float nozzle_diameter); -enum LiveviewLocal { - LVL_None, - LVL_Disable, - LVL_Local, - LVL_Rtsps, - LVL_Rtsp -}; - -enum LiveviewRemote { - LVR_None, - LVR_Tutk, - LVR_Agora, - LVR_TutkAgora -}; - -enum FileLocal { - FL_None, - FL_Local -}; - -enum FileRemote { - FR_None, - FR_Tutk, - FR_Agora, - FR_TutkAgora -}; - class MachineObject { private: diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 0938066970..b5db97403b 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -2,7 +2,6 @@ #define __I_PRINTER_AGENT_HPP__ #include "bambu_networking.hpp" -#include // why: these extend the BAMBU_NETWORK_* return space rather than opening a new one - the value // flows through the same int domain callers already compare against BAMBU_NETWORK_SUCCESS. // They live here and not in bambu_networking.hpp because that file is a vendor header replaced @@ -16,7 +15,7 @@ #include #include -#include "NetworkAgent.hpp" +#include "PrinterNetworkTypes.hpp" namespace Slic3r { diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 75cca1e314..4a2e69ecad 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -5,7 +5,7 @@ #include "libslic3r/ProjectTask.hpp" #include "ICloudServiceAgent.hpp" -#include "slic3r/GUI/DeviceManager.hpp" +#include "PrinterNetworkTypes.hpp" #include #include @@ -17,50 +17,6 @@ namespace Slic3r { class IPrinterAgent; enum class FilamentSyncMode; -enum URL_STATE { - URL_TCP, - URL_TUTK, -}; - -struct CameraURLParams { - std::string ip_address; - std::string user; - std::string password; - LiveviewLocal protocol; - std::string device; - std::string network_version; - std::string device_version; - std::string refresh_url; - std::string client_id; - std::string client_version; - bool apply_meta{false}; -}; - -struct FileTransferURLParams { - URL_STATE url_state{URL_TCP}; - std::string ip_address; - std::string username; - std::string password; - std::string device_id; - std::string network_version; - std::string device_version; - std::string refresh_url; - std::string client_id; - std::string client_version; -}; - -struct FileTransferURLResult { - bool is_success{false}; - std::string url; - int error_code{-1}; -}; - -struct CameraURLResult { - bool is_success{false}; - std::string url; - int error_code{-1}; -}; - // Forward declaration class BBLNetworkPlugin; diff --git a/src/slic3r/Utils/PrinterNetworkTypes.hpp b/src/slic3r/Utils/PrinterNetworkTypes.hpp new file mode 100644 index 0000000000..aa3809f768 --- /dev/null +++ b/src/slic3r/Utils/PrinterNetworkTypes.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include + +namespace Slic3r { + +enum LiveviewLocal { + LVL_None, + LVL_Disable, + LVL_Local, + LVL_Rtsps, + LVL_Rtsp +}; + +enum LiveviewRemote { + LVR_None, + LVR_Tutk, + LVR_Agora, + LVR_TutkAgora +}; + +enum FileLocal { + FL_None, + FL_Local +}; + +enum FileRemote { + FR_None, + FR_Tutk, + FR_Agora, + FR_TutkAgora +}; + +enum URL_STATE { + URL_TCP, + URL_TUTK, +}; + +struct CameraURLParams { + std::string ip_address; + std::string user; + std::string password; + LiveviewLocal protocol; + std::string device; + std::string network_version; + std::string device_version; + std::string refresh_url; + std::string client_id; + std::string client_version; + bool apply_meta{false}; +}; + +struct CameraURLResult { + bool is_success{false}; + std::string url; + int error_code{-1}; +}; + +struct FileTransferURLParams { + URL_STATE url_state{URL_TCP}; + std::string ip_address; + std::string username; + std::string password; + std::string device_id; + std::string network_version; + std::string device_version; + std::string refresh_url; + std::string client_id; + std::string client_version; +}; + +struct FileTransferURLResult { + bool is_success{false}; + std::string url; + int error_code{-1}; +}; + +} // namespace Slic3r diff --git a/src/slic3r/plugin/PythonPluginBridge.cpp b/src/slic3r/plugin/PythonPluginBridge.cpp index 4dd4c5ea93..5cf44e2f95 100644 --- a/src/slic3r/plugin/PythonPluginBridge.cpp +++ b/src/slic3r/plugin/PythonPluginBridge.cpp @@ -1,15 +1,3 @@ -#ifdef ORCA_PYTHON_STUBGEN_MODULE - #ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include - #endif -#endif - #include "PythonPluginBridge.hpp" #include @@ -25,7 +13,6 @@ #include #include "PythonInterpreter.hpp" -#include "PluginFsUtils.hpp" #include "PluginConfig.hpp" #include "host/PluginHost.hpp" #include "PyPluginPackage.hpp" From b2a04851392d2ad9154f15bdec469ccb6e450b92 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Tue, 25 Aug 2026 14:34:11 +0800 Subject: [PATCH 14/15] fix: defer filesystem and camera abstractions --- src/slic3r/GUI/MediaFilePanel.cpp | 40 ++++++------ src/slic3r/GUI/MediaFilePanel.h | 1 + src/slic3r/GUI/MediaPlayCtrl.cpp | 48 ++++++++------ src/slic3r/GUI/MediaPlayCtrl.h | 1 + src/slic3r/GUI/PartSkipDialog.cpp | 47 ++++++++------ src/slic3r/GUI/PartSkipDialog.hpp | 10 ++- src/slic3r/Utils/BBLPrinterAgent.cpp | 82 +----------------------- src/slic3r/Utils/BBLPrinterAgent.hpp | 6 -- src/slic3r/Utils/IPrinterAgent.hpp | 29 --------- src/slic3r/Utils/NetworkAgent.cpp | 32 --------- src/slic3r/Utils/NetworkAgent.hpp | 5 -- src/slic3r/Utils/PrinterNetworkTypes.hpp | 24 ------- 12 files changed, 82 insertions(+), 243 deletions(-) diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 6b460ce01d..c9a33cb838 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -207,6 +207,8 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) Bind(wxEVT_SHOW, onShowHide); parent->GetParent()->Bind(wxEVT_SHOW, onShowHide); + m_lan_user = "bblp"; + } MediaFilePanel::~MediaFilePanel() @@ -465,19 +467,15 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: " << m_local_proto << m_remote_proto; m_waiting_support = false; NetworkAgent *agent = wxGetApp().getAgent(); - if (agent && (m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { - agent->get_file_transfer_url( - m_machine, - [this, wfs](FileTransferURLResult result) { - CallAfter([this, wfs, result = std::move(result)] { - auto fs = wfs.lock(); - if (!fs || fs != m_image_grid->GetFileSystem()) - return; - fs->SetUrl(result.is_success ? result.url : std::to_string(result.error_code)); - }); - }, - {URL_TCP, m_lan_ip, agent->default_lan_username(), m_lan_passwd, - m_machine, agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); + std::string agent_version = agent ? agent->get_version() : ""; + if ((m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { + std::string url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; + url += "&device=" + m_machine; + url += "&net_ver=" + agent_version; + url += "&dev_ver=" + m_dev_ver; + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + fs->SetUrl(url); return; } if (!m_remote_proto && m_local_proto) { // not support tutk @@ -496,15 +494,15 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) return; } if (agent) { - agent->get_file_transfer_url( - m_machine, - [this, wfs, m = m_machine](FileTransferURLResult result) { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], + [this, wfs](CameraURLResult result) { std::string url = std::move(result.url); - BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: file_system_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; - if (result.is_success) { + if (result.is_success && boost::algorithm::starts_with(url, "bambu:///")) { fs->SetUrl(url); } else { m_image_grid->SetStatus(m_bmp_failed, _L("Connection Failed. Please check the network and try again")); @@ -512,9 +510,9 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) fs->SetUrl(res); } }); - }, - {URL_TUTK, "", "", "", m_machine, agent->get_version(), m_dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); + }, wxGetApp().get_printer_cloud_provider(), + CameraURLParams{"", "", "", LVL_None, m_machine, agent->get_version(), m_dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); } } diff --git a/src/slic3r/GUI/MediaFilePanel.h b/src/slic3r/GUI/MediaFilePanel.h index 41596c38c1..72fbc96a13 100644 --- a/src/slic3r/GUI/MediaFilePanel.h +++ b/src/slic3r/GUI/MediaFilePanel.h @@ -80,6 +80,7 @@ private: std::string m_machine; std::string m_lan_ip; + std::string m_lan_user; std::string m_lan_passwd; std::string m_dev_ver; bool m_lan_mode = false; diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 0a796981d1..c7304a3903 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -7,6 +7,7 @@ #include "I18N.hpp" #include "MsgDialog.hpp" #include "DownloadProgressDialog.hpp" +#include "slic3r/Utils/BBLNetworkPlugin.hpp" #include @@ -126,6 +127,9 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w parent->Bind(wxEVT_SHOW, &MediaPlayCtrl::on_show_hide, this); parent->GetParent()->GetParent()->Bind(wxEVT_SHOW, &MediaPlayCtrl::on_show_hide, this); + m_lan_user = "bblp"; + m_lan_passwd = "bblp"; + } MediaPlayCtrl::~MediaPlayCtrl() @@ -156,10 +160,8 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) m_device_busy = obj->is_camera_busy_off(); m_tutk_state = obj->tutk_state; - auto *agent = wxGetApp().getAgent(); - if (agent && !agent->supports_remote_liveview(obj->printer_type)) { - // The selected printer agent may force local mode for incompatible - // plugin/printer combinations. + if (DevPrinterConfigUtil::get_printer_series_str(obj->printer_type) == "series_o" && BBLNetworkPlugin::instance().use_legacy_network()) { + // Legacy plugin cannot support remote play for H2D, force using local mode m_remote_proto = LiveviewRemote::LVR_None; } } else { @@ -287,21 +289,20 @@ void MediaPlayCtrl::Play() return; } std::string agent_version = agent->get_version(); - const std::string lan_user = agent->default_lan_username(); if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { m_disable_lan = m_remote_proto && !m_lan_mode; // try remote next time - std::string url = agent->get_local_camera_url({ - m_lan_ip, - lan_user, - m_lan_passwd, - LiveviewLocal(m_lan_proto), - into_u8(m_machine), - agent_version, - m_dev_ver, - "", - wxGetApp().app_config->get("slicer_uuid"), - SLIC3R_VERSION - }); + std::string url; + if (m_lan_proto == LiveviewLocal::LVL_Local) + url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; + else if (m_lan_proto == LiveviewLocal::LVL_Rtsps) + url = "bambu:///rtsps___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsps"; + else if (m_lan_proto == LiveviewLocal::LVL_Rtsp) + url = "bambu:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp"; + url += "&device=" + m_machine; + url += "&net_ver=" + agent_version; + url += "&dev_ver=" + m_dev_ver; + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); m_url = url; load(); @@ -527,10 +528,15 @@ void MediaPlayCtrl::ToggleStream() if (res == wxID_CANCEL) return; } if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent) return; - std::string url = agent->get_local_camera_url({m_lan_ip, agent->default_lan_username(), m_lan_passwd, LiveviewLocal(m_lan_proto), - into_u8(m_machine), agent->get_version(), m_dev_ver, "", wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION}); + std::string url; + if (m_lan_proto == LiveviewLocal::LVL_Local) + url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; + else if (m_lan_proto == LiveviewLocal::LVL_Rtsps) + url = "bambu:///rtsps___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsps"; + else if (m_lan_proto == LiveviewLocal::LVL_Rtsp) + url = "bambu:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp"; + url += "&device=" + into_u8(m_machine); + url += "&dev_ver=" + m_dev_ver; BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); std::string file_url = data_dir() + "/cameratools/url.txt"; boost::nowide::ofstream file(file_url); diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h index 5a7c53f695..f5e5dcddfc 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.h +++ b/src/slic3r/GUI/MediaPlayCtrl.h @@ -80,6 +80,7 @@ private: std::string m_machine; int m_lan_proto = 0; std::string m_lan_ip; + std::string m_lan_user; std::string m_lan_passwd; std::string m_dev_ver; std::string m_tutk_state; diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 54c66fb80e..b912f69c8f 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -27,6 +27,7 @@ #include "PartSkipDialog.hpp" #include "SkipPartCanvas.hpp" #include "MediaPlayCtrl.h" +#include "slic3r/Utils/NetworkAgent.hpp" #include "DeviceCore/DevManager.h" @@ -434,37 +435,41 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) std::string dev_ver = obj->get_ota_version(); std::string dev_id = obj->get_dev_id(); + auto url_state = m_url_state; + if (obj->is_lan_mode_printer()) { url_state = URL_TCP; } + NetworkAgent *agent = wxGetApp().getAgent(); if (!agent) { fs->SetUrl("3"); return; } - auto url_state = m_url_state; - if (obj->is_lan_mode_printer()) { url_state = URL_TCP; } - - FileTransferURLParams params; - params.url_state = url_state; - params.ip_address = obj->get_dev_ip(); - params.username = agent->default_lan_username(); - params.password = obj->get_access_code(); - params.device_id = dev_id; - params.network_version = agent->get_version(); - params.device_version = dev_ver; - params.refresh_url = boost::lexical_cast(&refresh_agora_url); - params.client_id = wxGetApp().app_config->get("slicer_uuid"); - params.client_version = SLIC3R_VERSION; - - agent->get_file_transfer_url( - dev_id, - [this, wfs](FileTransferURLResult result) { + switch (url_state) { + case URL_TCP: { + std::string tcp_url = "bambu:///local/" + obj->get_dev_ip() + "?port=6000&user=bblp&passwd=" + obj->get_access_code(); + CallAfter([wfs, tcp_url = std::move(tcp_url)] { + if (auto fs = wfs.lock()) + fs->SetUrl(boost::algorithm::starts_with(tcp_url, "bambu:///") ? tcp_url : "3"); + }); + break; + } + case URL_TUTK: { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(dev_id + "|" + dev_ver + "|" + protocols[3], + [this, wfs](CameraURLResult result) { CallAfter([wfs, result = std::move(result)]() mutable { boost::shared_ptr fs(wfs.lock()); if (!fs) return; - fs->SetUrl(result.is_success ? result.url : "3"); + fs->SetUrl(result.is_success && boost::algorithm::starts_with(result.url, "bambu:///") ? result.url : "3"); }); - }, - std::move(params)); + }, wxGetApp().get_printer_cloud_provider(), + CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, + boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + break; + } + default: + break; + } } // controller void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index d204ba2043..2cc622dae0 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -14,7 +14,6 @@ #include #include -#include "NetworkAgent.hpp" #include "Widgets/Label.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/Button.hpp" @@ -30,6 +29,11 @@ namespace Slic3r { namespace GUI { class SkipPartCanvas; +enum URL_STATE { + URL_TCP, + URL_TUTK, +}; + class PartSkipConfirmDialog : public DPIDialog { private: @@ -118,7 +122,7 @@ private: std::map m_parts_name; std::vector m_partskip_ids; - URL_STATE m_url_state = URL_STATE::URL_TCP; + enum URL_STATE m_url_state = URL_STATE::URL_TCP; PartsInfo GetPartsInfo(); bool is_drag_mode(); @@ -156,4 +160,4 @@ private: void OnApplyDialog(wxCommandEvent &event); }; -}} // namespace Slic3r::GUI \ No newline at end of file +}} // namespace Slic3r::GUI diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index c1f5b5666a..138cc9463c 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -19,7 +19,7 @@ BBLPrinterAgent::~BBLPrinterAgent() = default; void BBLPrinterAgent::set_cloud_agent(std::shared_ptr cloud) { - m_cloud_agent = cloud; + (void) cloud; // BBL DLL manages tokens internally, so this is just for interface compliance } @@ -250,86 +250,6 @@ int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string jso return -1; } -std::string BBLPrinterAgent::get_local_camera_url(CameraURLParams params) -{ - std::string url; - if (params.protocol == LVL_Local) - url = "bambu:///local/" + params.ip_address + ".?port=6000&user=" + params.user + "&passwd=" + params.password; - else if (params.protocol == LVL_Rtsps) - url = "bambu:///rtsps___" + params.user + ":" + params.password + "@" + params.ip_address + "/streaming/live/1?proto=rtsps"; - else if (params.protocol == LVL_Rtsp) - url = "bambu:///rtsp___" + params.user + ":" + params.password + "@" + params.ip_address + "/streaming/live/1?proto=rtsp"; - else - url = "bambu:///local/" + params.ip_address + ".?port=6000&user=" + params.user + "&passwd=" + params.password; - - url += "&device=" + params.device; - url += "&net_ver=" + params.network_version; - url += "&dev_ver=" + params.device_version; - url += "&cli_id=" + params.client_id; - url += "&cli_ver=" + params.client_version; - return url; -} - -std::string BBLPrinterAgent::get_local_file_transfer_url(const FileTransferURLParams& params) -{ - // Keep the historical PartSkipDialog URL unchanged. It is a file-transfer - // tunnel URL, not a camera URL, so it intentionally has no camera metadata - // suffix and no dot before the query string. - return "bambu:///local/" + params.ip_address + "?port=6000&user=" + params.username + "&passwd=" + params.password; -} - -bool BBLPrinterAgent::supports_remote_liveview(const std::string& printer_type) const -{ - // The legacy Bambu networking plugin cannot provide remote live view for - // the O-series printers. Keep this compatibility rule in the Bambu agent - // instead of exposing plugin/version details to GUI code. - return !(DevPrinterConfigUtil::get_printer_series_str(printer_type) == "series_o" && - BBLNetworkPlugin::instance().use_legacy_network()); -} - -int BBLPrinterAgent::get_file_transfer_url(std::string dev_id, std::function callback, - FileTransferURLParams params) -{ - if (params.url_state == URL_TCP) { - FileTransferURLResult result; - result.url = get_local_file_transfer_url(params); - result.is_success = !result.url.empty(); - result.error_code = result.is_success ? 0 : -1; - if (callback) - callback(std::move(result)); - return result.is_success ? 0 : -1; - } - - if (!m_cloud_agent) { - if (callback) - callback({}); - return -1; - } - - const std::string protocols = "\"tutk\",\"agora\""; - return m_cloud_agent->get_camera_url( - std::move(dev_id) + "|" + params.device_version + "|" + protocols, - [callback = std::move(callback)](CameraURLResult result) { - if (!callback) - return; - FileTransferURLResult transfer_result; - transfer_result.is_success = result.is_success; - transfer_result.url = std::move(result.url); - transfer_result.error_code = result.error_code; - callback(std::move(transfer_result)); - }, - CameraURLParams{ - "", "", "", LVL_None, - params.device_id, - params.network_version, - params.device_version, - params.refresh_url, - params.client_id, - params.client_version, - true - }); -} - // ============================================================================ // Certificates // ============================================================================ diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index 94f7ea8466..8728084e99 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -45,11 +45,6 @@ public: 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; - std::string get_local_camera_url(CameraURLParams params) override; - std::string get_local_file_transfer_url(const FileTransferURLParams& params) override; - bool supports_remote_liveview(const std::string& printer_type) const override; - int get_file_transfer_url(std::string dev_id, std::function callback, - FileTransferURLParams params) override; std::string default_lan_username() const override { return "bblp"; } // Certificates @@ -108,7 +103,6 @@ private: // why: the lan/cloud DECISION stays machine-side; keep this mechanical branch in sync with publish_json. int publish(const std::string& dev_id, const nlohmann::json& j, bool lan_mode); - std::shared_ptr m_cloud_agent; }; } // namespace Slic3r diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index b5db97403b..4cc3bfaf3b 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -15,8 +15,6 @@ #include #include -#include "PrinterNetworkTypes.hpp" - namespace Slic3r { class ICloudServiceAgent; @@ -112,33 +110,6 @@ public: bool is_core_xy, bool supports_mqtt_axis_control, int sequence_id, bool lan_mode) { return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; } - /** - * Build a ready-to-use local (LAN) camera stream URL for this agent's protocol. - * Returns an empty string if the agent has no local camera stream support. - */ - virtual std::string get_local_camera_url(CameraURLParams params) { return ""; } - - /** - * Build a ready-to-use local (LAN) file transfer URL for this agent's protocol. - * Returns an empty string if the agent has no local file transfer support. - */ - virtual std::string get_local_file_transfer_url(const FileTransferURLParams& params) { return ""; } - - /** - * Whether remote live view is available for the selected printer and agent - * protocol. Implementations may use their plugin/version compatibility - * rules; the neutral default keeps existing agents permissive. - */ - virtual bool supports_remote_liveview(const std::string& printer_type) const - { (void) printer_type; return true; } - - virtual int get_file_transfer_url(std::string, std::function callback, FileTransferURLParams) - { - if (callback) - callback({}); - return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; - } - /** * Default LAN account username for this agent's protocol, if it has a fixed one. * Returns an empty string if the agent has no fixed default (e.g. caller must supply one). diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index f93a0cf882..d18d918841 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -854,38 +854,6 @@ int NetworkAgent::send_message_to_printer(std::string dev_id, std::string json_s return -1; } -std::string NetworkAgent::get_local_camera_url(CameraURLParams params) -{ - if (m_printer_agent) - return m_printer_agent->get_local_camera_url(params); - return {}; -} - -std::string NetworkAgent::get_local_file_transfer_url(const FileTransferURLParams& params) -{ - if (m_printer_agent) - return m_printer_agent->get_local_file_transfer_url(params); - return {}; -} - -bool NetworkAgent::supports_remote_liveview(const std::string& printer_type) const -{ - // Preserve the historical permissive behavior while the printer agent is - // being selected. A missing agent must not turn a supported remote - // protocol into LVNone before the Bambu agent has been installed. - return !m_printer_agent || m_printer_agent->supports_remote_liveview(printer_type); -} - -int NetworkAgent::get_file_transfer_url(std::string dev_id, std::function callback, - FileTransferURLParams params) -{ - if (m_printer_agent) - return m_printer_agent->get_file_transfer_url(std::move(dev_id), std::move(callback), std::move(params)); - if (callback) - callback({}); - return -1; -} - std::string NetworkAgent::default_lan_username() const { if (m_printer_agent) diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 4a2e69ecad..5588305dba 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -161,11 +161,6 @@ public: int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl); int disconnect_printer(); int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag); - std::string get_local_camera_url(CameraURLParams params); - std::string get_local_file_transfer_url(const FileTransferURLParams& params); - bool supports_remote_liveview(const std::string& printer_type) const; - int get_file_transfer_url(std::string dev_id, std::function callback, - FileTransferURLParams params = {}); std::string default_lan_username() const; int check_cert(); void install_device_cert(std::string dev_id, bool lan_only); diff --git a/src/slic3r/Utils/PrinterNetworkTypes.hpp b/src/slic3r/Utils/PrinterNetworkTypes.hpp index aa3809f768..e1c1924d4e 100644 --- a/src/slic3r/Utils/PrinterNetworkTypes.hpp +++ b/src/slic3r/Utils/PrinterNetworkTypes.hpp @@ -31,11 +31,6 @@ enum FileRemote { FR_TutkAgora }; -enum URL_STATE { - URL_TCP, - URL_TUTK, -}; - struct CameraURLParams { std::string ip_address; std::string user; @@ -56,23 +51,4 @@ struct CameraURLResult { int error_code{-1}; }; -struct FileTransferURLParams { - URL_STATE url_state{URL_TCP}; - std::string ip_address; - std::string username; - std::string password; - std::string device_id; - std::string network_version; - std::string device_version; - std::string refresh_url; - std::string client_id; - std::string client_version; -}; - -struct FileTransferURLResult { - bool is_success{false}; - std::string url; - int error_code{-1}; -}; - } // namespace Slic3r From 4df1607ec4af85f6c341483f79cc8f9ec18f428b Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Fri, 28 Aug 2026 16:45:01 +0800 Subject: [PATCH 15/15] fix: clear up some unrelated changes --- src/slic3r/GUI/Jobs/PrintJob.cpp | 2 +- src/slic3r/GUI/MediaFilePanel.cpp | 28 ++++--- src/slic3r/GUI/MediaPlayCtrl.cpp | 93 ++++++++++++---------- src/slic3r/GUI/PartSkipDialog.cpp | 73 ++++++++++------- src/slic3r/GUI/SendToPrinter.cpp | 29 ++++--- src/slic3r/Utils/BBLCloudServiceAgent.cpp | 39 +-------- src/slic3r/Utils/BBLCloudServiceAgent.hpp | 2 +- src/slic3r/Utils/ICloudServiceAgent.hpp | 6 +- src/slic3r/Utils/IPrinterAgent.hpp | 1 - src/slic3r/Utils/NetworkAgent.cpp | 5 +- src/slic3r/Utils/NetworkAgent.hpp | 4 +- src/slic3r/Utils/OrcaCloudServiceAgent.cpp | 6 +- src/slic3r/Utils/OrcaCloudServiceAgent.hpp | 2 +- src/slic3r/Utils/PrinterNetworkTypes.hpp | 20 ----- src/slic3r/plugin/PythonPluginBridge.cpp | 1 + 15 files changed, 141 insertions(+), 170 deletions(-) diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index 79bdecc1dd..1f7cfce708 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -288,7 +288,7 @@ void PrintJob::process(Ctl &ctl) if (v == "0" || v == "false") disable_emmc = false; } - params.try_emmc_print = this->could_emmc_print && !disable_emmc; + params.try_emmc_print = this->could_emmc_print && !disable_emmc; if (m_print_type == "from_sdcard_view") { params.dst_file = m_dst_path; diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index c9a33cb838..36316f8ff5 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -11,7 +11,6 @@ #include "Widgets/ProgressDialog.hpp" #include #include -#include #include "DeviceCore/DevStorage.h" #ifdef __WXMSW__ @@ -208,7 +207,6 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) parent->GetParent()->Bind(wxEVT_SHOW, onShowHide); m_lan_user = "bblp"; - } MediaFilePanel::~MediaFilePanel() @@ -467,7 +465,7 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: " << m_local_proto << m_remote_proto; m_waiting_support = false; NetworkAgent *agent = wxGetApp().getAgent(); - std::string agent_version = agent ? agent->get_version() : ""; + std::string agent_version = agent ? agent->get_version() : ""; if ((m_lan_mode || !m_remote_proto) && m_local_proto && !m_lan_ip.empty()) { std::string url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd; url += "&device=" + m_machine; @@ -496,23 +494,33 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) if (agent) { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, wfs](CameraURLResult result) { - std::string url = std::move(result.url); + [this, wfs, m = m_machine, v = agent->get_version(), dv = m_dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; - if (result.is_success && boost::algorithm::starts_with(url, "bambu:///")) { + if (boost::algorithm::starts_with(url, "bambu:///")) { fs->SetUrl(url); } else { m_image_grid->SetStatus(m_bmp_failed, _L("Connection Failed. Please check the network and try again")); - std::string res = result.error_code >= 0 ? std::to_string(result.error_code) : "3"; + std::string res = "3"; + if (boost::ends_with(url, "]")) { + size_t n = url.find_last_of('['); + if (n != std::string::npos) + res = url.substr(n + 1, url.length() - n - 2); + } fs->SetUrl(res); } }); - }, wxGetApp().get_printer_cloud_provider(), - CameraURLParams{"", "", "", LVL_None, m_machine, agent->get_version(), m_dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + }, wxGetApp().get_printer_cloud_provider()); } } diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index c7304a3903..0b4b4b9e79 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -129,7 +129,6 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w m_lan_user = "bblp"; m_lan_passwd = "bblp"; - } MediaPlayCtrl::~MediaPlayCtrl() @@ -249,8 +248,8 @@ void refresh_agora_url(char const* device, char const* dev_ver, char const* chan device2 += dev_ver; device2 += "|\"agora\"|"; device2 += channel; - wxGetApp().getAgent()->get_camera_url(device2, [context, callback](CameraURLResult result) { - callback(context, result.url.c_str()); + wxGetApp().getAgent()->get_camera_url(device2, [context, callback](std::string url) { + callback(context, url.c_str()); }, wxGetApp().get_printer_cloud_provider()); } @@ -284,11 +283,7 @@ void MediaPlayCtrl::Play() BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::Play: " << m_lan_proto << m_remote_proto << m_disable_lan; NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent) { - Stop(_L("Please confirm if the printer is connected.")); - return; - } - std::string agent_version = agent->get_version(); + std::string agent_version = agent ? agent->get_version() : ""; if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) { m_disable_lan = m_remote_proto && !m_lan_mode; // try remote next time std::string url; @@ -342,39 +337,46 @@ void MediaPlayCtrl::Play() if (agent) { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url( - m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, m = m_machine, token = std::weak_ptr(m_token)](CameraURLResult result) { - std::string url = std::move(result.url); - const bool success = result.is_success; - const int error_code = result.error_code; - if (token.expired()) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": token has been expired"; + agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], + [this, m = m_machine, v = agent_version, dv = m_dev_ver, token = std::weak_ptr(m_token)](std::string url) { + if (token.expired()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": token has been expired"; + return; + } + + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + into_u8(m); + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, + {"?uid=", "authkey=", "passwd=", "license=", "token="}); + CallAfter([this, m, url] { + if (m != m_machine) { + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for machine: " << m; return; } - - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, {"?uid=", "authkey=", "passwd=", "license=", "token="}); - CallAfter([this, m, url, success, error_code] { - if (m != m_machine) { - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for machine: " << m; - return; - } - if (m_last_state == MEDIASTATE_INITIALIZING) { - if (!success) { - m_failed_code = error_code >= 0 ? error_code : 3; - Stop(_L("Connection Failed. Please check the network and try again"), from_u8(url)); - } else { - m_url = url; - load(); + if (m_last_state == MEDIASTATE_INITIALIZING) { + if (url.empty() || !boost::algorithm::starts_with(url, "bambu:///")) { + m_failed_code = 3; + if (boost::ends_with(url, "]")) { + size_t n = url.find_last_of('['); + if (n != std::string::npos) + m_failed_code = std::atoi(url.substr(n + 1, url.length() - n - 2).c_str()); } + Stop(_L("Connection Failed. Please check the network and try again"), from_u8(url)); } else { - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for state: " << m_last_state; + m_url = url; + load(); } - }); - }, - wxGetApp().get_printer_cloud_provider(), - CameraURLParams{"", "", "", LVL_None, into_u8(m_machine), agent_version, m_dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + } else { + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for state: " << m_last_state; + } + }); + }, wxGetApp().get_printer_cloud_provider()); } } @@ -550,14 +552,20 @@ void MediaPlayCtrl::ToggleStream() if (!agent) return; std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], - [this, m = m_machine](CameraURLResult result) { - std::string url = std::move(result.url); - const bool success = result.is_success; + [this, m = m_machine, v = agent->get_version(), dv = m_dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(url, {"?uid=", "authkey=", "passwd=", "license=", "token="}); - CallAfter([this, m, url, success] { + CallAfter([this, m, url] { if (m != m_machine) return; - if (!success) { + if (url.empty() || !boost::algorithm::starts_with(url, "bambu:///")) { MessageDialog(this->GetParent(), wxString::Format(_L("Virtual camera initialize failed (%s)!"), url.empty() ? _L("Network unreachable") : from_u8(url)), _L("Information"), wxICON_INFORMATION) .ShowModal(); @@ -570,8 +578,7 @@ void MediaPlayCtrl::ToggleStream() file.close(); m_streaming = true; }); - }, wxGetApp().get_printer_cloud_provider(), CameraURLParams{"", "", "", LVL_None, into_u8(m_machine), agent->get_version(), m_dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + }, wxGetApp().get_printer_cloud_provider()); } void MediaPlayCtrl::msw_rescale() { diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index b912f69c8f..b9dd7d5007 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -27,7 +27,6 @@ #include "PartSkipDialog.hpp" #include "SkipPartCanvas.hpp" #include "MediaPlayCtrl.h" -#include "slic3r/Utils/NetworkAgent.hpp" #include "DeviceCore/DevManager.h" @@ -434,41 +433,57 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) } std::string dev_ver = obj->get_ota_version(); std::string dev_id = obj->get_dev_id(); + // int remote_proto = obj->get_file_remote(); + + NetworkAgent *agent = wxGetApp().getAgent(); + std::string agent_version = agent ? agent->get_version() : ""; auto url_state = m_url_state; if (obj->is_lan_mode_printer()) { url_state = URL_TCP; } - NetworkAgent *agent = wxGetApp().getAgent(); - if (!agent) { - fs->SetUrl("3"); - return; - } - - switch (url_state) { - case URL_TCP: { - std::string tcp_url = "bambu:///local/" + obj->get_dev_ip() + "?port=6000&user=bblp&passwd=" + obj->get_access_code(); - CallAfter([wfs, tcp_url = std::move(tcp_url)] { - if (auto fs = wfs.lock()) - fs->SetUrl(boost::algorithm::starts_with(tcp_url, "bambu:///") ? tcp_url : "3"); - }); - break; - } - case URL_TUTK: { - std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(dev_id + "|" + dev_ver + "|" + protocols[3], - [this, wfs](CameraURLResult result) { - CallAfter([wfs, result = std::move(result)]() mutable { + if (agent) { + switch (url_state) { + case URL_TCP: { + std::string devIP = obj->get_dev_ip(); + std::string accessCode = obj->get_access_code(); + std::string tcp_url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs) return; - fs->SetUrl(result.is_success && boost::algorithm::starts_with(result.url, "bambu:///") ? result.url : "3"); + if (boost::algorithm::starts_with(tcp_url, "bambu:///")) { + fs->SetUrl(tcp_url); + } else { + fs->SetUrl("3"); + } }); - }, wxGetApp().get_printer_cloud_provider(), - CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); - break; - } - default: - break; + break; + } + case URL_TUTK: { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[3], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) + { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(url, "bambu:///")) { + fs->SetUrl(url); + } else { + fs->SetUrl("3"); + } + }); + }); + break; + } + default: break; + } } } // controller diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index b970447cd8..17da4b66a0 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -868,12 +868,12 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) if (wxGetApp().plater()->using_exported_file()) { m_plater->set_print_job_plate_idx(m_print_plate_idx); result = 0; - } else { - result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool& cancel) { - if (this->m_is_canceled) - return; - bool cancelled = false; - wxString msg = _L("Preparing print job"); + } + else { + result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool &cancel) { + if (this->m_is_canceled) return; + bool cancelled = false; + wxString msg = _L("Preparing print job"); m_status_bar->update_status(msg, cancelled, 10, true); m_export_3mf_cancel = cancel = cancelled; }); @@ -1728,8 +1728,15 @@ void SendToPrinterDialog::GetConnection() else if (m_tutk_try_connect) { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], [this, m = dev_id](CameraURLResult result) { - std::string url = std::move(result.url); + agent->get_camera_url(obj->get_dev_id() + "|" + dev_ver + "|" + protocols[1], [this, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } if (m_url_timer && m_url_timer->IsRunning()) { @@ -1741,7 +1748,7 @@ void SendToPrinterDialog::GetConnection() #endif - if (result.is_success) + if (boost::algorithm::starts_with(url, "bambu:///")) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Connect method tutk"; m_filetransfer_tunnel = std::make_unique(module(), url); @@ -1761,9 +1768,7 @@ void SendToPrinterDialog::GetConnection() } BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " : Tutk url error: ress = " << res; } - }, wxGetApp().get_printer_cloud_provider(), - CameraURLParams{"", "", "", LVL_None, dev_id, agent->get_version(), dev_ver, - boost::lexical_cast(&refresh_agora_url), wxGetApp().app_config->get("slicer_uuid"), SLIC3R_VERSION, true}); + }); } } } diff --git a/src/slic3r/Utils/BBLCloudServiceAgent.cpp b/src/slic3r/Utils/BBLCloudServiceAgent.cpp index df0164c52e..846e4ce509 100644 --- a/src/slic3r/Utils/BBLCloudServiceAgent.cpp +++ b/src/slic3r/Utils/BBLCloudServiceAgent.cpp @@ -1,6 +1,5 @@ #include "BBLCloudServiceAgent.hpp" #include "BBLNetworkPlugin.hpp" -#include "NetworkAgent.hpp" #include #include "Http.hpp" @@ -607,47 +606,13 @@ int BBLCloudServiceAgent::modify_printer_name(std::string dev_id, std::string de // Model Mall & Publishing // ============================================================================ -int BBLCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) +int BBLCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback) { auto& plugin = BBLNetworkPlugin::instance(); auto agent = plugin.get_agent(); auto func = plugin.get_get_camera_url(); if (func && agent) { - auto make_result = [](std::string url) { - CameraURLResult result; - result.url = std::move(url); - result.is_success = result.url.rfind("bambu:///", 0) == 0; - if (result.is_success) { - result.error_code = 0; - } else if (!result.url.empty() && result.url.back() == ']') { - const auto start = result.url.rfind('['); - if (start != std::string::npos && start + 1 < result.url.size() - 1) { - try { - result.error_code = std::stoi(result.url.substr(start + 1, result.url.size() - start - 2)); - } catch (...) { - } - } - } - return result; - }; - if (params.apply_meta) { - auto decorated_callback = [callback = std::move(callback), params = std::move(params), make_result](std::string url) { - CameraURLResult result = make_result(std::move(url)); - if (result.is_success) { - result.url += "&device=" + params.device; - result.url += "&net_ver=" + params.network_version; - result.url += "&dev_ver=" + params.device_version; - result.url += "&refresh_url=" + params.refresh_url; - result.url += "&cli_id=" + params.client_id; - result.url += "&cli_ver=" + params.client_version; - } - callback(std::move(result)); - }; - return func(agent, std::move(dev_id), std::move(decorated_callback)); - } - return func(agent, std::move(dev_id), [callback = std::move(callback), make_result](std::string url) { - callback(make_result(std::move(url))); - }); + return func(agent, dev_id, callback); } return -1; } diff --git a/src/slic3r/Utils/BBLCloudServiceAgent.hpp b/src/slic3r/Utils/BBLCloudServiceAgent.hpp index 59de3e41d3..9f03f0b839 100644 --- a/src/slic3r/Utils/BBLCloudServiceAgent.hpp +++ b/src/slic3r/Utils/BBLCloudServiceAgent.hpp @@ -89,7 +89,7 @@ public: 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 callback, CameraURLParams params) override; + int get_camera_url(std::string dev_id, std::function callback) override; int get_design_staffpick(int offset, int limit, std::function 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; diff --git a/src/slic3r/Utils/ICloudServiceAgent.hpp b/src/slic3r/Utils/ICloudServiceAgent.hpp index e446afab09..556c253641 100644 --- a/src/slic3r/Utils/ICloudServiceAgent.hpp +++ b/src/slic3r/Utils/ICloudServiceAgent.hpp @@ -47,9 +47,6 @@ struct CloudEvent { using AppOnServerConnectedFn = std::function; using AppOnHttpErrorFn = std::function; -struct CameraURLParams; -struct CameraURLResult; - class ICloudServiceAgent { public: virtual ~ICloudServiceAgent() = default; @@ -331,8 +328,7 @@ public: /** * Request live camera streaming URL. */ - virtual int get_camera_url(std::string dev_id, std::function callback, - CameraURLParams params) = 0; + virtual int get_camera_url(std::string dev_id, std::function callback) = 0; /** * Fetch staff-picked designs from model mall. diff --git a/src/slic3r/Utils/IPrinterAgent.hpp b/src/slic3r/Utils/IPrinterAgent.hpp index 4cc3bfaf3b..f584a25d12 100644 --- a/src/slic3r/Utils/IPrinterAgent.hpp +++ b/src/slic3r/Utils/IPrinterAgent.hpp @@ -322,7 +322,6 @@ public: * Populates the MachineObject's DevFilaSystem with fetched filament data. */ virtual bool fetch_filament_info(std::string dev_id) { return false; } - }; } // namespace Slic3r diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index d18d918841..0ff531b5f2 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -508,12 +508,11 @@ int NetworkAgent::modify_printer_name(std::string dev_id, std::string dev_name, return -1; } -int NetworkAgent::get_camera_url(std::string dev_id, std::function callback, - const std::string& provider, CameraURLParams params) +int NetworkAgent::get_camera_url(std::string dev_id, std::function callback, const std::string& provider) { const auto cloud_agent = get_cloud_agent(provider); if (cloud_agent) - return cloud_agent->get_camera_url(std::move(dev_id), std::move(callback), std::move(params)); + return cloud_agent->get_camera_url(std::move(dev_id), std::move(callback)); return -1; } diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 5588305dba..f9fc2129ff 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -5,7 +5,6 @@ #include "libslic3r/ProjectTask.hpp" #include "ICloudServiceAgent.hpp" -#include "PrinterNetworkTypes.hpp" #include #include @@ -113,8 +112,7 @@ public: int get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json, const std::string& provider = ORCA_CLOUD_PROVIDER); int query_bind_status(std::vector query_list, unsigned int* http_code, std::string* http_body, const std::string& provider = ORCA_CLOUD_PROVIDER); int modify_printer_name(std::string dev_id, std::string dev_name, const std::string& provider = ORCA_CLOUD_PROVIDER); - int get_camera_url(std::string dev_id, std::function callback, - const std::string& provider = ORCA_CLOUD_PROVIDER, CameraURLParams params = {}); + int get_camera_url(std::string dev_id, std::function callback, const std::string& provider = ORCA_CLOUD_PROVIDER); int get_design_staffpick(int offset, int limit, std::function callback, const std::string& provider = ORCA_CLOUD_PROVIDER); int start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out, const std::string& provider = ORCA_CLOUD_PROVIDER); int get_model_publish_url(std::string* url, const std::string& provider = ORCA_CLOUD_PROVIDER); diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp index ce41847e33..4419395b4d 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp @@ -1,5 +1,4 @@ #include "OrcaCloudServiceAgent.hpp" -#include "NetworkAgent.hpp" #include "Http.hpp" #include "libslic3r/Utils.hpp" #include "slic3r/GUI/GUI_App.hpp" @@ -2699,12 +2698,11 @@ int OrcaCloudServiceAgent::modify_printer_name(std::string dev_id, std::string d return BAMBU_NETWORK_SUCCESS; } -int OrcaCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) +int OrcaCloudServiceAgent::get_camera_url(std::string dev_id, std::function callback) { - (void) params; BOOST_LOG_TRIVIAL(debug) << "OrcaCloudServiceAgent: get_camera_url (stub)"; if (callback) - callback({}); + callback(""); return BAMBU_NETWORK_SUCCESS; } diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp index 696ba261c6..3ae86ec27c 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp @@ -240,7 +240,7 @@ public: // ======================================================================== // ICloudServiceAgent Interface Implementation - Model Mall & Publishing // ======================================================================== - int get_camera_url(std::string dev_id, std::function callback, CameraURLParams params) override; + int get_camera_url(std::string dev_id, std::function callback) override; int get_design_staffpick(int offset, int limit, std::function 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; diff --git a/src/slic3r/Utils/PrinterNetworkTypes.hpp b/src/slic3r/Utils/PrinterNetworkTypes.hpp index e1c1924d4e..d87be6cdcb 100644 --- a/src/slic3r/Utils/PrinterNetworkTypes.hpp +++ b/src/slic3r/Utils/PrinterNetworkTypes.hpp @@ -31,24 +31,4 @@ enum FileRemote { FR_TutkAgora }; -struct CameraURLParams { - std::string ip_address; - std::string user; - std::string password; - LiveviewLocal protocol; - std::string device; - std::string network_version; - std::string device_version; - std::string refresh_url; - std::string client_id; - std::string client_version; - bool apply_meta{false}; -}; - -struct CameraURLResult { - bool is_success{false}; - std::string url; - int error_code{-1}; -}; - } // namespace Slic3r diff --git a/src/slic3r/plugin/PythonPluginBridge.cpp b/src/slic3r/plugin/PythonPluginBridge.cpp index 5cf44e2f95..40f317c016 100644 --- a/src/slic3r/plugin/PythonPluginBridge.cpp +++ b/src/slic3r/plugin/PythonPluginBridge.cpp @@ -13,6 +13,7 @@ #include #include "PythonInterpreter.hpp" +#include "PluginFsUtils.hpp" #include "PluginConfig.hpp" #include "host/PluginHost.hpp" #include "PyPluginPackage.hpp"