Add unique filament_id for inherited user presets

When a user creates a new filament preset by inheriting from an
existing one (e.g., "Brand ABS @BBL H2D" inheriting from "Generic
ABS @BBL H2D"), the resulting preset previously had no filament_id
of its own. This caused two problems:

1. The AMS sync pipeline could not distinguish the user preset from
   its parent, so syncing filaments from the printer always resolved
   to the generic base preset instead of the user's custom one.

2. External tools that rely on filament_id for preset identification
   (e.g., Bambuddy, see maziggy/bambuddy#1053) could not assign or
   recognize user-created inherited presets in AMS slots.

The root cause was twofold:

- No filament_id was generated during save_current_preset() for
  inherited filament presets. Only base (non-inheriting) presets
  received one via the CreatePresetsDialog flow.

- The AMS matching logic in sync_ams_list(), get_ams_cobox_infos(),
  and get_filament_presets() filtered candidates with
  `get_preset_base(f) == &f`, which by definition excludes any
  preset with a non-empty inherits() field.

This commit:

- Generates a unique filament_id (MD5 hash of preset name, prefixed
  with "P", truncated to 8 chars) when creating a new filament
  preset in PresetCollection::save_current_preset(). This matches
  the existing ID generation scheme used for base filaments in
  CreatePresetsDialog::get_filament_id().

- Persists filament_id into the JSON when saving inherited presets
  via Preset::save() and get_differed_values_to_update().

- Broadens the AMS filament matching predicates to also consider
  user presets that carry their own filament_id, rather than
  requiring them to be base presets.

Files changed:
  src/libslic3r/Preset.cpp       - ID generation, save, lookup
  src/libslic3r/PresetBundle.cpp - AMS sync matching predicates
This commit is contained in:
Felix Jen
2026-04-21 23:23:00 -05:00
committed by viewit
parent 468993d5de
commit ec54dcedd0
2 changed files with 24 additions and 7 deletions

View File

@@ -41,6 +41,8 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
#include <boost/uuid/detail/md5.hpp>
#include <boost/algorithm/hex.hpp>
#include "libslic3r.h"
#include "Utils.hpp"
@@ -615,6 +617,11 @@ void Preset::save(DynamicPrintConfig* parent_config)
opt_dst->set(opt_src);
}
}
if (!filament_id.empty()) {
temp_config.set_key_value(BBL_JSON_KEY_FILAMENT_ID, new ConfigOptionString(filament_id));
}
temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string());
} else if (!filament_id.empty() && inherits().empty()) {
DynamicPrintConfig temp_config = config;
@@ -1500,9 +1507,9 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std
key_values[BBL_JSON_KEY_BASE_ID] = preset.base_id;
} else {
key_values.erase(BBL_JSON_KEY_BASE_ID);
if (get_preset_base(preset) == &preset && !preset.filament_id.empty()) {
key_values[BBL_JSON_KEY_FILAMENT_ID] = preset.filament_id;
}
}
if (!preset.filament_id.empty()) {
key_values[BBL_JSON_KEY_FILAMENT_ID] = preset.filament_id;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " uploading user preset name is: " << preset.name << "and create filament_id is: " << preset.filament_id
<< " and base_id is: " << preset.base_id;
@@ -2417,7 +2424,7 @@ std::map<std::string, std::vector<Preset const *>> PresetCollection::get_filamen
std::map<std::string, std::vector<Preset const *>> filament_presets;
for (auto &preset : m_presets) {
if (preset.is_user()) {
if (preset.inherits() == "") { filament_presets[preset.filament_id].push_back(&preset); }
if (preset.inherits() == "" || !preset.filament_id.empty()) { filament_presets[preset.filament_id].push_back(&preset); }
continue;
}
if (get_preset_base(preset) == &preset) { filament_presets[preset.filament_id].push_back(&preset); }
@@ -2511,8 +2518,18 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
preset.is_project_embedded = false;
if (m_type == Preset::TYPE_PRINT)
preset.config.option<ConfigOptionString>("print_settings_id", true)->value = new_name;
else if (m_type == Preset::TYPE_FILAMENT)
else if (m_type == Preset::TYPE_FILAMENT) {
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = new_name;
boost::uuids::detail::md5 hash;
boost::uuids::detail::md5::digest_type digest;
hash.process_bytes(new_name.data(), new_name.size());
hash.get_digest(digest);
const auto char_digest = reinterpret_cast<const char *>(&digest);
std::string result;
boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(result));
preset.filament_id = "P" + result.substr(0, 7);
}
else if (m_type == Preset::TYPE_PRINTER)
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = new_name;
//BBS: add lock logic for sync preset in background

View File

@@ -2307,7 +2307,7 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
continue;
}
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; });
[this, &filament_id](auto &f) { return f.is_compatible && (filaments.get_preset_base(f) == &f || (f.is_user() && !f.filament_id.empty())) && f.filament_id == 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);
@@ -2409,7 +2409,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
auto filament_type = ams.opt_string("filament_type", 0u);
auto iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id, &has_type, filament_type](auto &f) {
has_type |= f.config.opt_string("filament_type", 0u) == filament_type;
return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
return f.is_compatible && (filaments.get_preset_base(f) == &f || (f.is_user() && !f.filament_id.empty())) && f.filament_id == filament_id; });
if (iter == filaments.end()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
if (!filament_type.empty()) {