v3.0: content-addressed mint tooling, succession runtime, W1 hardening

Implements phase v3.0 of filament_id_plan_v3.md - tooling and client
prerequisites. No filament_id values change in this commit.

Tooling (scripts/assign_filament_ids.py; 106 unit tests):
- The mint key becomes "filament_product/<filament_vendor>/<filament_type>/
  <family_name>", resolved loader-faithfully from the declarer's flattened
  config - bundle-independent and content-addressed; identity fixes re-id
  by design and are made safe by the succession ledger. --mint now takes
  the triple.
- The snapshot gains "triples" and "triple_exceptions" sections, folded
  into the equality gate: any vendor/type/name change surfaces as a
  reviewable snapshot diff. Check 3 rewritten to triple-mint conformance
  with (id, triple) grandfathering; check 5 generalized from OFL generics
  to every OFL-riding vendor preset; new check 8 (triple integrity) and
  check 9 (succession integrity).
- The retired ledger moves to resources/profiles/retired_filament_ids.json
  so it ships with the app; schema {claims, successor} plus cross-island
  "hints". The 14 v1 entries are migrated with mode-rule successors.
  Vanished ids in a foreign island's space (GF*/QD_*) are RELEASED with a
  hint instead of retired: the island catalog owns them and may
  legitimately (re)ship them, which check 4 must never block.
- New maintenance modes: --remint VENDOR, --drop-redundant-ids VENDOR,
  --add-hint "OLD=NEW", and --update-snapshot --forget-never-shipped FILE
  (never-shipped ids drop from lineage; chains splice through them).

Runtime (C++):
- Succession helpers in libslic3r/Preset (pure chain-follow + lazy ledger
  load from resources), consulted only on resolution miss in
  get_filament_by_filament_id, both AMS sync predicates,
  add_ams_filaments, setting_id_to_type, and the calibration-history
  lookup; behavior is byte-identical while the ledger has no matching
  entry. Catch2 coverage in tests/libslic3r.
- W1 hardening: check_ams_filament_valid no longer remote-wipes trays or
  rewrites temps for P-shaped ids that a system preset carries (ten such
  system ids ship today); the size==8 && [0]=='P' assert is relaxed; the
  unguarded filament_list find deref in the temp-equation check returns
  non-destructively on a miss.
- MoonrakerPrinterAgent: the 23 hardcoded OFL generic ids are replaced by
  a runtime lookup of "Generic <family> @System" (id-equivalent on
  today's shipped profiles, follows future re-mints automatically);
  scripts/test_moonraker_lane_data.py derives its expectations from the
  shipped profiles and gains --check-ofl-map.

Profile data (W3 - type is now a key component, so type bugs are fixed
before any re-mint):
- 17 same-name-different-type groups corrected across 50 files (OFL
  Generic PETG-CF/PE-CF/PP-CF; Flashforge ASA Basic/ASA-CF/ABS-CF/HIPS/
  PAHT-CF/PLA Silk; FusRock PAHT; Creality Generic PA6-CF; InfiMech PETG;
  Anycubic TPU 95A / TPU for ACE; Prusa Generic PA-CF/PLA-CF) - every
  value validated against MaterialType::all(); 3 Snapmaker U1 roots gain
  their missing filament_vendor. Flattened-config equivalence vs the
  previous commit: exactly the 54 intended diffs, zero BBL.
- doc/developer-reference/filament_id.md rewritten for the v3 rule.

Ambiguous type divergences (Prusa Generic TPU/TPU HF FLEX-vs-TPU, FusRock
S-Multi/S-PAHT) and all same-type vendor-tag divergences are deferred to
the v3.2 vendor worksheets, catalogued with analysis.
This commit is contained in:
SoftFever
2026-07-04 16:28:56 +08:00
parent 67406b24ba
commit 2c0867619c
67 changed files with 13311 additions and 330 deletions
+55 -35
View File
@@ -19,6 +19,7 @@
#include <chrono>
#include <cstdint>
#include <cctype>
#include <map>
#include <thread>
namespace {
@@ -609,51 +610,70 @@ std::string MoonrakerPrinterAgent::map_filament_type_to_generic_id(const std::st
{
const std::string upper = trim_and_upper(filament_type);
// Map to OrcaFilamentLibrary preset IDs (compatible with all printers)
// Source: resources/profiles/OrcaFilamentLibrary/filament/
// Normalize reported material names (trimmed, uppercased) to an OrcaFilamentLibrary
// generic family. The family's filament_id is resolved from the loaded system presets
// below rather than hardcoded, so profile id re-mints never require touching this table.
// scripts/test_moonraker_lane_data.py parses this initializer; keep the {"A", "B"} format.
static const std::map<std::string, std::string> type_to_ofl_family = {
// PLA variants
{"PLA", "PLA"},
{"PLA-CF", "PLA-CF"},
{"PLA SILK", "PLA Silk"},
{"PLA-SILK", "PLA Silk"},
{"PLA HIGH SPEED", "PLA High Speed"},
{"PLA-HS", "PLA High Speed"},
{"PLA HS", "PLA High Speed"},
// PLA variants
if (upper == "PLA") return "OGFL99";
if (upper == "PLA-CF") return "OGFL98";
if (upper == "PLA SILK" || upper == "PLA-SILK") return "OGFL96";
if (upper == "PLA HIGH SPEED" || upper == "PLA-HS" || upper == "PLA HS") return "OGFL95";
// ABS/ASA variants
{"ABS", "ABS"},
{"ASA", "ASA"},
// ABS/ASA variants
if (upper == "ABS") return "OGFB99";
if (upper == "ASA") return "OGFB98";
// PETG/PET variants
{"PETG", "PETG"},
{"PET", "PETG"},
{"PCTG", "PCTG"},
// PETG/PET variants
if (upper == "PETG" || upper == "PET") return "OGFG99";
if (upper == "PCTG") return "OGFG97";
// PA/Nylon variants
{"PA", "PA"},
{"NYLON", "PA"},
{"PA-CF", "PA-CF"},
{"PPA", "PPA-CF"},
{"PPA-CF", "PPA-CF"},
{"PPA-GF", "PPA-GF"},
// PA/Nylon variants
if (upper == "PA" || upper == "NYLON") return "OGFN99";
if (upper == "PA-CF") return "OGFN98";
if (upper == "PPA" || upper == "PPA-CF") return "OGFN97";
if (upper == "PPA-GF") return "OGFN96";
// PC variants
{"PC", "PC"},
// PC variants
if (upper == "PC") return "OGFC99";
// PP/PE variants
{"PE", "PE"},
{"PP", "PP"},
// PP/PE variants
if (upper == "PE") return "OGFP99";
if (upper == "PP") return "OGFP97";
// Support materials
{"PVA", "PVA"},
{"HIPS", "HIPS"},
{"BVOH", "BVOH"},
// Support materials
if (upper == "PVA") return "OGFS99";
if (upper == "HIPS") return "OGFS98";
if (upper == "BVOH") return "OGFS97";
// TPU variants
{"TPU", "TPU"},
// TPU variants
if (upper == "TPU") return "OGFU99";
// Other materials
{"EVA", "EVA"},
{"PHA", "PHA"},
{"COPE", "CoPE"},
{"SBS", "SBS"},
};
// Other materials
if (upper == "EVA") return "OGFR99";
if (upper == "PHA") return "OGFR98";
if (upper == "COPE") return "OGFLC99";
if (upper == "SBS") return "OFLSBS99";
auto it = type_to_ofl_family.find(upper);
if (it == type_to_ofl_family.end())
return UNKNOWN_FILAMENT_ID;
// Unknown material
if (auto* bundle = GUI::wxGetApp().preset_bundle) {
const Preset* preset = bundle->filaments.find_preset("Generic " + it->second + " @System");
if (preset != nullptr && preset->is_system && !preset->filament_id.empty())
return preset->filament_id;
}
// Unknown material, or no loaded preset data to resolve against
return UNKNOWN_FILAMENT_ID;
}