From 334ba8f84d37f664caf0439f8f026824b2852862 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Tue, 6 Oct 2026 20:05:39 +0800 Subject: [PATCH] chore: reconcile with PR 15710 squash base --- src/slic3r/GUI/DeviceManager.hpp | 2 +- src/slic3r/GUI/Monitor.cpp | 5 +- src/slic3r/GUI/ReleaseNote.cpp | 38 +++++++++------ src/slic3r/GUI/ReleaseNote.hpp | 2 +- src/slic3r/GUI/SelectMachine.cpp | 6 +-- src/slic3r/GUI/SendToPrinter.cpp | 42 ++++++---------- src/slic3r/Utils/BBLPrinterAgent.cpp | 48 ++++++++----------- src/slic3r/Utils/BBLPrinterAgent.hpp | 11 ++--- src/slic3r/Utils/Http.cpp | 14 ++++-- src/slic3r/Utils/MoonrakerPrinterAgent.cpp | 47 ++++++++++-------- src/slic3r/Utils/OrcaPrinterAgent.cpp | 2 + src/slic3r/Utils/OrcaPrinterAgent.hpp | 1 - src/slic3r/Utils/QidiPrinterAgent.cpp | 7 +++ src/slic3r/Utils/QidiPrinterAgent.hpp | 1 + src/slic3r/Utils/SnapmakerPrinterAgent.cpp | 9 ++++ tests/slic3rutils/test_printer_agent.cpp | 18 +++---- tests/slic3rutils/test_qidi_printer_agent.cpp | 3 ++ 17 files changed, 136 insertions(+), 120 deletions(-) diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 5c14e32f77..c4a63cb588 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -314,7 +314,7 @@ public: bool ams_support_virtual_tray { true }; time_t ams_user_setting_start = 0; time_t ams_switch_filament_start = 0; - AmsStatusMain ams_status_main; + AmsStatusMain ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_IDLE; int ams_status_sub; int ams_version = 0; diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index cf75e3defb..2c800ecebb 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -297,7 +297,10 @@ void MonitorPanel::select_machine(std::string machine_sn) void MonitorPanel::on_timer(wxTimerEvent& event) { - if (update_flag) { + // MediaPlayCtrl may yield the event loop while it joins its camera worker + // during window teardown. Do not let a queued monitor refresh touch panels + // that are already being destroyed. + if (!wxGetApp().is_closing() && update_flag) { update_all(); //Layout(); } diff --git a/src/slic3r/GUI/ReleaseNote.cpp b/src/slic3r/GUI/ReleaseNote.cpp index a56f7ed9c7..9a0bc54b6e 100644 --- a/src/slic3r/GUI/ReleaseNote.cpp +++ b/src/slic3r/GUI/ReleaseNote.cpp @@ -1823,7 +1823,7 @@ void InputIpAddressDialog::set_machine_obj(MachineObject* obj) auto str_ip = m_input_ip->GetTextCtrl()->GetValue(); auto str_access_code = m_input_access_code->GetTextCtrl()->GetValue(); // ORCA enabling / disabling buttons with conditions enough to change its style - m_button_ok->Enable(isIp(str_ip.ToStdString()) && + m_button_ok->Enable(isValidEndpoint(str_ip.ToStdString()) && (str_access_code.IsEmpty() || str_access_code.Length() >= 8)); Layout(); @@ -1861,19 +1861,29 @@ void InputIpAddressDialog::update_test_msg(wxString msg,bool connected) Fit(); } -bool InputIpAddressDialog::isIp(std::string ipstr) +bool InputIpAddressDialog::isValidEndpoint(std::string endpoint) { - istringstream ipstream(ipstr); - int num[4]; - char point[3]; - string end; - ipstream >> num[0] >> point[0] >> num[1] >> point[1] >> num[2] >> point[2] >> num[3] >> end; - for (int i = 0; i < 3; ++i) { - if (num[i] < 0 || num[i]>255) return false; - if (point[i] != '.') return false; - } - if (num[3] < 0 || num[3]>255) return false; - if (!end.empty()) return false; + if (endpoint.empty() || std::any_of(endpoint.begin(), endpoint.end(), [](unsigned char c) { + return std::isspace(c) != 0; + })) + return false; + + const bool has_http_scheme = endpoint.rfind("http://", 0) == 0; + const bool has_https_scheme = endpoint.rfind("https://", 0) == 0; + const auto scheme_pos = endpoint.find("://"); + if (scheme_pos != std::string::npos && !has_http_scheme && !has_https_scheme) + return false; + + std::string port; + const std::string host = Http::get_host_from_url(endpoint, &port); + if (host.empty()) + return false; + + // get_host_from_url returns its input when libcurl cannot parse it. For a + // URL with a scheme, that means a failed parse still needs to be rejected. + if (scheme_pos != std::string::npos && host == endpoint) + return false; + return true; } @@ -2176,7 +2186,7 @@ void InputIpAddressDialog::on_text(wxCommandEvent &evt) // ORCA enabling / disabling buttons with conditions enough to change its style bool valid_access_code_length = str_access_code.IsEmpty() || str_access_code.Length() >= 8; - bool enable_btns = isIp(str_ip.ToStdString()) && valid_access_code_length && invalid_access_code; + bool enable_btns = isValidEndpoint(str_ip.ToStdString()) && valid_access_code_length && invalid_access_code; m_button_manual_setup->Enable(enable_btns); m_button_ok->Enable(enable_btns); diff --git a/src/slic3r/GUI/ReleaseNote.hpp b/src/slic3r/GUI/ReleaseNote.hpp index 23ecb2a9ff..1234d59c60 100644 --- a/src/slic3r/GUI/ReleaseNote.hpp +++ b/src/slic3r/GUI/ReleaseNote.hpp @@ -360,7 +360,7 @@ public: void update_title(wxString title); void set_machine_obj(MachineObject* obj); void update_test_msg(wxString msg, bool connected); - bool isIp(std::string ipstr); + bool isValidEndpoint(std::string endpoint); void check_ip_address_failed(int result); void on_check_ip_address_failed(wxCommandEvent& evt); void on_ok(wxMouseEvent& evt); diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 8655443f3e..5d51e5429f 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -2378,9 +2378,7 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector 1) msg_text = wxString::Format(_L("Filament %s does not match the filament in AMS slot %s. Please update the printer firmware to support AMS slot assignment."), params[0], params[1]); @@ -2406,7 +2404,7 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector #include @@ -22,9 +23,8 @@ using json = nlohmann::json; #include #include #include -#include -#include -#include + +namespace Slic3r { class ICloudServiceAgent; } namespace Slic3r { @@ -156,36 +156,27 @@ void BBLPrinterAgent::set_cloud_agent(std::shared_ptr cloud) // Communication // ============================================================================ -std::string BBLPrinterAgent::ams_refresh_rfid_gcode(const std::string& tray_id) +int BBLPrinterAgent::command_ams_refresh_rfid(std::string dev_id, int ams_id, int slot_id, int sequence_id, bool lan_mode) { - return (boost::format("M620 R%1% \n") % tray_id).str(); -} - -std::string BBLPrinterAgent::ams_calibrate_gcode(int ams_id) -{ - return (boost::format("M620 C%1% \n") % ams_id).str(); -} - -std::string BBLPrinterAgent::ams_select_tray_gcode(const std::string& tray_id) -{ - return (boost::format("M620 P%1% \n") % tray_id).str(); -} - -int BBLPrinterAgent::command_ams_refresh_rfid(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) -{ - const std::string gcode = ams_refresh_rfid_gcode(tray_id); - BOOST_LOG_TRIVIAL(trace) << "ams_debug: gcode_cmd" << gcode; nlohmann::json j; - j["print"]["command"] = "gcode_line"; - j["print"]["param"] = gcode; - j["print"]["sequence_id"] = std::to_string(sequence_id); + if (ams_id == -1) { + const std::string gcode = (boost::format("M620 R%1% \n") % slot_id).str(); + j["print"]["command"] = "gcode_line"; + j["print"]["param"] = gcode; + j["print"]["sequence_id"] = std::to_string(sequence_id); + return publish(dev_id, j, lan_mode); + } + + j["print"]["command"] = "ams_get_rfid"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["ams_id"] = ams_id; + j["print"]["slot_id"] = slot_id; return publish(dev_id, j, lan_mode); } int BBLPrinterAgent::command_ams_calibrate(std::string dev_id, int ams_id, int sequence_id, bool lan_mode) { - const std::string gcode = ams_calibrate_gcode(ams_id); - BOOST_LOG_TRIVIAL(trace) << "ams_debug: gcode_cmd" << gcode; + const std::string gcode = (boost::format("M620 C%1% \n") % ams_id).str(); nlohmann::json j; j["print"]["command"] = "gcode_line"; j["print"]["param"] = gcode; @@ -195,8 +186,7 @@ int BBLPrinterAgent::command_ams_calibrate(std::string dev_id, int ams_id, int s int BBLPrinterAgent::command_ams_select_tray(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) { - const std::string gcode = ams_select_tray_gcode(tray_id); - BOOST_LOG_TRIVIAL(trace) << "ams_debug: gcode_cmd" << gcode; + const std::string gcode = (boost::format("M620 P%1% \n") % tray_id).str(); nlohmann::json j; j["print"]["command"] = "gcode_line"; j["print"]["param"] = gcode; diff --git a/src/slic3r/Utils/BBLPrinterAgent.hpp b/src/slic3r/Utils/BBLPrinterAgent.hpp index f2e9a1d28f..b5d83c278a 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.hpp +++ b/src/slic3r/Utils/BBLPrinterAgent.hpp @@ -7,6 +7,9 @@ #include #include #include +#include + +namespace Slic3r { class ICloudServiceAgent; } namespace Slic3r { @@ -30,19 +33,15 @@ public: // Communication int send_message(std::string dev_id, std::string json_str, int qos, int flag) override; - static std::string ams_refresh_rfid_gcode(const std::string& tray_id); - static std::string ams_calibrate_gcode(int ams_id); - static std::string ams_select_tray_gcode(const std::string& tray_id); - int command_ams_refresh_rfid(std::string dev_id, std::string tray_id, int sequence_id, bool lan_mode) override; + int command_ams_refresh_rfid(std::string dev_id, int ams_id, int slot_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_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; std::string default_lan_username() const override { return "bblp"; } - int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override; + int connect_printer(const PrinterConnectionParams& params) 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 default_lan_username() const override { return "bblp"; } // Certificates int check_cert() override; diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index c5c4946b32..552c0b6a39 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -1,6 +1,10 @@ #include "Http.hpp" #include "libslic3r_version.h" +#include +#include +#include +#include #include #include #include @@ -135,7 +139,7 @@ struct Http::priv std::string error_buffer; // Used for CURLOPT_ERRORBUFFER std::string headers; size_t limit; - bool cancel; + std::atomic_bool cancel; std::unique_ptr putFile; std::thread io_thread; @@ -273,9 +277,9 @@ int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_o self->progressfn(progress, cb_cancel); } - if (cb_cancel) { self->cancel = true; } + if (cb_cancel) { self->cancel.store(true); } - return self->cancel; + return self->cancel.load(); } int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow) @@ -486,7 +490,7 @@ void Http::priv::http_perform() if (res != CURLE_OK) { if (res == CURLE_ABORTED_BY_CALLBACK) { - if (cancel) { + if (cancel.load()) { // The abort comes from the request being cancelled programatically Progress dummyprogress(0, 0, 0, 0, std::string()); bool cancel = true; @@ -797,7 +801,7 @@ void Http::perform_sync() void Http::cancel() { - if (p) { p->cancel = true; } + if (p) { p->cancel.store(true); } } void Http::print() const diff --git a/src/slic3r/Utils/MoonrakerPrinterAgent.cpp b/src/slic3r/Utils/MoonrakerPrinterAgent.cpp index 2734a38080..8990a6cd00 100644 --- a/src/slic3r/Utils/MoonrakerPrinterAgent.cpp +++ b/src/slic3r/Utils/MoonrakerPrinterAgent.cpp @@ -21,8 +21,16 @@ #include #include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -32,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -42,10 +51,16 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include namespace { @@ -617,7 +632,7 @@ int MoonrakerPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusF return BAMBU_NETWORK_ERR_CANCELED; } - // Start print via Moonraker's print API, referencing the file we just uploaded. + // Start print via Moonraker's G-code script endpoint, referencing the file we just uploaded. if (update_fn) update_fn(PrintingStageSending, 0, "Starting print..."); std::string gcode = "SDCARD_PRINT_FILE FILENAME=" + upload_filename; @@ -1431,12 +1446,8 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std:: } response["print"]["param"] = gcode; - auto [base_url, api_key] = connection_snapshot(); - enqueue_command([this, dev_id, response = std::move(response), base_url = std::move(base_url), - api_key = std::move(api_key)]() mutable { - response["print"]["result"] = send_gcode(dev_id, response["print"]["param"].get(), base_url, api_key) - ? "success" - : "failed"; + send_gcode_async(dev_id, gcode, [this, dev_id, response](bool success) mutable { + response["print"]["result"] = success ? "success" : "failed"; dispatch_message(dev_id, response.dump()); }); return BAMBU_NETWORK_SUCCESS; @@ -1470,11 +1481,7 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std:: if (json["print"].contains("temp") && json["print"]["temp"].is_number()) { int temp = json["print"]["temp"].get(); std::string gcode = "SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=" + std::to_string(temp); - auto [base_url, api_key] = connection_snapshot(); - enqueue_command([this, dev_id, gcode = std::move(gcode), base_url = std::move(base_url), - api_key = std::move(api_key)] { - send_gcode(dev_id, gcode, base_url, api_key); - }); + send_gcode_async(dev_id, gcode); return BAMBU_NETWORK_SUCCESS; } } @@ -1489,11 +1496,7 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std:: } std::string heater = (extruder_idx == 0) ? "extruder" : "extruder" + std::to_string(extruder_idx); std::string gcode = "SET_HEATER_TEMPERATURE HEATER=" + heater + " TARGET=" + std::to_string(temp); - auto [base_url, api_key] = connection_snapshot(); - enqueue_command([this, dev_id, gcode = std::move(gcode), base_url = std::move(base_url), - api_key = std::move(api_key)] { - send_gcode(dev_id, gcode, base_url, api_key); - }); + send_gcode_async(dev_id, gcode); return BAMBU_NETWORK_SUCCESS; } } @@ -1501,10 +1504,7 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std:: // why: no current OrcaSlicer sender emits the "home" discriminator; // GUI homing uses gcode_line with G28 instead. if (cmd == "home") { - auto [base_url, api_key] = connection_snapshot(); - enqueue_command([this, dev_id, base_url = std::move(base_url), api_key = std::move(api_key)] { - send_gcode(dev_id, "G28", base_url, api_key); - }); + send_gcode_async(dev_id, "G28"); return BAMBU_NETWORK_SUCCESS; } } @@ -2010,6 +2010,11 @@ void MoonrakerPrinterAgent::send_gcode_async(const std::string& dev_id, const st } bool MoonrakerPrinterAgent::send_gcode(const std::string& dev_id, const std::string& gcode) const +{ + return send_gcode_sync(dev_id, gcode); +} + +bool MoonrakerPrinterAgent::send_gcode_sync(const std::string& dev_id, const std::string& gcode) const { // why: snapshot then release - see post_print_action. return send_gcode(dev_id, gcode, get_connection_settings()); diff --git a/src/slic3r/Utils/OrcaPrinterAgent.cpp b/src/slic3r/Utils/OrcaPrinterAgent.cpp index cb8b38acc8..6324e41465 100644 --- a/src/slic3r/Utils/OrcaPrinterAgent.cpp +++ b/src/slic3r/Utils/OrcaPrinterAgent.cpp @@ -7,6 +7,8 @@ #include #include "bambu_networking.hpp" +namespace Slic3r { class ICloudServiceAgent; } + namespace Slic3r { const std::string OrcaPrinterAgent_VERSION = "0.0.1"; diff --git a/src/slic3r/Utils/OrcaPrinterAgent.hpp b/src/slic3r/Utils/OrcaPrinterAgent.hpp index e0ad2d8cb8..4b299f6064 100644 --- a/src/slic3r/Utils/OrcaPrinterAgent.hpp +++ b/src/slic3r/Utils/OrcaPrinterAgent.hpp @@ -2,7 +2,6 @@ #define __ORCA_PRINTER_AGENT_HPP__ #include "IPrinterAgent.hpp" -#include "ICloudServiceAgent.hpp" #include "bambu_networking.hpp" #include #include diff --git a/src/slic3r/Utils/QidiPrinterAgent.cpp b/src/slic3r/Utils/QidiPrinterAgent.cpp index 2d25253d8d..175c3f88f7 100644 --- a/src/slic3r/Utils/QidiPrinterAgent.cpp +++ b/src/slic3r/Utils/QidiPrinterAgent.cpp @@ -1,19 +1,26 @@ #include "QidiPrinterAgent.hpp" #include "Http.hpp" +#include "MoonrakerPrinterAgent.hpp" #include "IPrinterAgent.hpp" +#include "bambu_networking.hpp" #include "libslic3r/PresetBundle.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "nlohmann/json.hpp" +#include #include #include #include #include #include #include "libslic3r/Preset.hpp" +#include #include #include #include +#include +#include +#include using json = nlohmann::json; diff --git a/src/slic3r/Utils/QidiPrinterAgent.hpp b/src/slic3r/Utils/QidiPrinterAgent.hpp index cbbdce8cab..0c83fa3d95 100644 --- a/src/slic3r/Utils/QidiPrinterAgent.hpp +++ b/src/slic3r/Utils/QidiPrinterAgent.hpp @@ -3,6 +3,7 @@ #include "IPrinterAgent.hpp" #include "MoonrakerPrinterAgent.hpp" +#include "bambu_networking.hpp" #include "nlohmann/json_fwd.hpp" #include diff --git a/src/slic3r/Utils/SnapmakerPrinterAgent.cpp b/src/slic3r/Utils/SnapmakerPrinterAgent.cpp index d5bd4a7217..71ec358b62 100644 --- a/src/slic3r/Utils/SnapmakerPrinterAgent.cpp +++ b/src/slic3r/Utils/SnapmakerPrinterAgent.cpp @@ -1,14 +1,23 @@ #include "SnapmakerPrinterAgent.hpp" #include "Http.hpp" +#include "MoonrakerPrinterAgent.hpp" #include "IPrinterAgent.hpp" +#include "bambu_networking.hpp" #include "libslic3r/PresetBundle.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "nlohmann/json.hpp" +#include #include #include +#include #include #include +#include +#include +#include "libslic3r/Preset.hpp" +#include +#include using json = nlohmann::json; diff --git a/tests/slic3rutils/test_printer_agent.cpp b/tests/slic3rutils/test_printer_agent.cpp index 693c36f316..4a82c69664 100644 --- a/tests/slic3rutils/test_printer_agent.cpp +++ b/tests/slic3rutils/test_printer_agent.cpp @@ -1,12 +1,15 @@ #include #include +#include #include +#include #include #include #include #include +#include "catch2/catch_approx.hpp" #include "python_test_support.hpp" #include @@ -15,9 +18,14 @@ #include #include #include -#include +#include #include #include +#include +#include + +namespace Slic3r { class ICloudServiceAgent; } +namespace Slic3r { class IPrinterAgent; } using namespace Slic3r; namespace py = pybind11; @@ -70,14 +78,6 @@ TEST_CASE("Moonraker parses nozzle diameter from raw config and tolerates missin CHECK(MoonrakerParserProbe::parse_nozzle_diameter(missing_response) == 0.0f); } -// why: these builders preserve the Bambu firmware dialect byte-for-byte, including its trailing space. -TEST_CASE("unit: BBL AMS gcode builders preserve command bytes", "[unit][bbl]") -{ - CHECK(BBLPrinterAgent::ams_refresh_rfid_gcode("123") == "M620 R123 \n"); - CHECK(BBLPrinterAgent::ams_calibrate_gcode(123) == "M620 C123 \n"); - CHECK(BBLPrinterAgent::ams_select_tray_gcode("123") == "M620 P123 \n"); -} - // why: an agent without a Bambu-dialect translation must refuse these commands before any network or wx path. TEST_CASE("unit: default AMS commands report not supported", "[unit][moonraker]") { diff --git a/tests/slic3rutils/test_qidi_printer_agent.cpp b/tests/slic3rutils/test_qidi_printer_agent.cpp index 2d3307381a..ad770e9647 100644 --- a/tests/slic3rutils/test_qidi_printer_agent.cpp +++ b/tests/slic3rutils/test_qidi_printer_agent.cpp @@ -1,3 +1,6 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers.hpp" +#include "catch2/matchers/catch_matchers_string.hpp" #include #include