From 8d574ce7063abe671e842588219c9f291fd6672c Mon Sep 17 00:00:00 2001 From: Broncosis Date: Thu, 25 Jun 2026 19:02:39 -0700 Subject: [PATCH 1/3] Match filament presets by filament_id/setting_id from Moonraker lane_data When OrcaSlicer reads lane_data from Moonraker, it now checks filament_id and setting_id fields first (populated by spoolman-lane-sync, ACE Pro, etc.) and attempts to match a filament preset before falling back to the existing material-type matching. - Add PresetCollection::filament_id_by_id_or_name() which tries matching by filament_id, setting_id, preset name (exact then case-insensitive/base-name) - Use it in MoonrakerPrinterAgent::fetch_moonraker_filament_data() for both filament_id and setting_id fields from lane_data - Add name-based fallback in PresetBundle::sync_ams_list() and get_ams_cobox_infos() for user presets that have no filament_id --- src/libslic3r/Preset.cpp | 46 ++++++++++++++++++++++ src/libslic3r/Preset.hpp | 3 ++ src/libslic3r/PresetBundle.cpp | 8 ++++ src/slic3r/Utils/MoonrakerPrinterAgent.cpp | 17 ++++++-- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 948c779137d..d6aeca7ed9f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -3231,6 +3231,52 @@ std::string PresetCollection::filament_id_by_type(const std::string& filament_ty return preset(first_visible_idx_by_type(filament_type)).filament_id; } +std::string PresetCollection::filament_id_by_id_or_name(const std::string& id_or_name) const +{ + if (id_or_name.empty()) + return {}; + size_t start = m_default_suppressed ? m_num_default_presets : 0; + // First pass: match by filament_id (e.g. "GFL00") + for (size_t i = start; i < m_presets.size(); ++i) { + const auto& p = m_presets[i]; + if (p.is_visible && p.is_compatible && p.filament_id == id_or_name) + return p.filament_id; + } + // Second pass: match by setting_id (e.g. "OGFSA04") — most presets have this even without filament_id + for (size_t i = start; i < m_presets.size(); ++i) { + const auto& p = m_presets[i]; + if (p.is_visible && p.is_compatible && p.setting_id == id_or_name) + return p.filament_id.empty() ? p.setting_id : p.filament_id; + } + // Third pass: match by preset name — user presets may have neither filament_id nor setting_id, + // so return the name itself as the identifier for downstream name-based lookup. + for (size_t i = start; i < m_presets.size(); ++i) { + const auto& p = m_presets[i]; + if (p.is_visible && p.is_compatible && p.name == id_or_name) { + if (!p.filament_id.empty()) return p.filament_id; + if (!p.setting_id.empty()) return p.setting_id; + return p.name; + } + } + // Fourth pass: case-insensitive match against full name or base name before '@' + // (e.g. "Esun pla+" matches "eSUN PLA+ @System"). + std::string id_lower = boost::algorithm::to_lower_copy(id_or_name); + for (size_t i = start; i < m_presets.size(); ++i) { + const auto& p = m_presets[i]; + if (!p.is_visible || !p.is_compatible) continue; + std::string name_lower = boost::algorithm::to_lower_copy(p.name); + // Strip trailing " @..." suffix to get the base name. + auto at_pos = name_lower.find(" @"); + std::string base_lower = (at_pos != std::string::npos) ? name_lower.substr(0, at_pos) : name_lower; + if (name_lower == id_lower || base_lower == id_lower) { + if (!p.filament_id.empty()) return p.filament_id; + if (!p.setting_id.empty()) return p.setting_id; + return p.name; + } + } + return {}; +} + std::vector PresetCollection::diameters_of_selected_printer() { std::set diameters; diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index e0d3a52a28e..b0b00ea3154 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -681,6 +681,9 @@ class PresetCollection size_t first_visible_idx_by_type(const std::string& filament_type) const; // Return the filament_id of the best-matching visible preset for the given filament type. std::string filament_id_by_type(const std::string& filament_type) const; + // Return the filament_id of the first visible preset matching by filament_id or preset name. + // Returns empty string if no match found. + std::string filament_id_by_id_or_name(const std::string& id_or_name) const; // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. // If one of the prefered_alternates is compatible, select it. template size_t first_compatible_idx(PreferedCondition prefered_condition) const diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 1ace4db6856..70ea8ed6d88 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -3112,6 +3112,10 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info) } auto iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; }); + // Also try matching by preset name for user presets that have no filament_id. + if (iter == filaments.end()) + iter = std::find_if(filaments.begin(), filaments.end(), + [&filament_id](auto &f) { return f.is_compatible && f.name == filament_id; }); if (iter == filaments.end()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id; auto filament_type = ams.opt_string("filament_type", 0u); @@ -3214,6 +3218,10 @@ unsigned int PresetBundle::sync_ams_list(std::vectorfilaments.filament_id_by_type(tray.tray_type) - : map_filament_type_to_generic_id(tray.tray_type); + // Try to match preset by filament_id, then setting_id from lane_data (spoolman-lane-sync + // stores the OrcaSlicer preset name/id in setting_id; filament_id may be a raw Spoolman DB id). + // Falls back to material-type matching if no preset found. + std::string lane_filament_id = safe_json_string(lane_obj, "filament_id"); + std::string lane_setting_id = safe_json_string(lane_obj, "setting_id"); + tray.tray_info_idx = ""; + if (!lane_filament_id.empty() && bundle) + tray.tray_info_idx = bundle->filaments.filament_id_by_id_or_name(lane_filament_id); + if (tray.tray_info_idx.empty() && !lane_setting_id.empty() && bundle) + tray.tray_info_idx = bundle->filaments.filament_id_by_id_or_name(lane_setting_id); + if (tray.tray_info_idx.empty()) + tray.tray_info_idx = bundle + ? bundle->filaments.filament_id_by_type(tray.tray_type) + : map_filament_type_to_generic_id(tray.tray_type); max_lane_index = std::max(max_lane_index, lane_index); trays.push_back(tray); From e0320d770a21e5a19bec6316e666a48c55f39bb9 Mon Sep 17 00:00:00 2001 From: Broncosis Date: Sun, 28 Jun 2026 11:30:07 -0700 Subject: [PATCH 2/3] Show filament_id as read-only field in filament preset editor --- src/slic3r/GUI/Tab.cpp | 13 +++++++++++++ src/slic3r/GUI/Tab.hpp | 1 + 2 files changed, 14 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 8391bb5d61a..6a3f364fab7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3918,6 +3918,14 @@ void TabFilament::build() line.append_option(optgroup->get_option("nozzle_temperature_range_high")); optgroup->append_line(line); + { + Line id_line = { L("Filament ID"), L("Internal identifier for this filament preset") }; + id_line.append_widget([this](wxWindow* parent) { + return description_line_widget(parent, &m_filament_id_line); + }); + optgroup->append_line(id_line); + } + optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { DynamicPrintConfig &filament_config = m_preset_bundle->filaments.get_edited_preset().config; @@ -4259,6 +4267,10 @@ void TabFilament::update_description_lines() if (m_active_page->title() == "Cooling" && m_cooling_description_line) m_cooling_description_line->SetText(from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()))); + if (m_active_page->title() == "Filament" && m_filament_id_line) { + const std::string& fid = m_presets->get_edited_preset().filament_id; + m_filament_id_line->SetText(fid.empty() ? _L("(none)") : from_u8(fid)); + } //BBS //if (m_active_page->title() == "Filament" && m_volumetric_speed_description_line) // this->update_volumetric_flow_preset_hints(); @@ -4399,6 +4411,7 @@ void TabFilament::clear_pages() m_volumetric_speed_description_line = nullptr; m_cooling_description_line = nullptr; + m_filament_id_line = nullptr; //BBS: GUI refactor m_overrides_options.clear(); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index e654f6c43bf..507f476adaa 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -570,6 +570,7 @@ class TabFilament : public Tab private: ogStaticText* m_volumetric_speed_description_line {nullptr}; ogStaticText* m_cooling_description_line {nullptr}; + ogStaticText* m_filament_id_line {nullptr}; void add_filament_overrides_page(); void update_filament_overrides_page(const DynamicPrintConfig* printers_config); From 587d372befcd57f3c609e1f35956ee3ab005efb3 Mon Sep 17 00:00:00 2001 From: Broncosis Date: Sun, 28 Jun 2026 18:35:39 -0700 Subject: [PATCH 3/3] Fix crash: use line.widget instead of append_widget for filament_id display --- src/slic3r/GUI/Tab.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6a3f364fab7..0d7faa41b2f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3920,9 +3920,9 @@ void TabFilament::build() { Line id_line = { L("Filament ID"), L("Internal identifier for this filament preset") }; - id_line.append_widget([this](wxWindow* parent) { + id_line.widget = [this](wxWindow* parent) { return description_line_widget(parent, &m_filament_id_line); - }); + }; optgroup->append_line(id_line); }