From 37bbdefabc2b05b2087cd51147c422b05f6b11c8 Mon Sep 17 00:00:00 2001 From: grant0013 Date: Wed, 20 May 2026 11:16:35 +0000 Subject: [PATCH] harktech: move CFS filament sync into CrealityPrintAgent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per @SoftFever review on #13752, printer-specific filament sync logic belongs in the agent rather than in Sidebar. This consolidates the previously-duplicated code so all CFS-specific work lives in CrealityPrintAgent. Changes: - New: CrealityPrintAgent::sync_filaments_into_ams_list() — static method that builds a CrealityPrint host from a printer_cfg, queries CFS slots, populates PresetBundle::filament_ams_list, and triggers sync_ams_list(). GUI-free; returns a result struct (Status + counts + detail) so the caller decides what dialog to show. - New: nested CFSAmsListResult struct describing the five possible outcomes (Success / NotCfsCapable / QueryFailed / EmptySlots / NoMatches). - Removed: Sidebar::sync_filaments_from_creality_cfs() entirely (its body is now the agent method). - Plater.hpp loses the declaration; Plater.cpp dispatches to the agent inline within sync_ams_list() and owns only the dialog + post-sync UI refresh (combo updates, layout, preset selection, persistence). Two CFS-related entry points on the agent now coexist: - fetch_filament_info() — agent-driven path; publishes via AmsTrayData and build_ams_payload(). Active when a MachineObject is bound (BBL concept, not currently created for Creality LAN hosts). - sync_filaments_into_ams_list() — explicit-pull path used today by the Sidebar's "Sync filaments" button until the K-series MachineObject work catches up. No user-visible behaviour change — same end-to-end flow, the data work just lives in the agent now. --- src/slic3r/GUI/Plater.cpp | 223 ++++++------------------ src/slic3r/GUI/Plater.hpp | 5 - src/slic3r/Utils/CrealityPrintAgent.cpp | 117 +++++++++++++ src/slic3r/Utils/CrealityPrintAgent.hpp | 56 +++++- 4 files changed, 221 insertions(+), 180 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3e7dd4868d..f7877ad903 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3481,14 +3481,64 @@ void Sidebar::load_ams_list(MachineObject* obj) void Sidebar::sync_ams_list(bool is_from_big_sync_btn) { - // K-series Creality CFS hosts don't bind a MachineObject (that's a BBL - // cloud-connected-printer concept), so the dispatch below would early-return. - // Route to the explicit-pull CFS sync instead. + // K-series Creality CFS hosts don't bind a MachineObject (BBL cloud-printer + // concept), so the dispatch below would early-return. Delegate to + // CrealityPrintAgent which queries CFS state and populates filament_ams_list + // directly, then surface the result here as the appropriate dialog + refresh. if (auto* preset_bundle = wxGetApp().preset_bundle) { - const auto* opt = preset_bundle->printers.get_edited_preset().config - .option>("host_type"); - if (opt && opt->value == htCrealityPrint) { - sync_filaments_from_creality_cfs(); + const auto& printer_cfg = preset_bundle->printers.get_edited_preset().config; + const auto* host_type_opt = printer_cfg.option>("host_type"); + if (host_type_opt && host_type_opt->value == htCrealityPrint) { + wxBusyCursor cursor; + auto result = CrealityPrintAgent::sync_filaments_into_ams_list(printer_cfg, preset_bundle); + using R = CrealityPrintAgent::CFSAmsListResult; + switch (result.status) { + case R::NotCfsCapable: { + MessageDialog dlg(this, + _L("This printer is not a CFS-capable K-series board, or could not be reached at its configured IP."), + _L("Sync filaments from CFS"), wxOK); + dlg.ShowModal(); + return; + } + case R::QueryFailed: { + MessageDialog dlg(this, + _L("Could not read CFS slot information from the printer.") + "\n" + from_u8(result.detail), + _L("Sync filaments from CFS"), wxOK); + dlg.ShowModal(); + return; + } + case R::EmptySlots: { + MessageDialog dlg(this, + _L("No loaded filament slots detected on the CFS."), + _L("Sync filaments from CFS"), wxOK); + dlg.ShowModal(); + return; + } + case R::NoMatches: { + MessageDialog dlg(this, + _L("CFS slots were read but no compatible filament presets resolved. " + "Enable the relevant filament profiles via the Filaments tickbox in printer settings."), + _L("Sync filaments from CFS"), wxOK); + dlg.ShowModal(); + return; + } + case R::Success: + break; // fall through to UI refresh below + } + + // Mirror the BBL post-sync refresh sequence. + auto& filament_presets = preset_bundle->filament_presets; + wxGetApp().plater()->on_filament_count_change(result.applied_filament_count); + for (auto& c : p->combos_filament) + c->update(); + update_filaments_area_height(); + Layout(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(filament_presets[0]); + preset_bundle->export_selections(*wxGetApp().app_config); + update_dynamic_filament_list(); + + BOOST_LOG_TRIVIAL(warning) + << "Sidebar::sync_ams_list: CFS sync applied " << result.applied_filament_count << " slot(s)"; return; } } @@ -3733,165 +3783,6 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "finish pop_finsish_sync_ams_dialog"; } -// Explicit-pull filament sync for Creality K-series printers with CFS. -// -// We can't reuse the BBL path (sync_ams_list above) because it requires a -// MachineObject bound to the device, which is a cloud-connected-printer concept -// that doesn't apply to LAN Moonraker-style hosts like the K2. Instead, we query -// the CFS over the K2's port-9999 WebSocket directly (using the CrealityPrint -// helper added in upstream PR #13291), then route the slot data through the -// existing PresetBundle::sync_ams_list machinery so the filament combo widgets -// get the same correct rebuild as BBL printers. -// -// PresetBundle::sync_ams_list resolves each tray's filament_id against system -// bases (get_preset_base(f) == &f). Combined with the matcher's system-over- -// user tiebreaker, this lands us on the brand-specific shipped preset for the -// loaded spool (e.g. "Hyper PLA @Creality K2 0.4 nozzle") rather than a -// generic-inheriting user copy. We do not override post-sync — local user -// customisations should be applied by the user via the dropdown, not silently -// substituted in. -void Sidebar::sync_filaments_from_creality_cfs() -{ - auto* preset_bundle = wxGetApp().preset_bundle; - if (!preset_bundle) - return; - - const auto& printer_cfg = preset_bundle->printers.get_edited_preset().config; - const auto* host_type_opt = printer_cfg.option>("host_type"); - if (!host_type_opt || host_type_opt->value != htCrealityPrint) { - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: host_type is not crealityprint"; - return; - } - - wxBusyCursor cursor; - - // Build a minimal config for the CrealityPrint helper. We can't pass - // printer_cfg directly because its option set is wider than CrealityPrint's - // constructor expects. - DynamicPrintConfig cfg; - cfg.set_key_value("print_host", new ConfigOptionString(printer_cfg.opt_string("print_host"))); - cfg.set_key_value("print_host_webui", new ConfigOptionString(printer_cfg.opt_string("print_host_webui"))); - cfg.set_key_value("printhost_cafile", new ConfigOptionString(printer_cfg.opt_string("printhost_cafile"))); - cfg.set_key_value("printhost_port", new ConfigOptionString(printer_cfg.opt_string("printhost_port"))); - cfg.set_key_value("printhost_apikey", new ConfigOptionString(printer_cfg.opt_string("printhost_apikey"))); - cfg.set_key_value("printhost_ssl_ignore_revoke", new ConfigOptionBool(printer_cfg.opt_bool("printhost_ssl_ignore_revoke"))); - - CrealityPrint host(&cfg); - if (!host.supports_multi_color_print()) { - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: " << host.model_name() - << " is not CFS-capable"; - MessageDialog dlg(this, - _L("This printer is not a CFS-capable K-series board, or could not be reached at its configured IP."), - _L("Sync filaments from CFS"), wxOK); - dlg.ShowModal(); - return; - } - - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: querying CFS slots on " << host.model_name(); - - const std::string response = host.query_boxes_info(); - - std::vector slots; - int box_count = 0; - std::string parse_err; - if (!CrealityPrintAgent::parse_cfs_response(response, slots, box_count, parse_err)) { - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: CFS query failed (" << parse_err << ")"; - MessageDialog dlg(this, - _L("Could not read CFS slot information from the printer.") + "\n" + parse_err, - _L("Sync filaments from CFS"), wxOK); - dlg.ShowModal(); - return; - } - - if (slots.empty()) { - MessageDialog dlg(this, - _L("No loaded filament slots detected on the CFS."), - _L("Sync filaments from CFS"), wxOK); - dlg.ShowModal(); - return; - } - - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: " << box_count << " CFS box(es), " - << slots.size() << " loaded slot(s)"; - - auto& filaments = preset_bundle->filaments; - - // Build filament_ams_list — same shape as Sidebar::build_filament_ams_list produces - // for BBL printers, consumed by PresetBundle::sync_ams_list. - // Map key encodes (extruder, ams_id, slot_id) — main extruder uses the 0x10000 prefix. - constexpr int kMainExtruder = 0x10000; - std::map new_filament_ams_list; - - for (const auto& s : slots) { - const std::string normalized_type = CrealityPrintAgent::normalize_filament_type(s.filament_type); - const std::string matched_id = CrealityPrintAgent::match_filament_preset( - filaments, s.vendor, s.brand_name, normalized_type); - - DynamicPrintConfig tray_config; - tray_config.set_key_value("filament_id", new ConfigOptionStrings{matched_id}); - tray_config.set_key_value("tag_uid", new ConfigOptionStrings{std::string()}); - tray_config.set_key_value("ams_id", new ConfigOptionStrings{std::to_string(s.box_id)}); - tray_config.set_key_value("slot_id", new ConfigOptionStrings{std::to_string(s.slot_id)}); - tray_config.set_key_value("filament_type", new ConfigOptionStrings{normalized_type}); - const std::string tray_name = std::string(1, char('A' + s.box_id)) + std::to_string(s.slot_id + 1); - tray_config.set_key_value("tray_name", new ConfigOptionStrings{tray_name}); - tray_config.set_key_value("filament_colour", new ConfigOptionStrings{s.color_hex.empty() ? std::string("#FFFFFF") : s.color_hex}); - tray_config.set_key_value("filament_multi_colour", new ConfigOptionStrings{}); - tray_config.set_key_value("filament_colour_type", new ConfigOptionStrings{std::string("0")}); - tray_config.set_key_value("filament_exist", new ConfigOptionBools{true}); - tray_config.set_key_value("filament_slot_placeholder", new ConfigOptionBools{false}); - tray_config.set_key_value("filament_is_support", new ConfigOptionBools{false}); - - const int slot_in_filament_array = s.box_id * 4 + s.slot_id; - const int map_key = kMainExtruder + slot_in_filament_array; - new_filament_ams_list.emplace(map_key, std::move(tray_config)); - - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: slot " << slot_in_filament_array - << " spool {" << s.vendor << " " << s.brand_name << " (" << normalized_type - << ")} -> filament_id=\"" << matched_id << "\""; - } - - preset_bundle->filament_ams_list = new_filament_ams_list; - - std::vector> unknowns; - std::map empty_maps; - MergeFilamentInfo merge_info; - auto n = preset_bundle->sync_ams_list(unknowns, false /*use_map*/, empty_maps, - false /*enable_append*/, merge_info, false /*color_only*/); - - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: PresetBundle::sync_ams_list returned " << n; - - if (n == 0) { - MessageDialog dlg(this, - _L("CFS slots were read but no compatible filament presets resolved. " - "Enable the relevant filament profiles via the Filaments tickbox in printer settings."), - _L("Sync filaments from CFS"), wxOK); - dlg.ShowModal(); - return; - } - - auto& filament_presets = preset_bundle->filament_presets; - - // Mirror the BBL post-sync refresh sequence. - wxGetApp().plater()->on_filament_count_change(n); - for (auto& c : p->combos_filament) - c->update(); - update_filaments_area_height(); - Layout(); - wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(filament_presets[0]); - preset_bundle->export_selections(*wxGetApp().app_config); - update_dynamic_filament_list(); - - BOOST_LOG_TRIVIAL(warning) - << "sync_filaments_from_creality_cfs: applied " << n << " slot(s)"; -} bool Sidebar::should_show_SEMM_buttons() { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 5d31ff9b08..f2089c1c4f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -193,11 +193,6 @@ public: void load_ams_list(MachineObject* obj); std::map build_filament_ams_list(MachineObject* obj); void sync_ams_list(bool is_from_big_sync_btn = false); - // Explicit-pull filament sync for Creality K-series hosts (host_type=crealityprint). - // The BBL-style sync_ams_list path requires a bound MachineObject which is never - // created for LAN Moonraker hosts. sync_ams_list dispatches here when host_type - // is crealityprint, but the function is also usable on its own. - void sync_filaments_from_creality_cfs(); bool sync_extruder_list(); bool need_auto_sync_extruder_list_after_connect_priner(const MachineObject* obj); void update_sync_status(const MachineObject* obj); diff --git a/src/slic3r/Utils/CrealityPrintAgent.cpp b/src/slic3r/Utils/CrealityPrintAgent.cpp index fb5519de06..055049b153 100644 --- a/src/slic3r/Utils/CrealityPrintAgent.cpp +++ b/src/slic3r/Utils/CrealityPrintAgent.cpp @@ -318,4 +318,121 @@ bool CrealityPrintAgent::fetch_filament_info(std::string dev_id) return true; } +CrealityPrintAgent::CFSAmsListResult CrealityPrintAgent::sync_filaments_into_ams_list( + const DynamicPrintConfig& printer_cfg, + PresetBundle* bundle) +{ + CFSAmsListResult result; + if (!bundle) { + result.status = CFSAmsListResult::QueryFailed; + result.detail = "no preset bundle"; + return result; + } + + // Build a minimal config for the CrealityPrint helper. We can't pass printer_cfg + // directly because its option set is wider than CrealityPrint's constructor expects. + DynamicPrintConfig cfg; + cfg.set_key_value("print_host", new ConfigOptionString(printer_cfg.opt_string("print_host"))); + cfg.set_key_value("print_host_webui", new ConfigOptionString(printer_cfg.opt_string("print_host_webui"))); + cfg.set_key_value("printhost_cafile", new ConfigOptionString(printer_cfg.opt_string("printhost_cafile"))); + cfg.set_key_value("printhost_port", new ConfigOptionString(printer_cfg.opt_string("printhost_port"))); + cfg.set_key_value("printhost_apikey", new ConfigOptionString(printer_cfg.opt_string("printhost_apikey"))); + cfg.set_key_value("printhost_ssl_ignore_revoke", new ConfigOptionBool(printer_cfg.opt_bool("printhost_ssl_ignore_revoke"))); + + CrealityPrint host(&cfg); + if (!host.supports_multi_color_print()) { + BOOST_LOG_TRIVIAL(warning) + << "CrealityPrintAgent::sync_filaments_into_ams_list: " << host.model_name() + << " is not CFS-capable"; + result.status = CFSAmsListResult::NotCfsCapable; + return result; + } + + BOOST_LOG_TRIVIAL(warning) + << "CrealityPrintAgent::sync_filaments_into_ams_list: querying CFS slots on " + << host.model_name(); + + const std::string response = host.query_boxes_info(); + + std::vector slots; + int box_count = 0; + std::string parse_err; + if (!parse_cfs_response(response, slots, box_count, parse_err)) { + BOOST_LOG_TRIVIAL(warning) + << "CrealityPrintAgent::sync_filaments_into_ams_list: CFS query failed (" + << parse_err << ")"; + result.status = CFSAmsListResult::QueryFailed; + result.detail = parse_err; + return result; + } + + if (slots.empty()) { + result.status = CFSAmsListResult::EmptySlots; + return result; + } + + BOOST_LOG_TRIVIAL(warning) + << "CrealityPrintAgent::sync_filaments_into_ams_list: " << box_count + << " CFS box(es), " << slots.size() << " loaded slot(s)"; + result.box_count = box_count; + result.loaded_slot_count = static_cast(slots.size()); + + auto& filaments = bundle->filaments; + + // Build filament_ams_list — same shape as Sidebar::build_filament_ams_list produces + // for BBL printers, consumed by PresetBundle::sync_ams_list. + // Map key encodes (extruder, ams_id, slot_id) — main extruder uses the 0x10000 prefix. + constexpr int kMainExtruder = 0x10000; + std::map new_filament_ams_list; + + for (const auto& s : slots) { + const std::string normalized_type = normalize_filament_type(s.filament_type); + const std::string matched_id = match_filament_preset( + filaments, s.vendor, s.brand_name, normalized_type); + + DynamicPrintConfig tray_config; + tray_config.set_key_value("filament_id", new ConfigOptionStrings{matched_id}); + tray_config.set_key_value("tag_uid", new ConfigOptionStrings{std::string()}); + tray_config.set_key_value("ams_id", new ConfigOptionStrings{std::to_string(s.box_id)}); + tray_config.set_key_value("slot_id", new ConfigOptionStrings{std::to_string(s.slot_id)}); + tray_config.set_key_value("filament_type", new ConfigOptionStrings{normalized_type}); + const std::string tray_name = std::string(1, char('A' + s.box_id)) + std::to_string(s.slot_id + 1); + tray_config.set_key_value("tray_name", new ConfigOptionStrings{tray_name}); + tray_config.set_key_value("filament_colour", new ConfigOptionStrings{s.color_hex.empty() ? std::string("#FFFFFF") : s.color_hex}); + tray_config.set_key_value("filament_multi_colour", new ConfigOptionStrings{}); + tray_config.set_key_value("filament_colour_type", new ConfigOptionStrings{std::string("0")}); + tray_config.set_key_value("filament_exist", new ConfigOptionBools{true}); + tray_config.set_key_value("filament_slot_placeholder", new ConfigOptionBools{false}); + tray_config.set_key_value("filament_is_support", new ConfigOptionBools{false}); + + const int slot_in_filament_array = s.box_id * 4 + s.slot_id; + const int map_key = kMainExtruder + slot_in_filament_array; + new_filament_ams_list.emplace(map_key, std::move(tray_config)); + + BOOST_LOG_TRIVIAL(warning) + << "CrealityPrintAgent::sync_filaments_into_ams_list: slot " + << slot_in_filament_array << " spool {" << s.vendor << " " << s.brand_name + << " (" << normalized_type << ")} -> filament_id=\"" << matched_id << "\""; + } + + bundle->filament_ams_list = new_filament_ams_list; + + std::vector> unknowns; + std::map empty_maps; + MergeFilamentInfo merge_info; + const unsigned int n = bundle->sync_ams_list(unknowns, false /*use_map*/, empty_maps, + false /*enable_append*/, merge_info, + false /*color_only*/); + + BOOST_LOG_TRIVIAL(warning) + << "CrealityPrintAgent::sync_filaments_into_ams_list: PresetBundle::sync_ams_list returned " << n; + result.applied_filament_count = static_cast(n); + if (n == 0) { + result.status = CFSAmsListResult::NoMatches; + return result; + } + + result.status = CFSAmsListResult::Success; + return result; +} } // namespace Slic3r diff --git a/src/slic3r/Utils/CrealityPrintAgent.hpp b/src/slic3r/Utils/CrealityPrintAgent.hpp index 288aa8a9d8..cc231bf942 100644 --- a/src/slic3r/Utils/CrealityPrintAgent.hpp +++ b/src/slic3r/Utils/CrealityPrintAgent.hpp @@ -9,23 +9,27 @@ namespace Slic3r { class PresetCollection; +class PresetBundle; +class DynamicPrintConfig; // Filament sync for Creality K-series printers with CFS. // // Inherits MoonrakerPrinterAgent for all communication / certificates / discovery / -// binding / print-job operations. Overrides fetch_filament_info() to query Creality's -// web-server (port 9999, websocket) for CFS slot state and publish it via the existing -// AmsTrayData / build_ams_payload() path so loaded CFS slots appear in Orca's filament -// UI like a Bambu AMS. +// binding / print-job operations. Owns two related entry points: +// +// * fetch_filament_info() — the agent-driven path; queries CFS and publishes via +// AmsTrayData / build_ams_payload(). Reached when a MachineObject is bound (BBL +// concept; not currently created for Creality LAN hosts). +// +// * sync_filaments_into_ams_list() — the explicit-pull path used by Sidebar's +// "Sync filaments" button. Same data flow up to the parse step, but writes +// directly into PresetBundle::filament_ams_list and triggers sync_ams_list() +// so the UI updates without a MachineObject. Returns a result struct so the +// caller can show appropriate dialogs without the agent touching wxWidgets. // // Model detection delegated to CrealityPrint::supports_multi_color_print() (PR #13291). // For non-CFS K-series boards or when the WS query fails, falls back to the base // MoonrakerPrinterAgent behaviour. -// -// The CFS-slot parser and preset matcher are also exposed as public statics so the -// Sidebar's manual "Sync from CFS" path can reuse them without going through the -// agent dispatch (which only fires when a MachineObject is bound — a BBL concept that -// doesn't apply to Moonraker-style hosts). class CrealityPrintAgent final : public MoonrakerPrinterAgent { @@ -65,6 +69,40 @@ public: const std::string& vendor, const std::string& brand_name, const std::string& base_type); + + // Return status from sync_filaments_into_ams_list(). The agent code is GUI-free + // so the caller (Sidebar) decides what dialog (if any) to show for each case. + struct CFSAmsListResult + { + enum Status { + Success, // bundle->filament_ams_list populated and sync_ams_list applied + NotCfsCapable, // printer model has no CFS, or unreachable at configured IP + QueryFailed, // CFS query returned bad JSON / HTTP error (detail set) + EmptySlots, // CFS responded but reports no loaded slots + NoMatches // slots read but PresetBundle::sync_ams_list returned 0 + }; + Status status = Success; + std::string detail; // free-text error / extra info, may be empty + int box_count = 0; // number of CFS boxes reported by the printer + int loaded_slot_count = 0; // number of physically loaded slots seen + int applied_filament_count = 0; // result of PresetBundle::sync_ams_list + }; + + // Explicit-pull entry point for the Sidebar "Sync filaments" button. + // + // Builds a CrealityPrint host from `printer_cfg`, queries CFS slot state over its + // port-9999 WebSocket, populates `bundle->filament_ams_list`, and triggers + // `bundle->sync_ams_list()` so the standard preset-reconciliation path runs. Pure + // data / model work — does not touch wxWidgets; the caller is responsible for + // showing dialogs and refreshing the UI based on the returned Status. + // + // Single source of truth for CFS-to-filament sync (was previously duplicated in + // Sidebar). The agent-driven fetch_filament_info() path still publishes through + // AmsTrayData / build_ams_payload() and will become the primary path once Creality + // K-series hosts have a MachineObject created for them. + static CFSAmsListResult sync_filaments_into_ams_list( + const DynamicPrintConfig& printer_cfg, + PresetBundle* bundle); }; } // namespace Slic3r