Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/libslic3r/PresetBundle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,135 @@ std::optional<FilamentBaseInfo> PresetBundle::get_filament_by_filament_id(const
return std::nullopt;
}

// GitHub #11937 helper: populate FilamentBaseInfo from a preset. Mirrors the field extraction in
// get_filament_by_filament_id() so both lookups report identical data for the same preset.
static FilamentBaseInfo make_filament_base_info(const Preset& filament_preset)
{
const auto& config = filament_preset.config;
FilamentBaseInfo info;
info.filament_id = filament_preset.filament_id;
info.is_system = filament_preset.is_system;
info.filament_name = filament_preset.alias;
info.setting_id = filament_preset.setting_id;
if (config.has("filament_is_support"))
info.is_support = config.option<ConfigOptionBools>("filament_is_support")->values[0];
if (config.has("filament_type"))
info.filament_type = config.option<ConfigOptionStrings>("filament_type")->values[0];
if (config.has("filament_vendor"))
info.vendor = config.option<ConfigOptionStrings>("filament_vendor")->values[0];
if (config.has("nozzle_temperature_range_high"))
info.nozzle_temp_range_high = config.option<ConfigOptionInts>("nozzle_temperature_range_high")->values[0];
if (config.has("nozzle_temperature_range_low"))
info.nozzle_temp_range_low = config.option<ConfigOptionInts>("nozzle_temperature_range_low")->values[0];
if (config.has("temperature_vitrification"))
info.temperature_vitrification = config.option<ConfigOptionInts>("temperature_vitrification")->values[0];
if (config.has("filament_printable"))
info.filament_printable = config.option<ConfigOptionInts>("filament_printable")->values[0];
if (config.has("filament_extruder_compatibility"))
info.set_filament_extruder_compatibility(config.option<ConfigOptionInts>("filament_extruder_compatibility")->values[0]);
return info;
}

std::optional<FilamentBaseInfo> PresetBundle::resolve_filament_for_spool(const std::string& stored_id,
const std::string& vendor,
const std::string& material_type,
bool* exact_match) const
{
if (exact_match)
*exact_match = false;

// Step 1 — the normal case: the spool stored a real Preset::filament_id.
if (!stored_id.empty()) {
if (auto info = get_filament_by_filament_id(stored_id)) {
if (exact_match)
*exact_match = true;
return info;
}
}

// Step 2 — the spool stored a setting_id (cloud user-settings id) instead of a filament_id.
// Prefer a preset that actually carries a filament_id so downstream AMS commands have
// something meaningful to send.
if (!stored_id.empty()) {
const Preset* setting_hit = nullptr;
for (auto iter = filaments.begin(); iter != filaments.end(); ++iter) {
const Preset& preset = *iter;
if (preset.setting_id != stored_id)
continue;
if (!preset.filament_id.empty()) {
setting_hit = &preset;
break;
}
if (!setting_hit)
setting_hit = &preset;
}
if (setting_hit) {
if (exact_match)
*exact_match = true;
return make_filament_base_info(*setting_hit);
}
}

// Steps 3 and 4 need a material type to work with.
if (material_type.empty())
return std::nullopt;

auto preset_type_of = [](const Preset& preset) -> std::string {
if (!preset.config.has("filament_type"))
return std::string();
const auto& values = preset.config.option<ConfigOptionStrings>("filament_type")->values;
return values.empty() ? std::string() : values.front();
};
auto preset_vendor_of = [](const Preset& preset) -> std::string {
if (!preset.config.has("filament_vendor"))
return std::string();
const auto& values = preset.config.option<ConfigOptionStrings>("filament_vendor")->values;
return values.empty() ? std::string() : values.front();
};

// Step 3 — same vendor and material type. Prefer a system preset, but accept a user preset
// (which inherits its base's filament_id) when that is all the user has.
if (!vendor.empty()) {
const Preset* fallback = nullptr;
for (auto iter = filaments.begin(); iter != filaments.end(); ++iter) {
const Preset& preset = *iter;
if (preset.filament_id.empty())
continue;
if (preset_vendor_of(preset) != vendor || preset_type_of(preset) != material_type)
continue;
if (preset.is_system)
return make_filament_base_info(preset);
if (!fallback)
fallback = &preset;
}
if (fallback)
return make_filament_base_info(*fallback);
}

// Step 4 — last resort: the shipped "Generic <type>" system preset. This is what makes a
// hand-typed third-party filament usable in the AMS at all: the printer only needs sane
// temperature/type parameters for the slot, which the Generic profile provides.
const std::string generic_name = "Generic " + material_type;
const Preset* generic_any = nullptr;
for (auto iter = filaments.begin(); iter != filaments.end(); ++iter) {
const Preset& preset = *iter;
if (preset.filament_id.empty())
continue;
if (preset_type_of(preset) != material_type)
continue;
const bool name_is_generic = boost::istarts_with(preset.name, generic_name) ||
boost::istarts_with(preset.alias, generic_name);
if (name_is_generic && preset.is_system)
return make_filament_base_info(preset);
if (name_is_generic && !generic_any)
generic_any = &preset;
}
if (generic_any)
return make_filament_base_info(*generic_any);

return std::nullopt;
}

//BBS: load project embedded presets
PresetsConfigSubstitutions PresetBundle::load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule)
{
Expand Down
23 changes: 23 additions & 0 deletions src/libslic3r/PresetBundle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,29 @@ class PresetBundle

std::optional<FilamentBaseInfo> get_filament_by_filament_id(const std::string& filament_id, const std::string& printer_name = std::string(), bool only_system = false) const;

// GitHub #11937: Filament Manager spools store an id in FilamentSpool::setting_id that is
// compared against Preset::filament_id everywhere. Spools created through the web
// "Add filament" dialog may instead carry a cloud user-settings id, or nothing at all when
// the user typed a third-party brand by hand. Those spools used to fail
// get_filament_by_filament_id() outright and were rendered as unselectable
// "Unsupported Filaments".
//
// resolve_filament_for_spool() is a tolerant lookup for exactly that situation. Resolution
// order, first hit wins:
// 1. exact Preset::filament_id match (the normal, correct case)
// 2. Preset::setting_id match (spool stored the wrong kind of id)
// 3. vendor + filament_type match on a base preset
// 4. "Generic <filament_type>" system preset
// `exact_match` reports whether step 1 or 2 succeeded, so callers can flag an approximate
// resolution to the user without blocking the operation.
//
// Purely local: never consults the cloud catalogue or RFID data, so it behaves identically
// in LAN mode.
std::optional<FilamentBaseInfo> resolve_filament_for_spool(const std::string& stored_id,
const std::string& vendor,
const std::string& material_type,
bool* exact_match = nullptr) const;

// Load support recommended params from JSON file
void load_support_recommended_params();
// Get support recommended params by (support_material, model_material)
Expand Down
25 changes: 22 additions & 3 deletions src/slic3r/GUI/AMSMaterialsSetting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,17 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent& event)
auto* store = wxGetApp().fila_manager_store();
const FilamentSpool* sp = store ? store->get_spool(m_selected_spool_id) : nullptr;
if (sp) {
filament_item.filament_id = sp->setting_id;
// GitHub #11937: resolve through filament_id > setting_id >
// vendor+type > "Generic <type>" instead of assuming
// sp->setting_id is already a valid Preset::filament_id, so a
// spool with a cloud user-settings id or a free-typed brand can
// still be confirmed into the AMS slot.
std::string resolved_filament_id = sp->setting_id;
if (auto* bundle = wxGetApp().preset_bundle) {
if (auto info = bundle->resolve_filament_for_spool(sp->setting_id, sp->brand, sp->material_type))
resolved_filament_id = info->filament_id;
}
filament_item.filament_id = resolved_filament_id;
filament_item.setting_id = sp->setting_id;
filament_item.spool_id = sp->spool_id;
}
Expand Down Expand Up @@ -1554,8 +1564,14 @@ static void _populate_filament_combobox_grouped(
for (const auto& spool_id : store->all_spool_ids()) {
const Slic3r::GUI::FilamentSpool* sp = store->get_spool(spool_id);
if (!sp) continue;
// GitHub #11937: a manually-added spool may carry a cloud
// user-settings id (or nothing, for a free-typed third-party
// brand) in setting_id instead of a real Preset::filament_id.
// Fall back through vendor+type and "Generic <type>" so those
// spools become selectable instead of being bucketed as
// "Unsupported Filaments" forever.
bool has_preset = bundle &&
bundle->get_filament_by_filament_id(sp->setting_id).has_value();
bundle->resolve_filament_for_spool(sp->setting_id, sp->brand, sp->material_type).has_value();
if (has_preset) {
wxString brand = sp->brand.empty() ? other_bucket : wxString::FromUTF8(sp->brand);
lib_brand_to_spools[brand].push_back(*sp);
Expand Down Expand Up @@ -2457,7 +2473,10 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
auto* store = wxGetApp().fila_manager_store();
const FilamentSpool* sp = store ? store->get_spool(m_selected_spool_id) : nullptr;
if (sp && preset_bundle) {
auto fila_info = preset_bundle->get_filament_by_filament_id(sp->setting_id);
// GitHub #11937: same tolerant resolution as on_select_ok() —
// sp->setting_id may be a cloud user-settings id or empty
// rather than a real Preset::filament_id.
auto fila_info = preset_bundle->resolve_filament_for_spool(sp->setting_id, sp->brand, sp->material_type);
if (fila_info.has_value()) {
ams_filament_id = fila_info->filament_id;
ams_setting_id = fila_info->setting_id;
Expand Down
Loading