mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-04 15:57:09 +00:00
fix: treat renamed printer presets as compatible aliases (#14504)
# Description Fixes user filament presets that reference an old printer preset name in `compatible_printers`, even when the current system printer preset correctly declares that old name in `renamed_from`. ## Core Issue `PresetCollection::find_preset()` was updated to resolve `renamed_from`, but this specific failure does not go through `find_preset()`. For root user filament presets with empty `inherits`, Orca infers `compatible_printers` from the filament name suffix after `@`. For example: ```json "ELEGOO PLA Base @Elegoo Neptune 4 Pro (0.4 nozzle)" ``` loads with: ```json "compatible_printers": ["Elegoo Neptune 4 Pro (0.4 nozzle)"] ``` The current system printer preset is named: ```text Elegoo Neptune 4 Pro 0.4 nozzle ``` and has: ```json "renamed_from": "Elegoo Neptune 4 Pro (0.4 nozzle)" ``` However, filament compatibility was doing a direct string comparison against the active printer name. Since that compatibility path does not call `find_preset()`, the `renamed_from` mapping was never considered. ## Fix Teach printer compatibility checks to treat `active_printer.preset.renamed_from` entries as aliases of the active printer name. This preserves existing user preset JSON and avoids rewriting `compatible_printers`, while allowing old printer suffixes to remain compatible with renamed system printer presets. ## Tests <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
This commit is contained in:
@@ -96,6 +96,7 @@ int main(int argc, char* argv[])
|
||||
("vendor,v", po::value<std::string>()->default_value(""), "Vendor name. Optional, all profiles present in the folder will be validated if not specified")
|
||||
("generate_presets,g", po::value<bool>()->default_value(false), "Generate user presets for mock test")
|
||||
("check_filament_subtypes,f", po::bool_switch()->default_value(false), "Also flag printers with duplicate (ambiguous) filament subtypes. Off unless this flag is present.")
|
||||
("check_preset_references,r", po::bool_switch()->default_value(false), "Also flag presets whose inherits/compatible_printers/compatible_prints reference a deleted or renamed preset. Off unless this flag is present.")
|
||||
("log_level,l", po::value<int>()->default_value(2), "Log level. Optional, default is 2 (warning). Higher values produce more detailed logs.");
|
||||
// clang-format on
|
||||
|
||||
@@ -120,6 +121,7 @@ int main(int argc, char* argv[])
|
||||
int log_level = vm["log_level"].as<int>();
|
||||
bool generate_user_preset = vm["generate_presets"].as<bool>();
|
||||
bool check_filament_subtypes = vm["check_filament_subtypes"].as<bool>();
|
||||
bool check_preset_references = vm["check_preset_references"].as<bool>();
|
||||
|
||||
// check if path is valid, and return error if not
|
||||
if (!fs::exists(path) || !fs::is_directory(path)) {
|
||||
@@ -166,7 +168,7 @@ int main(int argc, char* argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (preset_bundle->has_errors(check_filament_subtypes)) {
|
||||
if (preset_bundle->has_errors(check_filament_subtypes, check_preset_references)) {
|
||||
std::cout << "Validation failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -532,6 +532,17 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
||||
load_user_presets(dir_user_presets, substitution_rule);
|
||||
}
|
||||
|
||||
// Rewrite renamed compatible_printers / compatible_prints references before selection. Skipped
|
||||
// in validation mode so the profile validator (has_errors -> check_preset_references) sees the
|
||||
// raw vendor-JSON references instead of the silently-repaired ones.
|
||||
if (!validation_mode)
|
||||
this->normalize_compatible_presets();
|
||||
// Rewrite renamed compatible_printers / compatible_prints references before selection. Skipped
|
||||
// in validation mode so the profile validator (has_errors -> check_preset_references) sees the
|
||||
// raw vendor-JSON references instead of the silently-repaired ones.
|
||||
if (!validation_mode)
|
||||
this->normalize_compatible_presets();
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
|
||||
@@ -752,6 +763,8 @@ PresetsConfigSubstitutions PresetBundle::load_project_embedded_presets(std::vect
|
||||
|
||||
//this->update_multi_material_filament_presets();
|
||||
//this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
// Rewrite renamed compatible references before the caller (Plater) selects the project presets.
|
||||
this->normalize_compatible_presets();
|
||||
if (! errors_cummulative.empty())
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
@@ -1122,6 +1135,9 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &
|
||||
this->printers.update_after_user_presets_loaded();
|
||||
}*/
|
||||
|
||||
// Rewrite renamed compatible references in synced user presets before compatibility is evaluated.
|
||||
this->normalize_compatible_presets();
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
//this->load_selections(config, PresetPreferences());
|
||||
@@ -1807,6 +1823,9 @@ PresetsConfigSubstitutions PresetBundle::update_subscribed_presets(
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to save bundle metadata to: " << metadata_save_path.string();
|
||||
}
|
||||
|
||||
// Rewrite renamed compatible references in synced user presets before compatibility is evaluated.
|
||||
this->normalize_compatible_presets();
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
|
||||
@@ -5240,6 +5259,49 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite a preset-name-list field (compatible_printers / compatible_prints) so references to a
|
||||
// renamed system preset point at the current name. Sibling-collection analog of
|
||||
// Preset::normalize_inherits: target.find_preset(name, false) resolves "renamed_from" recursively
|
||||
// and returns nullptr for unknown names, so we rewrite only on a positive, changed match and leave
|
||||
// user/deleted names untouched.
|
||||
static void normalize_compatible_field(Preset &preset, const char *field_key, PresetCollection &target)
|
||||
{
|
||||
auto *opt = preset.config.option<ConfigOptionStrings>(field_key);
|
||||
if (opt == nullptr)
|
||||
return;
|
||||
for (std::string &name : opt->values) {
|
||||
if (name.empty())
|
||||
continue;
|
||||
if (const Preset *resolved = target.find_preset(name, false); resolved != nullptr && resolved->name != name)
|
||||
name = resolved->name;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve compatible_printers / compatible_prints references that point at a renamed system preset
|
||||
// to the current name, mirroring Preset::normalize_inherits for the "inherits" field. Because these
|
||||
// fields reference presets in sibling collections (printers / prints / sla_prints), the resolution
|
||||
// cannot happen inside a single collection's load_presets() and runs here, after update_system_maps()
|
||||
// has built every collection's rename map. Must be called before selection so the rewritten stored
|
||||
// preset is copied into the edited preset by select_preset (no spurious "modified" flag).
|
||||
void PresetBundle::normalize_compatible_presets()
|
||||
{
|
||||
// compatible_printers references a printer preset; compatible_prints (filaments / SLA materials)
|
||||
// references a process preset. System presets are normalized too: a vendor profile can itself
|
||||
// reference a sibling preset by a name that was later renamed, and the rewrite is in-memory only
|
||||
// (system presets are never persisted back to vendor JSON). (begin()/end() skip defaults.)
|
||||
auto normalize = [this](PresetCollection &holders, PresetCollection *processes) {
|
||||
for (Preset &p : holders) {
|
||||
normalize_compatible_field(p, "compatible_printers", this->printers);
|
||||
if (processes != nullptr)
|
||||
normalize_compatible_field(p, "compatible_prints", *processes);
|
||||
}
|
||||
};
|
||||
normalize(this->prints, nullptr);
|
||||
normalize(this->filaments, &this->prints);
|
||||
normalize(this->sla_prints, nullptr);
|
||||
normalize(this->sla_materials, &this->sla_prints);
|
||||
}
|
||||
|
||||
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
|
||||
{
|
||||
const Preset &printer_preset = this->printers.get_edited_preset();
|
||||
@@ -5471,7 +5533,8 @@ void PresetBundle::set_default_suppressed(bool default_suppressed)
|
||||
printers.set_default_suppressed(default_suppressed);
|
||||
}
|
||||
|
||||
bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes) const
|
||||
bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes, bool check_references) const
|
||||
bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes, bool check_references) const
|
||||
{
|
||||
if (m_errors != 0 || printers.m_errors != 0 || filaments.m_errors != 0 || prints.m_errors != 0)
|
||||
return true;
|
||||
@@ -5494,6 +5557,12 @@ bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes) const
|
||||
if (check_duplicate_filament_subtypes && this->check_duplicate_filament_subtypes())
|
||||
has_errors = true;
|
||||
|
||||
if (check_references && this->check_preset_references())
|
||||
has_errors = true;
|
||||
|
||||
if (check_references && this->check_preset_references())
|
||||
has_errors = true;
|
||||
|
||||
return has_errors;
|
||||
}
|
||||
|
||||
@@ -5524,6 +5593,68 @@ static std::string preset_file_uri(const std::string &file)
|
||||
return uri;
|
||||
}
|
||||
|
||||
// Orca: validator-only. Flag any system preset whose inherits / compatible_printers /
|
||||
// compatible_prints references a name that no longer resolves. Uses find_preset (exact match, then
|
||||
// the renamed_from map - no fuzzy find_preset2, no alias resolution), so:
|
||||
// nullptr -> the referenced preset was deleted/renamed away (name is dangling),
|
||||
// resolved != name -> the reference uses an old name that renamed_from maps to a current one.
|
||||
// Both should be fixed at the source rather than relying on load-time normalization - which is why
|
||||
// normalize_compatible_presets() is skipped in validation mode (see load_presets), so this sees the
|
||||
// raw vendor-JSON references. Safe under a single-vendor run (-v) too: inherits / compatible_printers
|
||||
// / compatible_prints only name same-vendor or OrcaFilamentLibrary presets (both loaded), so a
|
||||
// reference that does not resolve is genuinely dangling rather than an unloaded cross-vendor preset.
|
||||
bool PresetBundle::check_preset_references() const
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
// Resolve one reference (an inherits parent or a compatible_* entry) against its target
|
||||
// collection and log if it is dangling (unknown) or uses a renamed preset's old name.
|
||||
auto report_ref = [&](const Preset &p, const std::string &name, const PresetCollection &target,
|
||||
const char *verb, const char *noun) {
|
||||
const Preset *resolved = target.find_preset(name, false);
|
||||
if (resolved == nullptr) {
|
||||
found = true;
|
||||
BOOST_LOG_TRIVIAL(error) << "Preset \"" << p.name << "\" " << verb << " unknown " << noun << " \"" << name << "\":\n"
|
||||
<< preset_file_uri(p.file);
|
||||
} else if (resolved->name != name) {
|
||||
found = true;
|
||||
BOOST_LOG_TRIVIAL(error) << "Preset \"" << p.name << "\" " << verb << " renamed " << noun << " \"" << name
|
||||
<< "\" (now \"" << resolved->name << "\"):\n" << preset_file_uri(p.file);
|
||||
}
|
||||
};
|
||||
|
||||
auto check_list = [&](const Preset &p, const char *key, const PresetCollection &target) {
|
||||
const auto *opt = p.config.option<ConfigOptionStrings>(key);
|
||||
if (opt == nullptr)
|
||||
return;
|
||||
for (const std::string &name : opt->values)
|
||||
if (!name.empty())
|
||||
report_ref(p, name, target, "references", key);
|
||||
};
|
||||
|
||||
auto check_collection = [&](const PresetCollection &holders, const PresetCollection *processes) {
|
||||
for (const Preset &p : holders) {
|
||||
if (!p.is_system)
|
||||
continue;
|
||||
if (const std::string &inh = p.inherits(); !inh.empty())
|
||||
report_ref(p, inh, holders, "inherits", "parent");
|
||||
check_list(p, "compatible_printers", this->printers);
|
||||
if (processes != nullptr)
|
||||
check_list(p, "compatible_prints", *processes);
|
||||
}
|
||||
};
|
||||
|
||||
// Printers carry no compatible_printers/compatible_prints (those name a printer, so a printer
|
||||
// holding them makes no sense); check_list is a no-op for them, so only their inherits is checked.
|
||||
check_collection(this->printers, nullptr);
|
||||
check_collection(this->prints, nullptr);
|
||||
check_collection(this->filaments, &this->prints);
|
||||
check_collection(this->sla_prints, nullptr);
|
||||
check_collection(this->sla_materials, &this->sla_prints);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// Orca: a filament is matched from the AMS by (filament_id + printer compatibility).
|
||||
// For any one printer, at most one instantiated filament preset with a given
|
||||
// filament_id may be compatible - otherwise the AMS match is ambiguous and the
|
||||
|
||||
@@ -448,6 +448,11 @@ public:
|
||||
void update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible);
|
||||
void update_compatible(PresetSelectCompatibleType select_other_if_incompatible) { this->update_compatible(select_other_if_incompatible, select_other_if_incompatible); }
|
||||
|
||||
// Rewrite compatible_printers / compatible_prints references that point at a renamed system
|
||||
// preset to the current name, mirroring Preset::normalize_inherits for the "inherits" field.
|
||||
// Call after loading presets and before selection; requires update_system_maps() to have run.
|
||||
void normalize_compatible_presets();
|
||||
|
||||
// Set the is_visible flag for printer vendors, printer models and printer variants
|
||||
// based on the user configuration.
|
||||
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
|
||||
@@ -480,8 +485,13 @@ public:
|
||||
return { Preset::TYPE_PRINTER, Preset::TYPE_SLA_PRINT, Preset::TYPE_SLA_MATERIAL };
|
||||
}
|
||||
|
||||
// Orca: for validation only. The duplicate filament subtype check is opt-in for now
|
||||
bool has_errors(bool check_duplicate_filament_subtypes = false) const;
|
||||
// Orca: for validation only. The duplicate filament subtype and preset-reference checks are
|
||||
// opt-in for now (enabled per-vendor by the profile-check CI as vendors are cleaned up).
|
||||
bool has_errors(bool check_duplicate_filament_subtypes = false, bool check_preset_references = false) const;
|
||||
|
||||
// Orca: for validation only. Flag any system preset whose inherits / compatible_printers /
|
||||
// compatible_prints references a deleted (unknown) or renamed (old) preset name.
|
||||
bool check_preset_references() const;
|
||||
|
||||
private:
|
||||
// Orca: validation only - flag any printer with two or more compatible
|
||||
|
||||
Reference in New Issue
Block a user