diff --git a/docs/HLSD/filament_id.md b/docs/HLSD/filament_id.md index 0dfc5471b0..980d830d7d 100644 --- a/docs/HLSD/filament_id.md +++ b/docs/HLSD/filament_id.md @@ -23,8 +23,9 @@ When a printer reports what a tray holds (Bambu AMS, Qidi box, Creality CFS, Klipper, Snapmaker), OrcaSlicer matches the reported id against the filament presets compatible with that printer to select the right profile; other features — tray display names, support-material detection, vitrification warnings, multi-nozzle filament grouping — -look up material properties by id alone. When an id has been retired, a shipped succession -ledger forwards it to its successor so old trays and records keep resolving. +look up material properties by id alone. An id that changes is not forwarded anywhere: a +tray or record still holding the old value falls back to matching by material type until the +user re-selects the filament, so identity changes are made deliberately and rarely. This page is the rule for authoring `filament_id` in system profiles (`resources/profiles/**`). CI enforces everything below; the short version is: @@ -33,13 +34,13 @@ This page is the rule for authoring `filament_id` in system profiles > **Never write a `filament_id` value by hand.** New families get their id from > `python scripts/assign_filament_ids.py`; existing families already have one — inherit it. -## The design, in three pieces +## The design, in two pieces Because several consumers match **globally by id alone, first hit wins** (see the next section), any two materials sharing one id feed wrong data somewhere — a wrong tray name, a wrong support-material flag, a wrong nozzle grouping — and inside one printer a duplicated id makes AMS spool matching a coin toss. Hand-written ids produce such collisions constantly, so -the system is built to make them impossible and to make identity corrections safe: +the system is built to make them impossible: 1. **Deterministic minting.** An id is a pure hash of the product's identity — no registry to maintain, no next-free-number ceremony, no way for two concurrent PRs to race for the same @@ -48,9 +49,6 @@ the system is built to make them impossible and to make identity corrections saf `scripts/filament_id_snapshot.json` exactly, so every change to ids, claims (which bundles ship which id, and for which family), or product identity surfaces as a reviewable diff to one file — the maintainer gate. -3. **A shipped succession ledger.** Ids live outside the tree too — on device trays, in - calibration records, in user presets, in project files. When an id is retired, the ledger - forwards it to its successor so none of those references degrade to a generic fallback. ## Who consumes the id @@ -60,9 +58,8 @@ friends) resolves it to a preset. The matcher is printer-scoped and first-match- scanning only compatible family roots — system roots plus user-made custom filaments, which are user roots carrying their own `P*` ids; a preset derived from another resolves through its root and never matches directly — it picks the first one whose `filament_id` equals the -tray's and retries once through the succession ledger on a miss. Only then does it fall back -by filament type: a system `Generic ` preset (matched by name, then by type -similarity), else the slot's previous selection, else any compatible system generic or, +tray's. On a miss it falls back by filament type: a system `Generic ` preset +(matched by name, then by type similarity), else the slot's previous selection, else any compatible system generic or, failing that, any compatible system preset, else the slot is skipped — every fallback selection surfaces a user-visible notice. @@ -75,7 +72,7 @@ pattern, with the device-reported tray material id flowing through the shared ma | Ecosystem | Where the tray id comes from today | | --- | --- | | Bambu AMS | the device itself (RFID / user tray setting) — the `GF*` catalog | -| Qidi box | composed at runtime as `QD___` — vendor and type indices from the device's per-slot saved variables, the series digit inferred client-side from the printer model/name — then translated through the succession ledger to the family's minted id (the `QD_*` values themselves no longer exist as preset ids — see the reserved-namespaces section) | +| Qidi box | composed at runtime as `QD___` — vendor and type indices from the device's per-slot saved variables, the series digit inferred client-side from the printer model/name. No preset carries a `QD_*` value, so the slot currently resolves by filament type; mapping the composed id onto the family's minted id belongs in the agent | | Creality CFS | runtime brand/type scoring returns the winning preset's id | | Klipper (AFC / Happy Hare) | runtime lookup by filament type | | Snapmaker | runtime color/vendor/type match | @@ -155,12 +152,12 @@ the `Generic X` base name, write no id key). e.g. `Generic PLA @Sovol SV08 MAX` inherits `Generic PLA @System` and lists three Sovol nozzles. A vendor-*branded* filament never rides a generic family id. 5. **Ids follow the product identity.** The id is a pure function of the product triple - `(filament_vendor, filament_type, family name)` — correcting any of them re-mints the id - **by design** (via `--remint `; the exact sequence is in the FAQ), and - `--update-snapshot` records the old id in the shipped succession ledger with its successor - so device trays, calibration records, and user presets keep resolving (`renamed_from` - still gates preset-*name* compatibility as before). A shipped id is never recycled for a - different material: retired ids are blocked forever. + `(filament_vendor, filament_type, family name)`, so correcting any of them re-mints the id + **by design** (via `--remint `; the exact sequence is in the FAQ). Nothing forwards + the old value, so anything outside the tree that stored it — a device tray, a calibration + record, a saved project — falls back to matching by filament type until the user re-selects + the filament. Re-mint deliberately, and only to fix a genuinely wrong identity. + (`renamed_from` still gates preset-*name* compatibility, as before.) ## Minting — nobody invents ids @@ -191,7 +188,7 @@ Snapmaker bundles alike; the OFL generic `Generic/PLA/Generic PLA` mints `OFDSrz by ten bundles — most by independent declarations converging on the same mint, the rest purely through inheritance from the OFL family. -On the rare collision with any existing or retired id, the minter salts the input (`…/1`, +On the rare collision with an existing id, the minter salts the input (`…/1`, `…/2`, …) until free, and the result is frozen in the profile file. Salting is also used deliberately: a *salt split* keeps two presets of one product on distinct ids where a single id would be AMS-ambiguous on the same printer — the "selectable alongside" situation from the @@ -224,14 +221,6 @@ normally only used by id migrations): *same* triple is legal by design — that is the point. - `--drop-redundant-ids VENDOR` deletes declarations that merely re-declare an inherited OrcaFilamentLibrary id. -- `--add-hint "OLD=NEW"` records a succession hint for an id a foreign catalog owns - (`GF*` only — see the ledger section). -- `--retire "OLD=NEW"` records succession for a shipped id that vanished while another - declarer kept it alive — lineage the automatic claim vote (see the succession-ledger - section) can no longer see. -- `--forget-never-shipped FILE` (with `--update-snapshot`) — given a JSON list of ids that - never shipped in any release, drops them from the lineage entirely (no retirement entry) - and splices succession chains that pointed through them. - `--profiles DIR` points the tooling at a different profile tree (default `resources/profiles`). @@ -248,10 +237,9 @@ or device contract owns it. | Space | Status | Rule | | --- | --- | --- | | `GF*` | Bambu AMS/RFID catalog — the one remaining *island* | BBL vendor only; a claim by anyone else is refused unless the snapshot sanctions that exact claim (via `--allow-shared-catalog` — see the CI section) | -| `QD_*` | Qidi device protocol — *dissolved island* | declarable by **nobody**, Qidi included; every shipped `QD_*` id is retired in the ledger with a live successor | +| `QD_*` | Qidi device protocol | declarable by **nobody**, Qidi included: the box composes these ids at runtime and they are not preset ids | | `P` + 7 hex chars (case-insensitive), `"null"` | user-created custom filaments (`CreatePresetsDialog.cpp`) | never appears in system profiles | | every already-shipped id | frozen in the snapshot (grandfathered) | frozen as-is; new claims need maintainer sign-off | -| every retired id | `resources/profiles/retired_filament_ids.json` | never used again, for anything | The two device namespaces, in detail: @@ -259,43 +247,14 @@ The two device namespaces, in detail: declarations are frozen as-is and never re-minted. BBL also carries several dozen grandfathered legacy codes that are neither `GF*` nor `OF*` (the `BETA` family's `B*` ids, plus `Generic SBS`'s legacy `BFLSBS99`) — frozen the same way, via the snapshot. -- **Qidi (`QD_*`) — dissolved.** `QD_*` is a device-*protocol* namespace, not a preset id - space: the Qidi box path composes `QD___` ids at runtime (slot - vendor and type indices reported by the device, the series digit inferred client-side - from the printer model/name), and the Qidi agent translates a composed id through the - succession ledger to the family's minted id. Qidi presets themselves carry ordinary minted - `OF*` ids (generics share the OFL ids), and all 204 `QD_*` ids that ever shipped sit in - the ledger as retired entries with live successors — which is also what makes CI refuse - any future `QD_*` occurrence permanently. The alternative — treating per-series protocol ids - as preset ids — would put one product under five ids (`QIDI PLA Rapido` would be `QD_0_1_1` - through `QD_4_1_1`), exactly the fragmentation the mint rule removes. - -## The succession ledger - -`resources/profiles/retired_filament_ids.json` ships with the app and has two maps: - -- **`retired`** — ids Orca owned and withdrew. Each maps to - `{"claims": [...], "successor": }`; successor chains are followed to the live end. - When `--update-snapshot` retires a vanished id, it picks the successor by an automatic - *claim vote*: every old claim whose family still exists votes for that family's current id, - most votes wins. Alongside the deliberate re-mints, this map carries the whole pre-rule - legacy — old ad-hoc vendor codes, Creality's and Snapmaker's numeric ids, and the 204 - `QD_*` protocol ids. -- **`hints`** — the same forwarding for ids Orca *cannot* retire because a foreign island owns - them and may legitimately keep shipping them. That means `GF*` only: e.g. - `GFL99 → OFDSrzZ8` forwards Bambu's Generic PLA catalog id to the OFL generic family on - installs where no live preset carries `GFL99`. - -The client consults the ledger **only on resolution miss** — AMS tray sync and the sync-AMS -dialog's filament listing, the sidebar AMS dropdown, tray-id type lookup, calibration -history, the global filament-id preset lookup, and the Qidi box path. A live preset always -wins first, so BBL installs resolve `GF*` natively and behavior is unchanged wherever the -raw id still exists. This on-miss rule is what makes -identity-driven re-mints (structure rule 5) safe: the old id keeps resolving to the family's -current preset instead of degrading to a `Generic ` fallback. The file is append-only in -its keys — entries are never removed and a retired id never returns — and maintained -exclusively by `--update-snapshot` / `--add-hint` / `--retire` (`--forget-never-shipped`, an -`--update-snapshot` mode, may rewrite an existing entry's successor when splicing chains). +- **Qidi (`QD_*`).** `QD_*` is a device-*protocol* namespace, not a preset id space: the + Qidi box path composes `QD___` ids at runtime (slot vendor and + type indices reported by the device, the series digit inferred client-side from the printer + model/name). Qidi presets carry ordinary minted `OF*` ids (generics share the OFL ids), so + a composed id matches no preset and the slot falls back to filament type; translating it to + the family's id belongs in `QidiPrinterAgent`. The alternative — treating per-series + protocol ids as preset ids — would put one product under five ids (`QIDI PLA Rapido` would + be `QD_0_1_1` through `QD_4_1_1`), exactly the fragmentation the mint rule removes. ## How CI enforces this @@ -319,11 +278,6 @@ The checks, in brief: - **Mint conformance** — a non-grandfathered `OF*` id must equal the mint (or a low salt iteration) of its declarer's product triple; the error prints the expected id to paste into the family root. -- **Retired reuse** — any tree id present in the `retired` map of - `resources/profiles/retired_filament_ids.json` is an error, with no grandfathering (keys in - the same file's `hints` map may legitimately stay live — BBL still ships them). Ids that - fully vanish from the tree are appended to the `retired` map by `--update-snapshot`; ids - are only ever added there, never removed or reused. - **Alias hygiene** — a vendor preset riding an OFL family id must keep the library preset's base name (a rename re-exposes the library preset, since alias shadowing is name-based), a non-empty `compatible_printers` (an empty one shadows nothing), and no own id key @@ -331,12 +285,8 @@ The checks, in brief: - **Triple integrity** — every declarer outside the BBL island must resolve a non-empty `filament_vendor` and `filament_type` (generics use `"Generic"`), and all declarers of one family within a bundle must agree on the triple. -- **Succession integrity** — retired successor chains terminate at a live id (or null) with - no cycles; `hints` keys stay in the island namespace (`GF*`), are never also retired, and - their chains end at a live tree id. - **Reserved namespaces** — `GF*` outside BBL, `P<7-hex>` or `"null"` anywhere, unless that - exact claim is grandfathered in the snapshot; `QD_*` anywhere, with no exception (all are - retired). + exact claim is grandfathered in the snapshot; `QD_*` anywhere, with no exception. - **Structure** — no `filament_id` key on newly instantiated presets; no new declared-vs-inherited id drift; every instantiated system filament must resolve an effective id through its `inherits` chain (recall: an id-less one is a hard load error in @@ -364,18 +314,15 @@ ambiguity check behind structure rule 3. - **A tuned generic ("our profile for Generic PLA")?** Inherit `Generic PLA @System`, keep the `Generic PLA` base name, set `compatible_printers`, write no id key. - **I need to fix a family's `filament_vendor` or `filament_type`.** Fix the config, run - `--remint ` then `--update-snapshot`: the id re-derives from the corrected identity - and the old id lands in the succession ledger pointing at the new one. Commit the profile, - snapshot, and ledger diffs together. -- **I need to rename a family.** Renames need one extra step, because the succession vote - follows family names (`renamed_from` gates preset-name compatibility but is not read by the - id tooling): rename the presets (adding `renamed_from`), run `--update-snapshot` once to - sanction the renamed claims on the old id, then `--remint `, then `--update-snapshot` - again — the old id now lands in the ledger pointing at the new one. Re-minting first would - retire the old id *heirless* (successor `null`), which cannot be repaired afterwards. -- **Can I reuse a `QD_*` id for a Qidi profile?** No — nobody can. The namespace is a - dissolved island: the device still composes those ids at runtime, and the succession ledger - translates them to the families' minted ids. Author Qidi filaments like any other vendor's. + `--remint ` then `--update-snapshot`, and commit the profile and snapshot diffs + together. The id re-derives from the corrected identity, and nothing forwards the old + value, so a tray or record still holding it falls back to matching by filament type. +- **I need to rename a family.** Rename the presets (adding `renamed_from`, which keeps the + preset *name* resolving), then `--remint `, then `--update-snapshot`. The id follows + the new family name; as with any identity fix, the old id is not forwarded. +- **Can I reuse a `QD_*` id for a Qidi profile?** No — nobody can. It is the device protocol's + own id space: the box composes those values at runtime and no preset carries one. Author + Qidi filaments like any other vendor's. - **CI says my family needs an id.** Run `python scripts/assign_filament_ids.py`, then `--update-snapshot`, and commit both diffs. Do not type an id by hand. diff --git a/resources/profiles/retired_filament_ids.json b/resources/profiles/retired_filament_ids.json deleted file mode 100644 index 7f5dda5901..0000000000 --- a/resources/profiles/retired_filament_ids.json +++ /dev/null @@ -1,6983 +0,0 @@ -{ - "hints": { - "GFA00_01": "OF9HCdyQ", - "GFA03": "OFjXrKFO", - "GFA50_01": "OF7OqDp3", - "GFA99": "OF0yFLkY", - "GFASA": "OF0yFLkY", - "GFB00_01": "OFNQKF0M", - "GFB01_01": "OFdrYBmI", - "GFB98": "OFLPAxz3", - "GFB98_01": "OFUaVAdj", - "GFB98_2": "OFoJcr46", - "GFB98_3": "OFoJcr46", - "GFB98_4": "OFoJcr46", - "GFB98_5": "OFK4LdYI", - "GFB98_6": "OFK4LdYI", - "GFB98_7": "OFK4LdYI", - "GFB99": "OFY9muEs", - "GFB99_01": "OFNLXV8X", - "GFB99_2": "OFvGc67v", - "GFB99_3": "OFvGc67v", - "GFB99_4": "OFvGc67v", - "GFB99_5": "OFvGc67v", - "GFB99_6": "OFyeSq8A", - "GFB99_7": "OFyeSq8A", - "GFC00_01": "OFdXVjGZ", - "GFC98": "OFjz0a3m", - "GFC99": "OFLakOUI", - "GFC99_01": "OFN7aNHe", - "GFC99_1": "OF97ru74", - "GFC99_2": "OF97ru74", - "GFC99_3": "OF97ru74", - "GFC99_4": "OF97ru74", - "GFC99_5": "OFq5QANf", - "GFC99_6": "OFq5QANf", - "GFG00_01": "OFObyktP", - "GFG03": "OF3F63OE", - "GFG96": "OF7lOgYF", - "GFG97": "OFu1evlr", - "GFG97-01": "OFTKFAZ4", - "GFG98": "OFoYSJKi", - "GFG98_01": "OFWadebJ", - "GFG99": "OFYPdQJh", - "GFG99_01": "OF1uR1t2", - "GFG99_2": "OF1Id239", - "GFG99_3": "OF1Id239", - "GFG99_4": "OF1Id239", - "GFG99_5": "OF1Id239", - "GFG99_6": "OFOKYhi6", - "GFG99_7": "OFOKYhi6", - "GFL100": "OFBpcB0a", - "GFL106": "OFDSrzZ8", - "GFL108": "OFDSrzZ8", - "GFL204": "OFY9muEs", - "GFL206": "OFY9muEs", - "GFL208": "OFY9muEs", - "GFL304": "OFYPdQJh", - "GFL306": "OFYPdQJh", - "GFL308": "OFYPdQJh", - "GFL404": "OFg8ndtj", - "GFL406": "OFg8ndtj", - "GFL408": "OFg8ndtj", - "GFL504": "OFsP8PKH", - "GFL506": "OFsP8PKH", - "GFL508": "OFsP8PKH", - "GFL71": "OF0omtEb", - "GFL72": "OFFZ458u", - "GFL73": "OFeVzkSU", - "GFL74": "OF0VpkBM", - "GFL75": "OFnXwW8l", - "GFL76": "OF6H51Xx", - "GFL77": "OFtFtiS0", - "GFL78": "OFelDBgv", - "GFL79": "OF5oXk4M", - "GFL80": "OFHknN4v", - "GFL94": "OFrGJ4tR", - "GFL95": "OFmpMwxS", - "GFL95_01": "OFodPDxO", - "GFL96": "OFesA6rF", - "GFL96_01": "OF0GIkJm", - "GFL97": "OFCp3Bgo", - "GFL98": "OFWbdGsC", - "GFL98_01": "OFgVJhVt", - "GFL98_1": "OFQ7I5vC", - "GFL98_3": "OFQ7I5vC", - "GFL98_4": "OFQ7I5vC", - "GFL98_5": "OFQ7I5vC", - "GFL99": "OFDSrzZ8", - "GFL99_01": "OFuSwKgm", - "GFL99_2": "OFiCHnso", - "GFL99_3": "OFiCHnso", - "GFL99_4": "OFiCHnso", - "GFL99_5": "OFiCHnso", - "GFL99_6": "OFl4ItvQ", - "GFL99_7": "OFl4ItvQ", - "GFLLK10PETG": "OFYPdQJh", - "GFLLK10PLA": "OFDSrzZ8", - "GFLLK10PPETG": "OFYPdQJh", - "GFLLK10PPLA": "OFDSrzZ8", - "GFLSBS99": "OFhQfUQY", - "GFN05_01": "OFe8tuNb", - "GFN96": "OF8Tlg47", - "GFN96_01": "OFptvi3F", - "GFN97": "OF8tPByX", - "GFN97_01": "OFSPdsuW", - "GFN98": "OFQLcbps", - "GFN98_02": "OFHa3RZT", - "GFN98_1": "OFfznI0Q", - "GFN98_2": "OFfznI0Q", - "GFN98_3": "OFfznI0Q", - "GFN98_4": "OFfznI0Q", - "GFN99": "OFg8ndtj", - "GFN99_01": "OFQswlnF", - "GFN99_1": "OFIy3kLw", - "GFN99_2": "OFIy3kLw", - "GFN99_3": "OFIy3kLw", - "GFN99_4": "OFIy3kLw", - "GFOT001": "OFxA1p01", - "GFOT002": "OFGkyftV", - "GFOT003": "OF1Pke8t", - "GFOT004": "OFMZvH0z", - "GFOT005": "OF2pj5l5", - "GFOT006": "OFcfQk4h", - "GFOT008": "OFcvKkrs", - "GFOT009": "OFM91vZs", - "GFP95": "OFNiSLfL", - "GFP95_01": "OFkYkQGO", - "GFP96": "OFXkm8q1", - "GFP96_01": "OFMTBcnz", - "GFP97": "OF1UNk9P", - "GFP97_01": "OF3s929u", - "GFP98": "OFd0Fv0k", - "GFP98_01": "OFkUaDnU", - "GFP99": "OFPklMI1", - "GFP99_01": "OFKuTr12", - "GFPA": "OFzS7zt4", - "GFPA6-CF": "OFQe7sAx", - "GFPAHT-CF": "OFaEuvNF", - "GFPC": "OFZtkHRq", - "GFPC-CF": "OFeIyhUx", - "GFPC-GF": "OFxJRuVF", - "GFPEBA": "OFjLAYgO", - "GFPET-CF": "OFRhCTUg", - "GFPETG": "OFYPdQJh", - "GFPETG-1": "OFIeCuXj", - "GFPETG-CF": "OF9UJOGF", - "GFPLA Glow": "OF5PtjTp", - "GFPLA Translucent": "OF7c9fln", - "GFPLA-CF": "OFZ0mR5b", - "GFPVA": "OFTkj6DR", - "GFR98": "OF74KeQR", - "GFR98_01": "OFxvKuYW", - "GFR99": "OFCD8qiU", - "GFR99_01": "OFrnJJEa", - "GFS04_01": "OFyreocX", - "GFS97": "OFo2UF2C", - "GFS97_01": "OFGDaEHO", - "GFS98": "OFsFon5l", - "GFS98_01": "OFW9uIkE", - "GFS99": "OFDvXujf", - "GFS99_01": "OFMZAoqa", - "GFS99_1": "OFcCzEkS", - "GFS99_2": "OFcCzEkS", - "GFS99_3": "OFcCzEkS", - "GFS99_4": "OFcCzEkS", - "GFS99_5": "OFoT8F7Y", - "GFS99_6": "OFoT8F7Y", - "GFSEP001": "OFtudokz", - "GFSEP002": "OFf5awy4", - "GFSEP003": "OFbP8zEO", - "GFSEP004": "OFrFBEfd", - "GFSEP005": "OFvzvwCu", - "GFSEP006": "OFH2nN08", - "GFSEP007": "OFPc4zVY", - "GFSEP008": "OFkzr35f", - "GFSEP009": "OFHfzQXo", - "GFSEP010": "OFhWhL1i", - "GFSEP011": "OFx8Lmsd", - "GFSEP012": "OFRiFnfQ", - "GFSEP013": "OFhfd722", - "GFSEP014": "OFIegjmD", - "GFSEP015": "OFvVy7yo", - "GFSEP016": "OFTOPcx0", - "GFSEP017": "OFXCBZTA", - "GFSEP018": "OFlSNkhc", - "GFSL99": "OFqOk9Ww", - "GFT01_01": "OFCnywiC", - "GFT97_01": "OFvPomQf", - "GFT98_01": "OFGzUNbm", - "GFTPU 95A": "OFML2aVd", - "GFTPU for ACE": "OF0FfcAG", - "GFU01_01": "OFDJU6R3", - "GFU99": "OFgbpcy9", - "GFU99_01": "OFCVUEaV", - "GFU99_2": "OFbenYUK", - "GFU99_3": "OFU3zwPc" - }, - "retired": { - "00001": { - "claims": [ - "Creality/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "00002": { - "claims": [ - "Creality/Generic PLA-Silk" - ], - "successor": "OFi6PfUM" - }, - "00003": { - "claims": [ - "Creality/Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "00004": { - "claims": [ - "Creality/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "00005": { - "claims": [ - "Creality/Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "00006": { - "claims": [ - "Creality/Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "00007": { - "claims": [ - "Creality/Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "00008": { - "claims": [ - "Creality/Generic PA" - ], - "successor": "OFg8ndtj" - }, - "00009": { - "claims": [ - "Creality/Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "00010": { - "claims": [ - "Creality/Generic BVOH" - ], - "successor": "OFo2UF2C" - }, - "00011": { - "claims": [ - "Creality/Generic PVA" - ], - "successor": "OFDvXujf" - }, - "00012": { - "claims": [ - "Creality/Generic HIPS" - ], - "successor": "OFsFon5l" - }, - "00013": { - "claims": [ - "Creality/Generic PET-CF" - ], - "successor": "OFQdaVZS" - }, - "00014": { - "claims": [ - "Creality/Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "00015": { - "claims": [ - "Creality/Generic PA6-CF" - ], - "successor": "OFhTPf6L" - }, - "00016": { - "claims": [ - "Creality/Generic PAHT-CF" - ], - "successor": "OFH7b12v" - }, - "00017": { - "claims": [ - "Creality/Generic PPS" - ], - "successor": "OFq9svOz" - }, - "00018": { - "claims": [ - "Creality/Generic PPS-CF" - ], - "successor": "OF6rdQ6M" - }, - "00019": { - "claims": [ - "Creality/Generic PP" - ], - "successor": "OF1UNk9P" - }, - "00020": { - "claims": [ - "Creality/Generic PET" - ], - "successor": "OF6MOPWx" - }, - "00021": { - "claims": [ - "Creality/Generic PC" - ], - "successor": "OFLakOUI" - }, - "00022": { - "claims": [ - "Creality/Generic PA612-CF" - ], - "successor": "OF8eaY7j" - }, - "00023": { - "claims": [ - "Creality/Generic Support for PA" - ], - "successor": "OFDtop41" - }, - "00024": { - "claims": [ - "Creality/Generic Support for PLA" - ], - "successor": "OFin64Qg" - }, - "00025": { - "claims": [ - "Creality/Generic PA12-CF" - ], - "successor": "OFM4iJlv" - }, - "00026": { - "claims": [ - "Creality/Generic TPU 64D" - ], - "successor": "OFF3cuzT" - }, - "00027": { - "claims": [ - "Creality/Generic PETG-GF" - ], - "successor": "OFyLWVF3" - }, - "00031": { - "claims": [ - "Creality/Generic PP-CF" - ], - "successor": "OFXkm8q1" - }, - "00032": { - "claims": [ - "Creality/Generic PCTG" - ], - "successor": "OFu1evlr" - }, - "00033": { - "claims": [ - "Creality/Generic ASA-CF" - ], - "successor": "OF53eLVD" - }, - "00034": { - "claims": [ - "Creality/Generic PA6-GF" - ], - "successor": "OFh9u9c0" - }, - "00035": { - "claims": [ - "Creality/eSUN PLA-LW" - ], - "successor": "OFi5RSve" - }, - "01001": { - "claims": [ - "Creality/Creality Hyper PLA", - "Creality/Hyper PLA" - ], - "successor": "OFAIrQXu" - }, - "01002": { - "claims": [ - "Creality/Hyper L-W PLA" - ], - "successor": "OF70zbGS" - }, - "01003": { - "claims": [ - "Creality/Hyper Luminous" - ], - "successor": "OFC1PzXz" - }, - "01004": { - "claims": [ - "Creality/Hyper Stardust" - ], - "successor": "OFXnToSX" - }, - "01601": { - "claims": [ - "Creality/Soleyin Ultra PLA" - ], - "successor": "OF02UAVh" - }, - "02001": { - "claims": [ - "Creality/Creality Hyper PLA-CF", - "Creality/Hyper PLA-CF" - ], - "successor": "OFGwT5Av" - }, - "03001": { - "claims": [ - "Creality/Creality Hyper ABS", - "Creality/Hyper ABS" - ], - "successor": "OF8GnyFU" - }, - "04001": { - "claims": [ - "Creality/CR-PLA", - "Creality/Creality Generic PLA" - ], - "successor": "OFACSw9O" - }, - "05001": { - "claims": [ - "Creality/CR-Silk", - "Creality/Creality Silk PLA" - ], - "successor": "OF3OMgrI" - }, - "06001": { - "claims": [ - "Creality/CR-PETG", - "Creality/Creality Generic PETG" - ], - "successor": "OF3WQaKK" - }, - "06002": { - "claims": [ - "Creality/Hyper PETG" - ], - "successor": "OFujZSdh" - }, - "06003": { - "claims": [ - "Creality/Hyper PETG-CF" - ], - "successor": "OFusbWjj" - }, - "06004": { - "claims": [ - "Creality/Hyper PETG-GF" - ], - "successor": "OFLgwqp2" - }, - "06005": { - "claims": [ - "Creality/Soleyin Basic PETG" - ], - "successor": "OFBLktDy" - }, - "07001": { - "claims": [ - "Creality/CR-ABS", - "Creality/Creality Generic ABS" - ], - "successor": "OFGwZmgS" - }, - "07002": { - "claims": [ - "Creality/Hyper PC" - ], - "successor": "OFUF7oA4" - }, - "08001": { - "claims": [ - "Creality/Ender-PLA" - ], - "successor": "OFWao3Ic" - }, - "09001": { - "claims": [ - "Creality/EN-PLA+" - ], - "successor": "OFks6esg" - }, - "09002": { - "claims": [ - "Creality/ENDER FAST PLA" - ], - "successor": "OFfSqS4R" - }, - "10001": { - "claims": [ - "Creality/Creality Generic TPU", - "Creality/HP-TPU" - ], - "successor": "OFGcMkEB" - }, - "1009481135": { - "claims": [ - "Snapmaker/Snapmaker J1 PET" - ], - "successor": "OF8Jl6NT" - }, - "1042511226": { - "claims": [ - "Snapmaker/Snapmaker Dual PETG-CF" - ], - "successor": "OF3msXEa" - }, - "10425112260": { - "claims": [ - "Snapmaker/Snapmaker PETG-CF" - ], - "successor": "OFOwQuZM" - }, - "11001": { - "claims": [ - "Creality/CR-Nylon", - "Creality/Creality Generic PA" - ], - "successor": "OFMqYusc" - }, - "1172603684": { - "claims": [ - "Snapmaker/Snapmaker J1 PETG" - ], - "successor": "OFQMTRc8" - }, - "1181363872": { - "claims": [ - "Snapmaker/Snapmaker Dual PLA Silk" - ], - "successor": "OFyj3m13" - }, - "11813638720": { - "claims": [ - "Snapmaker/Snapmaker PLA Silk" - ], - "successor": "OFWgFOjn" - }, - "1192769348": { - "claims": [ - "Snapmaker/Snapmaker J1 PLA Matte" - ], - "successor": "OFY3AB7j" - }, - "12002": { - "claims": [ - "Creality/Hyper PPA-CF" - ], - "successor": "OFNvBIL0" - }, - "12003": { - "claims": [ - "Creality/Hyper PAHT-CF" - ], - "successor": "OF4APyxe" - }, - "12004": { - "claims": [ - "Creality/Hyper PA612-CF" - ], - "successor": "OFhkN0a7" - }, - "12005": { - "claims": [ - "Creality/Hyper PA6-CF" - ], - "successor": "OFn2GlhY" - }, - "1207881278": { - "claims": [ - "Snapmaker/Snapmaker Dual Breakaway" - ], - "successor": "OFfmHUkQ" - }, - "1210173120": { - "claims": [ - "Snapmaker/Snapmaker J1 PA-CF" - ], - "successor": "OFYEtXuk" - }, - "1223824394": { - "claims": [ - "Snapmaker/Snapmaker J1 ABS" - ], - "successor": "OFy14TRA" - }, - "1247172706": { - "claims": [ - "Snapmaker/Snapmaker Dual ASA" - ], - "successor": "OFKUEXPA" - }, - "12471727060": { - "claims": [ - "Snapmaker/Snapmaker ASA" - ], - "successor": "OF1d7c3A" - }, - "13001": { - "claims": [ - "Creality/CR-PLA Carbon" - ], - "successor": "OFrM4hdm" - }, - "1344609062": { - "claims": [ - "Snapmaker/Snapmaker PVA" - ], - "successor": "OFyhjiNs" - }, - "1355502217": { - "claims": [ - "Snapmaker/Snapmaker PETG-CF" - ], - "successor": "OFOwQuZM" - }, - "1393866034": { - "claims": [ - "Snapmaker/PolyLite Dual PLA", - "Snapmaker/PolyLite J1 PLA", - "Snapmaker/PolyLite PLA" - ], - "successor": "OF5CgdDq" - }, - "14001": { - "claims": [ - "Creality/CR-PLA Matte" - ], - "successor": "OFM0xjBG" - }, - "1417031127": { - "claims": [ - "Snapmaker/Snapmaker Dual PLA" - ], - "successor": "OFZivHGL" - }, - "14170311270": { - "claims": [ - "Snapmaker/Snapmaker PLA" - ], - "successor": "OFlufQpZ" - }, - "141703112701": { - "claims": [ - "Snapmaker/Snapmaker PLA SnapSpeed" - ], - "successor": "OFwhLMs4" - }, - "1417031127011": { - "claims": [ - "Snapmaker/Snapmaker PLA Lite" - ], - "successor": "OFEOnBj6" - }, - "144877656": { - "claims": [ - "Snapmaker/Snapmaker J1 ASA" - ], - "successor": "OFt7SngH" - }, - "1480063856": { - "claims": [ - "Snapmaker/Snapmaker TPU" - ], - "successor": "OF65InrS" - }, - "15001": { - "claims": [ - "Creality/CR-PLA Fluo" - ], - "successor": "OFgpQwkk" - }, - "1528786603": { - "claims": [ - "Snapmaker/Snapmaker J1 PLA Silk" - ], - "successor": "OFQ1KzO8" - }, - "16001": { - "claims": [ - "Creality/CR-TPU" - ], - "successor": "OFG2RkhN" - }, - "1655727393": { - "claims": [ - "Snapmaker/Snapmaker PLA Silk" - ], - "successor": "OFWgFOjn" - }, - "168223792": { - "claims": [ - "Snapmaker/Snapmaker Dual ABS" - ], - "successor": "OFJhT2fd" - }, - "1682237920": { - "claims": [ - "Snapmaker/Snapmaker ABS" - ], - "successor": "OF72YYTd" - }, - "1695556157": { - "claims": [ - "Snapmaker/Snapmaker PLA Eco" - ], - "successor": "OFpu74Qe" - }, - "17001": { - "claims": [ - "Creality/CR-Wood" - ], - "successor": "OFGxC8gB" - }, - "1702147325": { - "claims": [ - "Snapmaker/Snapmaker Dual PLA-CF" - ], - "successor": "OF6ATsCF" - }, - "17021473250": { - "claims": [ - "Snapmaker/Snapmaker PLA-CF" - ], - "successor": "OFhQf8ou" - }, - "18001": { - "claims": [ - "Creality/HP Ultra PLA" - ], - "successor": "OFCJpR7a" - }, - "1895495477": { - "claims": [ - "Snapmaker/Snapmaker PETG" - ], - "successor": "OF4WB37S" - }, - "19001": { - "claims": [ - "Creality/Creality Generic ASA", - "Creality/HP-ASA" - ], - "successor": "OFPHLnoe" - }, - "200803790": { - "claims": [ - "Snapmaker/Snapmaker Dual PLA Eco" - ], - "successor": "OFyPG8v2" - }, - "2008037900": { - "claims": [ - "Snapmaker/Snapmaker PLA Eco" - ], - "successor": "OFpu74Qe" - }, - "2029994346": { - "claims": [ - "Snapmaker/Snapmaker Dual PLA Metal" - ], - "successor": "OFg3cWVt" - }, - "20299943460": { - "claims": [ - "Snapmaker/Snapmaker PLA Metal" - ], - "successor": "OFtybfNX" - }, - "2128577941": { - "claims": [ - "Snapmaker/Snapmaker Dual PET" - ], - "successor": "OFUIfGoh" - }, - "21285779410": { - "claims": [ - "Snapmaker/Snapmaker PET" - ], - "successor": "OFQYaiGg" - }, - "2209001062": { - "claims": [ - "Snapmaker/Snapmaker Dual PETG" - ], - "successor": "OFBlFMVp" - }, - "22090010620": { - "claims": [ - "Snapmaker/Snapmaker PETG" - ], - "successor": "OF4WB37S" - }, - "2549587591": { - "claims": [ - "Snapmaker/Snapmaker PET" - ], - "successor": "OFQYaiGg" - }, - "2742961008": { - "claims": [ - "Snapmaker/Snapmaker ASA" - ], - "successor": "OF1d7c3A" - }, - "29001": { - "claims": [ - "Creality/Hyper Marble" - ], - "successor": "OFLzx3J4" - }, - "2971656290": { - "claims": [ - "Snapmaker/Snapmaker Dual TPU" - ], - "successor": "OFjkdnyN" - }, - "29716562900": { - "claims": [ - "Snapmaker/Snapmaker TPU" - ], - "successor": "OF65InrS" - }, - "297165629001": { - "claims": [ - "Snapmaker/Snapmaker TPU 95A" - ], - "successor": "OFKo87c6" - }, - "3104636980": { - "claims": [ - "Snapmaker/Snapmaker Dual PVA" - ], - "successor": "OFDKIKyw" - }, - "31046369800": { - "claims": [ - "Snapmaker/Snapmaker PVA" - ], - "successor": "OFyhjiNs" - }, - "3177068229": { - "claims": [ - "Snapmaker/Snapmaker PLA" - ], - "successor": "OFlufQpZ" - }, - "3383257822": { - "claims": [ - "Snapmaker/Snapmaker J1 PLA Eco" - ], - "successor": "OFLbdkrE" - }, - "3492897526": { - "claims": [ - "Snapmaker/Snapmaker J1 Breakaway" - ], - "successor": "OFeTATC7" - }, - "3493177425": { - "claims": [ - "Snapmaker/Snapmaker Dual PA-CF" - ], - "successor": "OFRooa0F" - }, - "34931774250": { - "claims": [ - "Snapmaker/Snapmaker PA-CF" - ], - "successor": "OFEmsric" - }, - "3503790988": { - "claims": [ - "Snapmaker/Snapmaker Dual PLA Matte" - ], - "successor": "OFlIsW6f" - }, - "377675245": { - "claims": [ - "Snapmaker/Snapmaker J1 PLA" - ], - "successor": "OFSa39yP" - }, - "3806593857": { - "claims": [ - "Snapmaker/Snapmaker J1 PLA-CF" - ], - "successor": "OFdD5Wbu" - }, - "3811508002": { - "claims": [ - "Snapmaker/Snapmaker ABS" - ], - "successor": "OF72YYTd" - }, - "3864371306": { - "claims": [ - "Snapmaker/Snapmaker PLA-CF" - ], - "successor": "OFhQf8ou" - }, - "4012961186": { - "claims": [ - "Snapmaker/Snapmaker J1 PLA Metal" - ], - "successor": "OFt0JbR7" - }, - "4092268632": { - "claims": [ - "Snapmaker/Snapmaker J1 TPU" - ], - "successor": "OFyrnlUn" - }, - "4227461134": { - "claims": [ - "Snapmaker/Snapmaker Breakaway Support For PLA", - "Snapmaker/Snapmaker J1 PVA" - ], - "successor": "OF6TuVAg" - }, - "4235401834": { - "claims": [ - "Snapmaker/Snapmaker J1 PETG-CF" - ], - "successor": "OFxYB4Sw" - }, - "581236806": { - "claims": [ - "Snapmaker/Snapmaker PA-CF" - ], - "successor": "OFEmsric" - }, - "AZ01-1": { - "claims": [ - "BBL/AliZ PETG-CF", - "OrcaFilamentLibrary/AliZ PETG-CF" - ], - "successor": "OFJOFnOZ" - }, - "AZ01-2": { - "claims": [ - "BBL/AliZ PETG-Metal", - "OrcaFilamentLibrary/AliZ PETG-Metal" - ], - "successor": "OFOABq7t" - }, - "AliZ001": { - "claims": [ - "BBL/AliZ PETG", - "OrcaFilamentLibrary/AliZ PETG" - ], - "successor": "OFocyw69" - }, - "AliZ002": { - "claims": [ - "BBL/AliZ PLA", - "OrcaFilamentLibrary/AliZ PLA" - ], - "successor": "OFyxayW4" - }, - "AliZ003": { - "claims": [ - "BBL/AliZ PA-CF", - "OrcaFilamentLibrary/AliZ PA-CF" - ], - "successor": "OFd4zw5l" - }, - "BSFI001": { - "claims": [ - "Blocks/Blocks Generic PLA" - ], - "successor": "OFNYewRS" - }, - "BSFI002": { - "claims": [ - "Blocks/Blocks Generic PETG" - ], - "successor": "OFrgGex8" - }, - "BSFI003": { - "claims": [ - "Blocks/Blocks Generic TPU" - ], - "successor": "OFMEbuBE" - }, - "BSFI004": { - "claims": [ - "Blocks/Blocks Generic ABS" - ], - "successor": "OFma52eS" - }, - "BSFI005": { - "claims": [ - "Blocks/Blocks Generic ASA" - ], - "successor": "OFB9dx27" - }, - "BSFI006": { - "claims": [ - "Blocks/Blocks Generic PA" - ], - "successor": "OFiUSDih" - }, - "BSFI007": { - "claims": [ - "Blocks/Blocks Generic PA-CF" - ], - "successor": "OF8bLDu7" - }, - "BSFI008": { - "claims": [ - "Blocks/Blocks Generic PC" - ], - "successor": "OFahE7HP" - }, - "BSFI009": { - "claims": [ - "Blocks/Blocks Generic PVA" - ], - "successor": "OFiorRtJ" - }, - "BSFI010": { - "claims": [ - "Blocks/Blocks Generic PLA-CF" - ], - "successor": "OFupq0tY" - }, - "CX30DB20": { - "claims": [ - "BBL/COEX TPE 30D", - "OrcaFilamentLibrary/COEX TPE 30D" - ], - "successor": "OFVptRxp" - }, - "CX40DB20": { - "claims": [ - "BBL/COEX TPE 40D", - "OrcaFilamentLibrary/COEX TPE 40D" - ], - "successor": "OFfnXFie" - }, - "CX60AB17": { - "claims": [ - "BBL/COEX TPU 60A", - "OrcaFilamentLibrary/COEX TPU 60A" - ], - "successor": "OFE0NFRh" - }, - "CX60DB19": { - "claims": [ - "BBL/COEX TPE 60D", - "OrcaFilamentLibrary/COEX TPE 60D" - ], - "successor": "OFfr4XNO" - }, - "CXABPB09": { - "claims": [ - "BBL/COEX ABS PRIME", - "OrcaFilamentLibrary/COEX ABS PRIME" - ], - "successor": "OFuWCQXN" - }, - "CXABSB01": { - "claims": [ - "BBL/COEX ABS", - "OrcaFilamentLibrary/COEX ABS" - ], - "successor": "OF99UMPq" - }, - "CXASAB04": { - "claims": [ - "BBL/COEX ASA PRIME", - "OrcaFilamentLibrary/COEX ASA PRIME" - ], - "successor": "OFtpXCCv" - }, - "CXPACB23": { - "claims": [ - "BBL/COEX NYLEX PA6-CF", - "OrcaFilamentLibrary/COEX NYLEX PA6-CF" - ], - "successor": "OF0NgY1y" - }, - "CXPAUB21": { - "claims": [ - "BBL/COEX NYLEX UNFILLED " - ], - "successor": "OFJs1ToM" - }, - "CXPCTB15": { - "claims": [ - "BBL/COEX PCTG PRIME", - "OrcaFilamentLibrary/COEX PCTG PRIME" - ], - "successor": "OFwu7IAG" - }, - "CXPETB13": { - "claims": [ - "BBL/COEX PETG", - "OrcaFilamentLibrary/COEX PETG" - ], - "successor": "OFvTVZc3" - }, - "CXPLAB02": { - "claims": [ - "BBL/COEX PLA", - "OrcaFilamentLibrary/COEX PLA" - ], - "successor": "OFhlCNqq" - }, - "CXPLAB08": { - "claims": [ - "BBL/COEX PLA PRIME", - "OrcaFilamentLibrary/COEX PLA PRIME" - ], - "successor": "OFd2vX0G" - }, - "CXPLSB03": { - "claims": [ - "BBL/COEX PLA+Silk", - "OrcaFilamentLibrary/COEX PLA+Silk" - ], - "successor": "OF0pzFLc" - }, - "DFL99": { - "claims": [ - "Volumic/Désactivé" - ], - "successor": "OFCuaCer" - }, - "DREMC001": { - "claims": [], - "successor": null - }, - "DREMC002": { - "claims": [], - "successor": null - }, - "DREMC003": { - "claims": [], - "successor": null - }, - "DREMC004": { - "claims": [], - "successor": null - }, - "DREMC005": { - "claims": [], - "successor": null - }, - "DREMC006": { - "claims": [ - "OrcaFilamentLibrary/DREMC PA6-CF" - ], - "successor": "OFsoykbm" - }, - "DREMC007": { - "claims": [], - "successor": null - }, - "DREMC008": { - "claims": [], - "successor": null - }, - "DREMC009": { - "claims": [ - "OrcaFilamentLibrary/DREMC ABS-GF" - ], - "successor": "OFCidU7j" - }, - "DREMC010": { - "claims": [ - "OrcaFilamentLibrary/DREMC PPA-CF" - ], - "successor": "OFiWaoqD" - }, - "DREMC011": { - "claims": [], - "successor": null - }, - "DREMC012": { - "claims": [], - "successor": null - }, - "DREMC013": { - "claims": [], - "successor": null - }, - "E1001": { - "claims": [ - "Creality/eSUN PLA+" - ], - "successor": "OFqINlYj" - }, - "E1002": { - "claims": [ - "Creality/eSUN PLA-Silk" - ], - "successor": "OFIRUqWi" - }, - "E1003": { - "claims": [ - "Creality/eSUN PLA-Matte" - ], - "successor": "OFDETOM6" - }, - "E1004": { - "claims": [ - "Creality/eSUN PLA-Lite" - ], - "successor": "OFhbolij" - }, - "E1005": { - "claims": [ - "Creality/eSUN PLA-CF" - ], - "successor": "OFnHc3D6" - }, - "E1006": { - "claims": [ - "Creality/eSUN PLA-HS" - ], - "successor": "OFOHOf2F" - }, - "E2001": { - "claims": [ - "Creality/eSUN PETG" - ], - "successor": "OF3kBrdA" - }, - "E2002": { - "claims": [ - "Creality/eSUN PETG+HS" - ], - "successor": "OFrdZ0cK" - }, - "E2003": { - "claims": [ - "Creality/eSUN PETG-Basic" - ], - "successor": "OFFbQscs" - }, - "E3001": { - "claims": [ - "Creality/eSUN ABS+" - ], - "successor": "OFTXduCM" - }, - "E4001": { - "claims": [ - "Creality/eSUN ASA+" - ], - "successor": "OFUmS5z5" - }, - "E8001": { - "claims": [ - "Creality/eSUN PET-Basic" - ], - "successor": "OFHaYaB1" - }, - "EABSB00": { - "claims": [ - "Elegoo/Elegoo ABS" - ], - "successor": "OFxOHhZw" - }, - "EASAB00": { - "claims": [ - "Elegoo/Elegoo ASA" - ], - "successor": "OFobDOW5" - }, - "EFL33": { - "claims": [ - "Eryone/Eryone PP-CF" - ], - "successor": "OFbh7HcA" - }, - "EFL40": { - "claims": [ - "Eryone/Eryone PLA-CF" - ], - "successor": "OFilAA3b" - }, - "EFL43": { - "claims": [ - "Eryone/Eryone PETG-CF" - ], - "successor": "OFNstOna" - }, - "EFL71": { - "claims": [ - "Eryone/Eryone PA" - ], - "successor": "OFS4jbj3" - }, - "EFL72": { - "claims": [ - "Eryone/Eryone PA-CF" - ], - "successor": "OFp9wdmf" - }, - "EFL73": { - "claims": [ - "Eryone/Eryone PA-GF" - ], - "successor": "OFkq7rPy" - }, - "EFL81": { - "claims": [ - "Eryone/Eryone ABS-CF" - ], - "successor": "OFQ1zgm3" - }, - "EFL82": { - "claims": [ - "Eryone/Eryone ASA-CF" - ], - "successor": "OFwyt1xt" - }, - "EFL90": { - "claims": [ - "Eryone/Eryone PLA" - ], - "successor": "OFmKU2Ha" - }, - "EFL91": { - "claims": [ - "Eryone/Eryone ABS" - ], - "successor": "OFOzwywz" - }, - "EFL92": { - "claims": [ - "Eryone/Eryone ASA" - ], - "successor": "OFVtIasg" - }, - "EFL93": { - "claims": [ - "Eryone/Eryone PETG" - ], - "successor": "OF8kJys9" - }, - "EFL941": { - "claims": [ - "Eryone/Eryone Silk PLA" - ], - "successor": "OF7nBAd3" - }, - "EFL95": { - "claims": [ - "Eryone/Eryone TPU" - ], - "successor": "OFhUjPA5" - }, - "EPAHTB00": { - "claims": [ - "Elegoo/Elegoo PAHT-CF" - ], - "successor": "OF0h7ERL" - }, - "EPCB00": { - "claims": [ - "Elegoo/Elegoo PC" - ], - "successor": "OFUyDDNv" - }, - "EPETGB00": { - "claims": [ - "Elegoo/Elegoo PETG" - ], - "successor": "OFLcd093" - }, - "EPLAB00": { - "claims": [ - "Elegoo/Elegoo PLA" - ], - "successor": "OFvgE0Zh" - }, - "ESN02": { - "claims": [ - "OrcaFilamentLibrary/eSUN ePLA-LW" - ], - "successor": "OFRzR5mi" - }, - "ESN03": { - "claims": [ - "OrcaFilamentLibrary/eSUN PETG" - ], - "successor": "OF3kBrdA" - }, - "ETPUB00": { - "claims": [ - "Elegoo/Elegoo TPU 95A" - ], - "successor": "OFmhJs5V" - }, - "FDPLAST01": { - "claims": [ - "OrcaFilamentLibrary/FDplast ABS" - ], - "successor": "OFjorDcx" - }, - "FDPLAST02": { - "claims": [ - "OrcaFilamentLibrary/FDplast HIPS" - ], - "successor": "OFmJAd3A" - }, - "FDPLAST03": { - "claims": [ - "OrcaFilamentLibrary/FDplast PETG" - ], - "successor": "OFfJSCzm" - }, - "FDPLAST04": { - "claims": [ - "OrcaFilamentLibrary/FDplast PLA" - ], - "successor": "OFtr8eZw" - }, - "FDPLAST05": { - "claims": [ - "OrcaFilamentLibrary/FDplast SBS" - ], - "successor": "OFtRn8X8" - }, - "FDPLAST06": { - "claims": [ - "OrcaFilamentLibrary/FDplast TPU" - ], - "successor": "OFfmZrI3" - }, - "FFF01": { - "claims": [ - "Flashforge/Flashforge PLA" - ], - "successor": "OFDKgoqx" - }, - "FFF02": { - "claims": [ - "Flashforge/Flashforge ABS" - ], - "successor": "OF5SJ3jz" - }, - "FFF03": { - "claims": [ - "Flashforge/Flashforge PETG", - "Flashforge/Generic PETG" - ], - "successor": "OFMpxamb" - }, - "FFG01": { - "claims": [ - "Flashforge/Flashforge Generic PLA" - ], - "successor": "OFtTHNTj" - }, - "FILAR0001": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Bronce" - ], - "successor": "OFWTRJ1K" - }, - "FILAR0002": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Gris Plata" - ], - "successor": "OFKKajh6" - }, - "FILAR0003": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Cobre" - ], - "successor": "OFNemJM6" - }, - "FILAR0004": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Titanio" - ], - "successor": "OFQWIKbV" - }, - "FILAR0005": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Tabaco" - ], - "successor": "OFjT9PkZ" - }, - "FILAR0006": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Cafe con Leche" - ], - "successor": "OFWwF7K7" - }, - "FILAR0007": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Manteca" - ], - "successor": "OFexaZU2" - }, - "FILAR0008": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Marron Oxido" - ], - "successor": "OFAt6ec0" - }, - "FILAR0009": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Carpincho" - ], - "successor": "OFKWWgyp" - }, - "FILAR0010": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Rosa Amaranto" - ], - "successor": "OFPqX3x2" - }, - "FILAR0011": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Rosa Flamenco" - ], - "successor": "OFZ4QbfK" - }, - "FILAR0012": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Piel" - ], - "successor": "OFsDXRpS" - }, - "FILAR0013": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Verde FilAr" - ], - "successor": "OFvPPMxL" - }, - "FILAR0014": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Verde Manzana" - ], - "successor": "OFdb8YNz" - }, - "FILAR0015": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Verde Pixel" - ], - "successor": "OFkGvN4d" - }, - "FILAR0016": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Verde Oliva" - ], - "successor": "OFkoLUtR" - }, - "FILAR0017": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Blanco Antartida" - ], - "successor": "OFHAccHz" - }, - "FILAR0018": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Blanco Calido" - ], - "successor": "OFKTSiYF" - }, - "FILAR0019": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Negro Azabache" - ], - "successor": "OFgMWJ8T" - }, - "FILAR0020": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Naranja Tigre" - ], - "successor": "OFKZ9skj" - }, - "FILAR0021": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Rojo de Carreras" - ], - "successor": "OFaEesXQ" - }, - "FILAR0022": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Amarillo Lirio" - ], - "successor": "OF8aFWfQ" - }, - "FILAR0023": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Violeta Jacaranda" - ], - "successor": "OFP26zNb" - }, - "FILAR0024": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Gris Pizarra" - ], - "successor": "OFTlRzpe" - }, - "FILAR0025": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Gris Ceniza" - ], - "successor": "OFIamQnD" - }, - "FILAR0026": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Azul Francia" - ], - "successor": "OFnYVFk2" - }, - "FILAR0027": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Celeste Cielo" - ], - "successor": "OFaDOaNt" - }, - "FILAR0028": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Oro" - ], - "successor": "OF5nlfKY" - }, - "FILAR0029": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA Dorado" - ], - "successor": "OF5P4jgA" - }, - "FILAR0030": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Amarillo" - ], - "successor": "OFj3bTjw" - }, - "FILAR0031": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Azul" - ], - "successor": "OF5vgi2G" - }, - "FILAR0032": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Beige" - ], - "successor": "OFGcLA5R" - }, - "FILAR0033": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Blanco" - ], - "successor": "OFtefokm" - }, - "FILAR0034": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Bordo" - ], - "successor": "OFiDpM1B" - }, - "FILAR0035": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Celeste Cielo" - ], - "successor": "OFqtx549" - }, - "FILAR0036": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Chocolate" - ], - "successor": "OFdzlrhi" - }, - "FILAR0037": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Gris" - ], - "successor": "OFGVwfK0" - }, - "FILAR0038": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Marron" - ], - "successor": "OF8jh0Lk" - }, - "FILAR0039": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Naranja" - ], - "successor": "OFmaeU4a" - }, - "FILAR0040": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Negro" - ], - "successor": "OFufTAK9" - }, - "FILAR0041": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Piel" - ], - "successor": "OFfZcoKr" - }, - "FILAR0042": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Rojo" - ], - "successor": "OFIBXa0k" - }, - "FILAR0043": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Rosa" - ], - "successor": "OFausr3F" - }, - "FILAR0044": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Uva" - ], - "successor": "OFEZ449N" - }, - "FILAR0045": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Verde" - ], - "successor": "OFCjG251" - }, - "FILAR0046": { - "claims": [ - "OrcaFilamentLibrary/FilAr PLA-mate Violeta" - ], - "successor": "OF2EK9F1" - }, - "FILAR0047": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Amarillo Lima" - ], - "successor": "OFt6e0US" - }, - "FILAR0048": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Amarillo Radiante" - ], - "successor": "OF0Vtzaw" - }, - "FILAR0049": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Azul Boreal" - ], - "successor": "OFNU9YIW" - }, - "FILAR0050": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Azul Francia" - ], - "successor": "OF44VYzK" - }, - "FILAR0051": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Azul Imperial" - ], - "successor": "OF2Z1nT5" - }, - "FILAR0052": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Blanco Antartida" - ], - "successor": "OFsXQXbs" - }, - "FILAR0053": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Cian" - ], - "successor": "OFnejEt4" - }, - "FILAR0054": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Coral" - ], - "successor": "OFysxCep" - }, - "FILAR0055": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Cristal" - ], - "successor": "OFJQDUk3" - }, - "FILAR0056": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Gris Ceniza" - ], - "successor": "OFybzvQN" - }, - "FILAR0057": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Gris Plata" - ], - "successor": "OFgpLTMR" - }, - "FILAR0058": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Magenta" - ], - "successor": "OFurdQxc" - }, - "FILAR0059": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Negro Azabache" - ], - "successor": "OFq8gfS4" - }, - "FILAR0060": { - "claims": [ - "OrcaFilamentLibrary/FilAr PETG Rojo Carmesi" - ], - "successor": "OFPbyOSk" - }, - "FILARB01": { - "claims": [], - "successor": null - }, - "FILARB02": { - "claims": [], - "successor": null - }, - "FILARB03": { - "claims": [], - "successor": null - }, - "FILL3D001": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PLA Turbo" - ], - "successor": "OFoSknDB" - }, - "FILL3D002": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PLA Basic" - ], - "successor": "OFDpW3J8" - }, - "FILL3D003": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PETG" - ], - "successor": "OFQbjX3s" - }, - "FILL3D004": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PP" - ], - "successor": "OFPWPlSC" - }, - "FILL3D005": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PPCF" - ], - "successor": "OFg8hMzT" - }, - "FILL3D006": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PA" - ], - "successor": "OFJNeELv" - }, - "FILL3D007": { - "claims": [ - "OrcaFilamentLibrary/FILL3D PETG CF" - ], - "successor": "OFOiJfLM" - }, - "GABSB00": { - "claims": [ - "Elegoo/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "GASAB00": { - "claims": [ - "Elegoo/Generic ASA", - "Elegoo/Generic ASA-CF" - ], - "successor": "OFLPAxz3" - }, - "GFABS": { - "claims": [ - "Anycubic/Anycubic ABS" - ], - "successor": "OF223rZv" - }, - "GFL92": { - "claims": [ - "Anycubic/Anycubic PLA" - ], - "successor": "OFmjN2bc" - }, - "GFL93": { - "claims": [ - "Anycubic/Anycubic PLA+" - ], - "successor": "OFnmp6rt" - }, - "GFPLA": { - "claims": [ - "Anycubic/Anycubic PLA" - ], - "successor": "OFmjN2bc" - }, - "GFPLA High Speed": { - "claims": [ - "Anycubic/Anycubic PLA High Speed" - ], - "successor": "OFjVOWfJ" - }, - "GFPLA Matte": { - "claims": [ - "Anycubic/Anycubic PLA Matte" - ], - "successor": "OFIE3vOf" - }, - "GFPLA Silk": { - "claims": [ - "Anycubic/Anycubic PLA Silk" - ], - "successor": "OFFwJWea" - }, - "GFPLA+": { - "claims": [ - "Anycubic/Anycubic PLA+" - ], - "successor": "OFnmp6rt" - }, - "GPAB00": { - "claims": [ - "Elegoo/Generic PA" - ], - "successor": "OFg8ndtj" - }, - "GPCB00": { - "claims": [ - "Elegoo/Generic PC" - ], - "successor": "OFLakOUI" - }, - "GPETB00": { - "claims": [ - "Elegoo/Generic PET" - ], - "successor": "OF6MOPWx" - }, - "GPETGB00": { - "claims": [ - "Elegoo/Generic PETG", - "Elegoo/Generic PETG-CF" - ], - "successor": "OFYPdQJh" - }, - "GPLAB00": { - "claims": [ - "Elegoo/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "Generic ABS @E3NG v1.2S": { - "claims": [ - "RH3D/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "Generic ABS @MK4S": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ABS @MK4S 0.6": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ABS @MK4S 0.8": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ABS @MK4S HF0.4": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ABS @MK4S HF0.5": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ABS @MK4S HF0.6": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ABS @MK4S HF0.8": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Generic ASA @E3NG v1.2S": { - "claims": [ - "RH3D/Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "Generic FLEX @MK4S": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFbenYUK" - }, - "Generic FLEX @MK4S 0.6": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFbenYUK" - }, - "Generic FLEX @MK4S 0.8": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFbenYUK" - }, - "Generic PCCF @E3NG v1.2S": { - "claims": [ - "RH3D/Generic PCCF" - ], - "successor": "OFC5f4Ay" - }, - "Generic PETG @E3NG v1.2S": { - "claims": [ - "RH3D/Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "Generic PETG @MK4S": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PETG @MK4S 0.6": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PETG @MK4S 0.8": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PETG @MK4S HF0.4": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PETG @MK4S HF0.5": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PETG @MK4S HF0.6": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PETG @MK4S HF0.8": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Generic PLA @E3NG v1.2S": { - "claims": [ - "RH3D/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "Generic PLA @MK4S": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA @MK4S 0.6": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA @MK4S 0.8": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA @MK4S HF0.4": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA @MK4S HF0.5": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA @MK4S HF0.6": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA @MK4S HF0.8": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Generic PLA Silk @MK4S": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFHFZ1Te" - }, - "Generic PLA Silk @MK4S 0.6": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFHFZ1Te" - }, - "Generic PLA Silk @MK4S 0.8": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFHFZ1Te" - }, - "Generic TPU @E3NG v1.2S": { - "claims": [ - "RH3D/Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "IQM1": { - "claims": [ - "iQ/Fiberthree PACF Pro P1" - ], - "successor": "OFjUGUIK" - }, - "IQM1011": { - "claims": [ - "iQ/VXL90 TiQ2 P2" - ], - "successor": "OFOJPAAS" - }, - "IQM2": { - "claims": [ - "iQ/Material4Print ABS Natur P1" - ], - "successor": "OFJWKoYG" - }, - "IQM3": { - "claims": [ - "iQ/Polymaker PETG Polymax black P1" - ], - "successor": "OFaeIx7W" - }, - "IQM7": { - "claims": [ - "iQ/Fiberthree PACF Pro P2" - ], - "successor": "OF6kj4jI" - }, - "LHF_abs": { - "claims": [ - "LH/LHS ABS" - ], - "successor": "OFztQ7rO" - }, - "LHF_asa": { - "claims": [ - "LH/LHS ASA" - ], - "successor": "OFaP2SsH" - }, - "LHF_ftpuf": { - "claims": [ - "LH/LHS TPU Foamy 78A" - ], - "successor": "OFngcE81" - }, - "LHF_pccf": { - "claims": [ - "LH/LHS PC CF" - ], - "successor": "OFu0fjPJ" - }, - "LHF_pctg": { - "claims": [ - "LH/LHS PCTG" - ], - "successor": "OFBJAdLw" - }, - "LHF_petg": { - "claims": [ - "LH/LHS PETG" - ], - "successor": "OFRJ325z" - }, - "LHF_pla": { - "claims": [ - "LH/LHS PLA" - ], - "successor": "OFyoBZdm" - }, - "LHF_ptpu_pl": { - "claims": [ - "LH/LHS TPU" - ], - "successor": "OFIZMQwM" - }, - "NIT01": { - "claims": [ - "OrcaFilamentLibrary/NIT ABS" - ], - "successor": "OFhWc5Bv" - }, - "NIT02": { - "claims": [ - "OrcaFilamentLibrary/NIT PLA" - ], - "successor": "OFwRJ4g0" - }, - "NIT03": { - "claims": [ - "OrcaFilamentLibrary/NIT PETG" - ], - "successor": "OFJu1XwK" - }, - "OEABSB00": { - "claims": [ - "OrcaFilamentLibrary/Elegoo ABS" - ], - "successor": "OFxOHhZw" - }, - "OEASAB00": { - "claims": [ - "OrcaFilamentLibrary/Elegoo ASA" - ], - "successor": "OFobDOW5" - }, - "OEPAHTB0": { - "claims": [ - "OrcaFilamentLibrary/Elegoo PAHT-CF" - ], - "successor": "OF0h7ERL" - }, - "OEPCB00": { - "claims": [ - "OrcaFilamentLibrary/Elegoo PC" - ], - "successor": "OFUyDDNv" - }, - "OEPETGB0": { - "claims": [ - "OrcaFilamentLibrary/Elegoo PETG" - ], - "successor": "OFLcd093" - }, - "OEPLAB00": { - "claims": [ - "OrcaFilamentLibrary/Elegoo PLA" - ], - "successor": "OFvgE0Zh" - }, - "OETPUB00": { - "claims": [ - "OrcaFilamentLibrary/Elegoo TPU 95A" - ], - "successor": "OFmhJs5V" - }, - "OF016CJw": { - "claims": [ - "Sovol/Sovol SV08 ABS" - ], - "successor": "OFY9muEs" - }, - "OF0GIkJm": { - "claims": [ - "Tiertime/Tiertime Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OF0zx94L": { - "claims": [ - "Vzbot/Vzbot Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OF1Id239": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OF1OZujR": { - "claims": [ - "Flashforge/Flashforge Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OF1bQyX2": { - "claims": [ - "Flashforge/Flashforge PLA Matte" - ], - "successor": "OF6qAuyi" - }, - "OF1daEUW": { - "claims": [ - "Anker/Anker Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "OF1uR1t2": { - "claims": [ - "Tiertime/Tiertime Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OF2RjrWH": { - "claims": [ - "Flashforge/FusRock Generic PAHT-GF" - ], - "successor": "OFcVLpNA" - }, - "OF2bZtyg": { - "claims": [ - "Flashforge/Flashforge PLA-CF" - ], - "successor": "OF9cAwMK" - }, - "OF2mgDsI": { - "claims": [ - "Vzbot/Vzbot Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OF3P0BQA": { - "claims": [ - "Anker/Anker Generic ABS", - "Anker/Anker Generic ABS 0.2 nozzle", - "Anker/Anker Generic ABS 0.25 nozzle" - ], - "successor": "OFY9muEs" - }, - "OF3s929u": { - "claims": [ - "Tiertime/Tiertime Generic PP" - ], - "successor": "OF1UNk9P" - }, - "OF3wxlw9": { - "claims": [ - "CoLiDo/CoLiDo Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OF41jaTh": { - "claims": [ - "Flashforge/Flashforge TPU-90A" - ], - "successor": "OFwlZ7gH" - }, - "OF42ZDpN": { - "claims": [ - "Flashforge/FusRock PAHT" - ], - "successor": "OF7pjCsO" - }, - "OF48L671": { - "claims": [ - "Ratrig/RatRig Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OF4Mf5Gz": { - "claims": [ - "Creality/Creality Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OF4XAUnp": { - "claims": [ - "Vzbot/Vzbot Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OF4oCRKe": { - "claims": [ - "DeltaMaker/DeltaMaker Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OF4xhTuK": { - "claims": [ - "OrcaArena/OrcaArena Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OF4zzGwp": { - "claims": [ - "Flashforge/FusRock Generic PET-GF" - ], - "successor": "OFzwA5Z9" - }, - "OF51XQar": { - "claims": [ - "CONSTRUCT3D/C1 Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OF5E5KNA": { - "claims": [ - "SecKit/SecKit Generic PC" - ], - "successor": "OFLakOUI" - }, - "OF5NQFKI": { - "claims": [ - "Flashforge/Flashforge PLA Galaxy" - ], - "successor": "OFJJ0Ar3" - }, - "OF5Zk4e1": { - "claims": [ - "Anycubic/Anycubic Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OF5vzBhb": { - "claims": [ - "Chuanying/Chuanying Generic PLA", - "Chuanying/Chuanying PLA" - ], - "successor": "OFDSrzZ8" - }, - "OF5w29Kq": { - "claims": [ - "Flashforge/Flashforge HS PETG" - ], - "successor": "OFDxxTpW" - }, - "OF67EdL5": { - "claims": [ - "Sovol/Sovol SV06 ACE ABS" - ], - "successor": "OFY9muEs" - }, - "OF6Fpw1E": { - "claims": [ - "Comgrow/Comgrow Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OF6K467o": { - "claims": [ - "InfiMech/InfiMech Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OF6KELDx": { - "claims": [ - "Co Print/CoPrint Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OF6O67Xk": { - "claims": [ - "Lulzbot/Lulzbot 2.85mm ABS" - ], - "successor": "OFY9muEs" - }, - "OF6VgL2U": { - "claims": [ - "Phrozen/Phrozen PLA" - ], - "successor": "OFDSrzZ8" - }, - "OF6WddJ1": { - "claims": [ - "Flashforge/FusRock S-PAHT" - ], - "successor": "OFCn83wF" - }, - "OF6ooviO": { - "claims": [ - "FLSun/FLSun S1 TPU" - ], - "successor": "OFgbpcy9" - }, - "OF6rMr3U": { - "claims": [ - "Flashforge/FusRock Generic S-Multi" - ], - "successor": "OF0AquDh" - }, - "OF6tKa1Z": { - "claims": [ - "Anker/Anker Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OF6ueBwd": { - "claims": [ - "Flashforge/Flashforge PLA Pro" - ], - "successor": "OFm4SivL" - }, - "OF6vKLaU": { - "claims": [ - "Artillery/Artillery Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OF6xLvYg": { - "claims": [ - "InfiMech/InfiMech Generic PC" - ], - "successor": "OFLakOUI" - }, - "OF6zAVFj": { - "claims": [ - "Sovol/Sovol Zero TPU" - ], - "successor": "OFgbpcy9" - }, - "OF7VJMYM": { - "claims": [ - "Sovol/Sovol Zero PLA Basic" - ], - "successor": "OFDSrzZ8" - }, - "OF7g2vYQ": { - "claims": [ - "Co Print/CoPrint Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OF7uE5KB": { - "claims": [ - "Anker/Anker Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OF8CXrvm": { - "claims": [ - "SecKit/SecKit Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OF8bLDu7": { - "claims": [ - "Blocks/Blocks Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OF8eQjjt": { - "claims": [ - "Lulzbot/Lulzbot 2.85mm PETG" - ], - "successor": "OFYPdQJh" - }, - "OF97ru74": { - "claims": [ - "Prusa/Prusa Generic PC" - ], - "successor": "OFLakOUI" - }, - "OF995YXg": { - "claims": [ - "Flashforge/Flashforge TPU-64D" - ], - "successor": "OF8GtyKJ" - }, - "OF9fOPpd": { - "claims": [ - "Vzbot/Vzbot Generic PC" - ], - "successor": "OFLakOUI" - }, - "OF9z5LZ9": { - "claims": [ - "FLSun/FLSun S1 PETG" - ], - "successor": "OFYPdQJh" - }, - "OFACSw9O": { - "claims": [ - "Creality/Creality Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFAf9pJF": { - "claims": [ - "Flashforge/Flashforge TPU 95A" - ], - "successor": "OFSqIcJC" - }, - "OFB09fpe": { - "claims": [ - "Ratrig/RatRig Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFB0a3hT": { - "claims": [ - "Anker/Anker Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFB1Tv1E": { - "claims": [ - "Anycubic/Anycubic Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFB8wSps": { - "claims": [ - "Flashforge/FusRock PET-CF" - ], - "successor": "OFDu1qE7" - }, - "OFB9dx27": { - "claims": [ - "Blocks/Blocks Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFBC32Wb": { - "claims": [ - "Flashforge/FusRock PET" - ], - "successor": "OFKTGKJU" - }, - "OFBIadjM": { - "claims": [ - "Chuanying/Chuanying ASA", - "Chuanying/Chuanying Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFBK6zAi": { - "claims": [ - "Flashforge/Flashforge PETG-CF" - ], - "successor": "OFuFEtKX" - }, - "OFBpcB0a": { - "claims": [ - "TwoTrees/TwoTrees Generic HS PLA" - ], - "successor": "OFvxghTE" - }, - "OFBtlI8M": { - "claims": [ - "FlyingBear/FlyingBear Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFC20UMj": { - "claims": [ - "Flashforge/Flashforge Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFCEA7Nt": { - "claims": [ - "Sovol/Sovol SV06 Plus ACE ABS" - ], - "successor": "OFY9muEs" - }, - "OFCVUEaV": { - "claims": [ - "Tiertime/Tiertime Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFCaJHAA": { - "claims": [ - "Flashforge/FusRock Generic S-PAHT" - ], - "successor": "OFCn83wF" - }, - "OFCcsYtf": { - "claims": [ - "FLSun/FLSun Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFCu3jES": { - "claims": [], - "successor": null - }, - "OFD2JNS1": { - "claims": [ - "Dremel/Dremel Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFD6kN7F": { - "claims": [ - "OrcaArena/OrcaArena Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFDeTsJw": { - "claims": [ - "Chuanying/Chuanying Generic HIPS" - ], - "successor": "OFsFon5l" - }, - "OFEE1Dld": { - "claims": [ - "Artillery/Artillery Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFEMfQsX": { - "claims": [ - "Flashforge/Flashforge PETG Basic" - ], - "successor": "OFYYeMyQ" - }, - "OFENzGSm": { - "claims": [ - "FLSun/FLSun T1 TPU" - ], - "successor": "OFgbpcy9" - }, - "OFETFWx7": { - "claims": [ - "SecKit/SecKit Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFEZNNw1": { - "claims": [ - "Ratrig/RatRig Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFF73Bvp": { - "claims": [ - "SecKit/SecKit Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFFC0VqZ": { - "claims": [ - "Ratrig/RatRig BigNozzle PCTG" - ], - "successor": "OFYfAcLY" - }, - "OFFZ0BBL": { - "claims": [ - "FlyingBear/FlyingBear Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFFxZ7hZ": { - "claims": [ - "Chuanying/Chuanying Generic PLA-CF10" - ], - "successor": "OFX2zcrM" - }, - "OFGDaEHO": { - "claims": [ - "Tiertime/Tiertime Generic BVOH" - ], - "successor": "OFo2UF2C" - }, - "OFGGyAgJ": { - "claims": [ - "Vzbot/Vzbot Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFGKLoUm": { - "claims": [ - "Anycubic/Anycubic Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFGLeiNL": { - "claims": [], - "successor": null - }, - "OFGPJxnt": { - "claims": [ - "Anker/Anker Generic PC", - "Anker/Anker Generic PC 0.2 nozzle", - "Anker/Anker Generic PC 0.25 nozzle" - ], - "successor": "OFLakOUI" - }, - "OFGzUNbm": { - "claims": [ - "Tiertime/Tiertime Generic PPS-CF" - ], - "successor": "OF6rdQ6M" - }, - "OFH5UH6P": { - "claims": [ - "Flashforge/Flashforge ABS-GF" - ], - "successor": "OFojbdhR" - }, - "OFH6Pjxe": { - "claims": [ - "Flashforge/Flashforge PLA Matte" - ], - "successor": "OF6qAuyi" - }, - "OFHFZ1Te": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFHQUAoZ": { - "claims": [ - "FLSun/FLSun Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFHTVfaU": { - "claims": [ - "Sovol/Sovol Zero PETG" - ], - "successor": "OFYPdQJh" - }, - "OFHUnLY2": { - "claims": [ - "Vzbot/Vzbot Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFHa3RZT": { - "claims": [ - "Tiertime/Tiertime Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFI80Hct": { - "claims": [ - "Anycubic/Anycubic Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFIK0xFa": { - "claims": [ - "FLSun/FLSun S1 ASA" - ], - "successor": "OFLPAxz3" - }, - "OFIYTy8S": { - "claims": [ - "Anker/Anker Generic PLA+", - "Anker/Anker Generic PLA+ 0.2 nozzle", - "Anker/Anker Generic PLA+ 0.25 nozzle" - ], - "successor": "OFEWYR89" - }, - "OFIda816": { - "claims": [ - "Vzbot/Vzbot Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFIeCuXj": { - "claims": [ - "Peopoly/Peopoly Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFIieIka": { - "claims": [ - "Flashforge/Polymaker CoPA" - ], - "successor": "OFQyyO0l" - }, - "OFIxitiU": { - "claims": [ - "Flashforge/Flashforge PLA Basic" - ], - "successor": "OFJ3F2jm" - }, - "OFIy3kLw": { - "claims": [ - "Prusa/Prusa Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFJ80y1e": { - "claims": [ - "FLSun/FLSun T1 PETG" - ], - "successor": "OFYPdQJh" - }, - "OFJJpaLS": { - "claims": [ - "FLSun/FLSun Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFJSksUs": { - "claims": [ - "FLSun/FLSun Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFJVFpJz": { - "claims": [ - "Sovol/Sovol Zero PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFJfv07m": { - "claims": [ - "Sovol/Sovol SV06 ACE TPU" - ], - "successor": "OFgbpcy9" - }, - "OFK01afJ": { - "claims": [ - "Qidi/Qidi Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFK4LdYI": { - "claims": [ - "Prusa/Prusa Generic ASA HF" - ], - "successor": "OFzRtFM9" - }, - "OFK74HJD": { - "claims": [ - "Flashforge/Flashforge PLA Pro" - ], - "successor": "OFm4SivL" - }, - "OFKNAhls": { - "claims": [ - "Qidi/Qidi Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFKo87c6": { - "claims": [ - "Snapmaker/Snapmaker TPU 95A" - ], - "successor": "OFvmwTZE" - }, - "OFKpQTJI": { - "claims": [ - "Flashforge/Flashforge TPU 65D" - ], - "successor": "OFSDfduA" - }, - "OFKuTr12": { - "claims": [ - "Tiertime/Tiertime Generic PE" - ], - "successor": "OFPklMI1" - }, - "OFKyCgaK": { - "claims": [ - "Anycubic/Anycubic Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFLBUkwC": { - "claims": [ - "Vzbot/Vzbot Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFLDSqqi": { - "claims": [ - "Flashforge/Flashforge Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFLSBS99": { - "claims": [ - "OrcaFilamentLibrary/Generic SBS" - ], - "successor": "OFhQfUQY" - }, - "OFLYDIb8": { - "claims": [ - "Flashforge/Flashforge PLA Silk" - ], - "successor": "OFgjgN35" - }, - "OFLrdHse": { - "claims": [ - "Flashforge/Flashforge Generic HS PLA" - ], - "successor": "OFvxghTE" - }, - "OFLuczRG": { - "claims": [ - "Creality/Creality Generic ASA-CF" - ], - "successor": "OF53eLVD" - }, - "OFM87w9T": { - "claims": [ - "Tiertime/Tiertime Generic SBS" - ], - "successor": "OFhQfUQY" - }, - "OFMEbuBE": { - "claims": [ - "Blocks/Blocks Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFMIma8C": { - "claims": [ - "Flashforge/Flashforge PLA Luminous" - ], - "successor": "OFdqkWtz" - }, - "OFMIurFh": { - "claims": [ - "Creality/Creality HF Generic Speed PLA" - ], - "successor": "OFXmv5p0" - }, - "OFMTBcnz": { - "claims": [ - "Tiertime/Tiertime Generic PP-CF" - ], - "successor": "OFXkm8q1" - }, - "OFMZAoqa": { - "claims": [ - "Tiertime/Tiertime Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFMgJQCZ": { - "claims": [ - "Anker/Anker Generic PA", - "Anker/Anker Generic PA 0.2 nozzle", - "Anker/Anker Generic PA 0.25 nozzle" - ], - "successor": "OFg8ndtj" - }, - "OFMoLF46": { - "claims": [ - "FLSun/FLSun Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFMqYusc": { - "claims": [ - "Creality/Creality Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFMyLYV8": { - "claims": [ - "Flashforge/Flashforge PETG Transparent" - ], - "successor": "OFwjMiKL" - }, - "OFN7aNHe": { - "claims": [ - "Tiertime/Tiertime Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFNLXV8X": { - "claims": [ - "Tiertime/Tiertime Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFNOxcYl": { - "claims": [ - "Ratrig/RatRig Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFNW4ZTC": { - "claims": [ - "Creality/Creality Generic PLA Wood" - ], - "successor": "OFIKZxPF" - }, - "OFNWcJsM": { - "claims": [ - "Comgrow/Comgrow T300 PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFNWnx2c": { - "claims": [ - "Flashforge/Flashforge TPU-95A" - ], - "successor": "OFx0WlzS" - }, - "OFNX8GI1": { - "claims": [ - "Sovol/Sovol Zero ABS" - ], - "successor": "OFY9muEs" - }, - "OFNYewRS": { - "claims": [ - "Blocks/Blocks Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFNZYgLA": { - "claims": [ - "Qidi/Qidi Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFNZjrLo": { - "claims": [ - "Sovol/Sovol SV06 Plus ACE PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFNcdCrm": { - "claims": [ - "Anycubic/Anycubic Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFNiSLfL": { - "claims": [ - "OrcaFilamentLibrary/Generic PP-GF" - ], - "successor": "OFsijjtH" - }, - "OFNn21yM": { - "claims": [ - "Vzbot/Vzbot Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFOKYhi6": { - "claims": [ - "Prusa/Prusa Generic PETG HF" - ], - "successor": "OF7lOgYF" - }, - "OFPQfNAf": { - "claims": [ - "Flashforge/Flashforge PETG Pro" - ], - "successor": "OFXiArjA" - }, - "OFPdhDlK": { - "claims": [ - "Anycubic/Anycubic Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFPjOUtz": { - "claims": [ - "Flashforge/FusRock Generic PAHT-CF" - ], - "successor": "OFKW5hEW" - }, - "OFPryWt5": { - "claims": [ - "Lulzbot/Lulzbot 2.85mm PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFPy0Ogq": { - "claims": [ - "Qidi/Qidi Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFQ7I5vC": { - "claims": [ - "Prusa/Prusa Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFQ9qnkd": { - "claims": [ - "FLSun/FLSun T1 PLA High Speed" - ], - "successor": "OFmpMwxS" - }, - "OFQC8Vna": { - "claims": [ - "Flashforge/Flashforge Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "OFQTrLXB": { - "claims": [ - "Qidi/Qidi Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFQZeyZv": { - "claims": [ - "Flashforge/Flashforge PLA Buint Ti" - ], - "successor": "OF8O09RG" - }, - "OFQbqeN7": { - "claims": [ - "Qidi/Qidi Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFQiq4fb": { - "claims": [ - "Flashforge/Flashforge PVA" - ], - "successor": "OFaaddbQ" - }, - "OFQswlnF": { - "claims": [ - "Tiertime/Tiertime Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFRDLNM9": { - "claims": [ - "Peopoly/Peopoly Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFRPEs5o": { - "claims": [ - "Flashforge/Flashforge Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFRi7v0b": { - "claims": [ - "Sovol/Sovol SV08 PETG" - ], - "successor": "OFYPdQJh" - }, - "OFRiyes1": { - "claims": [ - "Anker/Anker Generic ASA", - "Anker/Anker Generic ASA 0.2 nozzle", - "Anker/Anker Generic ASA 0.25 nozzle" - ], - "successor": "OFLPAxz3" - }, - "OFRujL0v": { - "claims": [ - "Flashforge/FusRock Generic PET-CF" - ], - "successor": "OFDu1qE7" - }, - "OFRusgEn": { - "claims": [ - "Chuanying/Chuanying Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFRzsaDK": { - "claims": [ - "FlyingBear/FlyingBear Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFS8JF3S": { - "claims": [ - "Qidi/Qidi Generic PLA High Speed" - ], - "successor": "OFmpMwxS" - }, - "OFSDFYs4": { - "claims": [ - "Creality/Creality Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFSGbf2I": { - "claims": [ - "Anycubic/Anycubic Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFSPdsuW": { - "claims": [ - "Tiertime/Tiertime Generic PPA-CF" - ], - "successor": "OF8tPByX" - }, - "OFSStjay": { - "claims": [ - "FLSun/FLSun S1 PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFSTCno1": { - "claims": [ - "re3D/re3D Greengate rPETG" - ], - "successor": "OFmCOW2Y" - }, - "OFShw1X8": { - "claims": [ - "Flashforge/Flashforge ABS Basic" - ], - "successor": "OFibWuBd" - }, - "OFTKFAZ4": { - "claims": [ - "Tiertime/Tiertime Generic PCTG" - ], - "successor": "OFu1evlr" - }, - "OFTvvox9": { - "claims": [ - "Sovol/Sovol SV06 ACE PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFU0n0zW": { - "claims": [ - "FLSun/FLSun T1 PLA Generic" - ], - "successor": "OFDSrzZ8" - }, - "OFU3zwPc": { - "claims": [ - "Prusa/Prusa Generic TPU HF" - ], - "successor": "OFnT7Qpb" - }, - "OFU5N9jI": { - "claims": [ - "OrcaArena/OrcaArena Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFU6fjbi": { - "claims": [ - "Co Print/CoPrint Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFUCpLaL": { - "claims": [ - "Flashforge/FlashForge PPS" - ], - "successor": "OFlgMgNM" - }, - "OFUScM0D": { - "claims": [ - "Flashforge/FusRock S-Multi" - ], - "successor": "OF0AquDh" - }, - "OFUWRqwb": { - "claims": [ - "Creality/Creality Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFUaVAdj": { - "claims": [ - "Tiertime/Tiertime Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFUrStd6": { - "claims": [ - "Ratrig/RatRig Generic PCTG" - ], - "successor": "OFu1evlr" - }, - "OFUtGagr": { - "claims": [ - "Artillery/Artillery Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFV0KrAG": { - "claims": [ - "FLSun/FLSun T1 ABS" - ], - "successor": "OFY9muEs" - }, - "OFVKADz5": { - "claims": [ - "Comgrow/Comgrow Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFVqZ2H0": { - "claims": [ - "FLSun/FLSun Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFW9uIkE": { - "claims": [ - "Tiertime/Tiertime Generic HIPS" - ], - "successor": "OFsFon5l" - }, - "OFWIr7ZE": { - "claims": [ - "CoLiDo/CoLiDo Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFWY6vBh": { - "claims": [ - "Anycubic/Anycubic Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFWadebJ": { - "claims": [ - "Tiertime/Tiertime Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "OFWbbuRY": { - "claims": [ - "InfiMech/InfiMech Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFWnSvGu": { - "claims": [ - "Flashforge/Flashforge ASA-GF" - ], - "successor": "OFrZDhVt" - }, - "OFWof1Ap": { - "claims": [ - "FLSun/FLSun Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFWukXja": { - "claims": [ - "Qidi/Qidi Generic TPU 95A" - ], - "successor": "OFjiXpyf" - }, - "OFWzr7m0": { - "claims": [ - "Anycubic/Anycubic Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFXXUHSG": { - "claims": [ - "Chuanying/Chuanying Generic PETG-CF10" - ], - "successor": "OFO5djrM" - }, - "OFXhZ3uY": { - "claims": [ - "OrcaArena/OrcaArena Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFXuwtnq": { - "claims": [ - "Co Print/CoPrint Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFXyyfX6": { - "claims": [ - "Anker/Anker Generic PLA Silk", - "Anker/Anker Generic PLA Silk 0.2 nozzle", - "Anker/Anker Generic PLA Silk 0.25 nozzle" - ], - "successor": "OFesA6rF" - }, - "OFYHzKyS": { - "claims": [ - "Vzbot/Vzbot Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFYPdJ7t": { - "claims": [ - "Creality/Creality Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFYfAcLY": { - "claims": [ - "Ratrig/Generic PCTG BigNozzle" - ], - "successor": "OFXW4CDh" - }, - "OFYl0c8v": { - "claims": [ - "Flashforge/Flashforge Generic HIPS" - ], - "successor": "OFsFon5l" - }, - "OFYlZMmy": { - "claims": [ - "Chuanying/Chuanying Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFYlthXI": { - "claims": [ - "Flashforge/Flashforge Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFYtS9zg": { - "claims": [ - "Flashforge/FusRock PAHT-CF" - ], - "successor": "OFKW5hEW" - }, - "OFYvSO1W": { - "claims": [ - "Flashforge/Flashforge Generic PLA-Silk" - ], - "successor": "OFi6PfUM" - }, - "OFZ1hANN": { - "claims": [ - "Flashforge/FusRock Generic NexPA-CF25" - ], - "successor": "OFjt8dDO" - }, - "OFZ4fTO8": { - "claims": [ - "CONSTRUCT3D/C1 Generic High Flow PETG" - ], - "successor": "OFNjku08" - }, - "OFZHgTVI": { - "claims": [ - "Flashforge/Flashforge Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFZPlCck": { - "claims": [ - "Chuanying/Chuanying Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFZsChIG": { - "claims": [ - "Qidi/Qidi Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFa9X7yl": { - "claims": [ - "Flashforge/FusRock S-PAHT" - ], - "successor": "OFCn83wF" - }, - "OFaBwRvT": { - "claims": [ - "Flashforge/Flashforge ASA Basic" - ], - "successor": "OFFFg2Pf" - }, - "OFaFNP2U": { - "claims": [ - "Sovol/Sovol SV07 PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFaU2JxD": { - "claims": [ - "Dremel/Dremel Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFahE7HP": { - "claims": [ - "Blocks/Blocks Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFaz8VoR": { - "claims": [ - "Flashforge/Flashforge HIPS" - ], - "successor": "OFvaLVML" - }, - "OFbeeFkl": { - "claims": [ - "OrcaArena/OrcaArena Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFbenYUK": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFbjtPQy": { - "claims": [ - "FLSun/FLSun T1 ASA" - ], - "successor": "OFLPAxz3" - }, - "OFbt04e0": { - "claims": [ - "DeltaMaker/DeltaMaker Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFc0xiCz": { - "claims": [ - "InfiMech/InfiMech Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFcCzEkS": { - "claims": [ - "Prusa/Prusa Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFcJ7AKd": { - "claims": [ - "Sovol/Sovol SV08 PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFcdqaWI": { - "claims": [ - "Sovol/Sovol Zero PC" - ], - "successor": "OFLakOUI" - }, - "OFctbSkL": { - "claims": [ - "Blocks/Blocks Generic ASA-CF" - ], - "successor": "OF53eLVD" - }, - "OFd10YeG": { - "claims": [ - "Ratrig/RatRig Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFdEIwxy": { - "claims": [ - "Flashforge/Flashforge Generic PLA-CF10" - ], - "successor": "OFX2zcrM" - }, - "OFdHsxB4": { - "claims": [ - "TwoTrees/TwoTrees Generic 95A TPU" - ], - "successor": "OF6MMR8P" - }, - "OFdLdGgJ": { - "claims": [ - "OrcaArena/OrcaArena Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFdOWhCa": { - "claims": [ - "Ratrig/RatRig Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFdXgp2K": { - "claims": [ - "Chuanying/Chuanying Generic HS PLA", - "Chuanying/Chuanying HS PLA" - ], - "successor": "OFvxghTE" - }, - "OFdlGycb": { - "claims": [ - "CoLiDo/CoLiDo Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFe6sfXS": { - "claims": [ - "Flashforge/FlashForge PC" - ], - "successor": "OFWXCbVF" - }, - "OFe7Sx5B": { - "claims": [ - "Sovol/Sovol SV06 ACE PETG" - ], - "successor": "OFYPdQJh" - }, - "OFe8UJXH": { - "claims": [ - "Creality/Creality Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFeDfaCh": { - "claims": [ - "CoLiDo/CoLiDo Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFeJjoKF": { - "claims": [ - "FLSun/FLSun Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFeK2vkS": { - "claims": [ - "Chuanying/Chuanying Generic PLA-Silk", - "Chuanying/Chuanying PLA-SILK" - ], - "successor": "OFi6PfUM" - }, - "OFec7Qiw": { - "claims": [ - "Flashforge/Flashforge PLA Sparkle" - ], - "successor": "OFV5c8hG" - }, - "OFetENFF": { - "claims": [ - "OrcaArena/OrcaArena Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFfZ0lTS": { - "claims": [ - "Qidi/Qidi Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "OFfegVpA": { - "claims": [ - "Flashforge/Polymaker Generic S1" - ], - "successor": "OFaDsTE0" - }, - "OFfxFQf9": { - "claims": [ - "Chuanying/Chuanying Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFfznI0Q": { - "claims": [ - "Prusa/Prusa Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFgSEwOZ": { - "claims": [ - "OrcaArena/OrcaArena Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFgSPtDn": { - "claims": [ - "MagicMaker/MM Generic PEEK" - ], - "successor": "OFz5oHgf" - }, - "OFgVJhVt": { - "claims": [ - "Tiertime/Tiertime Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFgcryz4": { - "claims": [ - "Ratrig/RatRig BigNozzle ASA" - ], - "successor": "OF4GM5qX" - }, - "OFgf4gZa": { - "claims": [ - "Flashforge/Flashforge PA" - ], - "successor": "OFEvEfw5" - }, - "OFgvOmM2": { - "claims": [ - "Anker/Anker Generic PETG", - "Anker/Anker Generic PETG 0.2 nozzle", - "Anker/Anker Generic PETG 0.25 nozzle" - ], - "successor": "OFYPdQJh" - }, - "OFhcrFw9": { - "claims": [ - "Creality/Creality Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFi7Bf0F": { - "claims": [ - "Flashforge/Flashforge ABS-CF" - ], - "successor": "OFYTIMbj" - }, - "OFiCHnso": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFiUSDih": { - "claims": [ - "Blocks/Blocks Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFifxPW1": { - "claims": [ - "DeltaMaker/DeltaMaker Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFiorRtJ": { - "claims": [ - "Blocks/Blocks Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFiuJSmy": { - "claims": [ - "Ratrig/RatRig BigNozzle PLA" - ], - "successor": "OFqFRiLb" - }, - "OFixgF4N": { - "claims": [ - "Flashforge/FusRock S-Multi" - ], - "successor": "OF0AquDh" - }, - "OFjoIVZ6": { - "claims": [ - "FlyingBear/FlyingBear Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFkUaDnU": { - "claims": [ - "Tiertime/Tiertime Generic PE-CF" - ], - "successor": "OFd0Fv0k" - }, - "OFkWaWRo": { - "claims": [ - "Sovol/Sovol SV06 Plus ACE TPU" - ], - "successor": "OFgbpcy9" - }, - "OFkYkQGO": { - "claims": [ - "Tiertime/Tiertime Generic PP-GF" - ], - "successor": "OFsijjtH" - }, - "OFkwIWwc": { - "claims": [ - "Flashforge/Flashforge PLA Color Change" - ], - "successor": "OFMkAW6r" - }, - "OFl2SXmv": { - "claims": [ - "Prusa/Prusa Generic FLEX" - ], - "successor": "OFbPuPKY" - }, - "OFl4ItvQ": { - "claims": [ - "Prusa/Prusa Generic PLA HF" - ], - "successor": "OFZr862X" - }, - "OFl7nOAf": { - "claims": [ - "Sovol/Sovol SV06 Plus ACE PETG" - ], - "successor": "OFYPdQJh" - }, - "OFlLmUeV": { - "claims": [ - "Ginger Additive/Ginger Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFlNWVr5": { - "claims": [ - "Flashforge/Flashforge Generic PETG-CF10" - ], - "successor": "OFO5djrM" - }, - "OFlXJxX1": { - "claims": [ - "Chuanying/Chuanying PETG" - ], - "successor": "OFYPdQJh" - }, - "OFlx52rn": { - "claims": [ - "Ratrig/RatRig Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFmQkb3c": { - "claims": [ - "Flashforge/Flashforge HS PLA Burnt Ti" - ], - "successor": "OF8O09RG" - }, - "OFma52eS": { - "claims": [ - "Blocks/Blocks Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFmexHIS": { - "claims": [ - "Ratrig/RatRig BigNozzle PETG" - ], - "successor": "OFvD0Qqt" - }, - "OFmhWhEf": { - "claims": [ - "Creality/Creality HF Generic PLA" - ], - "successor": "OFZr862X" - }, - "OFmnJNqY": { - "claims": [ - "OrcaArena/OrcaArena Generic PA" - ], - "successor": "OFg8ndtj" - }, - "OFmzftED": { - "claims": [ - "OrcaArena/OrcaArena Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFnMv3W5": { - "claims": [ - "Flashforge/Flashforge HS PLA" - ], - "successor": "OFIK2H3G" - }, - "OFndmfJ1": { - "claims": [ - "Anker/Anker Generic PLA", - "Anker/Anker Generic PLA 0.2 nozzle", - "Anker/Anker Generic PLA 0.25 nozzle" - ], - "successor": "OFDSrzZ8" - }, - "OFnfBvn5": { - "claims": [ - "Artillery/Artillery Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFnh79eM": { - "claims": [ - "SecKit/SecKit Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFoDgVnY": { - "claims": [ - "FLSun/FLSun S1 PLA Generic" - ], - "successor": "OFDSrzZ8" - }, - "OFoJcr46": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFoT8F7Y": { - "claims": [ - "Prusa/Prusa Generic PVA HF" - ], - "successor": "OFDwBwO5" - }, - "OFoVZg32": { - "claims": [ - "SecKit/SecKit Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OFodPDxO": { - "claims": [ - "Tiertime/Tiertime Generic PLA High Speed" - ], - "successor": "OFmpMwxS" - }, - "OFogbnCb": { - "claims": [ - "Flashforge/FlashForge PPS-CF" - ], - "successor": "OFTSXxzE" - }, - "OFoqrHTw": { - "claims": [ - "FLSun/FLSun S1 PLA High Speed" - ], - "successor": "OFmpMwxS" - }, - "OFp8gtPH": { - "claims": [ - "Creality/Creality Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFpAaY6w": { - "claims": [ - "Creality/Creality Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFpBV2Ma": { - "claims": [ - "Qidi/Tinmorry PETG-ECO" - ], - "successor": "OFYE7YZw" - }, - "OFpSYJLI": { - "claims": [ - "Creality/Creality Generic PLA Matte" - ], - "successor": "OFmY0l6t" - }, - "OFptvi3F": { - "claims": [ - "Tiertime/Tiertime Generic PPA-GF" - ], - "successor": "OF8Tlg47" - }, - "OFq5QANf": { - "claims": [ - "Prusa/Prusa Generic PC HF" - ], - "successor": "OFwwAQnA" - }, - "OFqKmLJe": { - "claims": [ - "Creality/Creality Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "OFqOk9Ww": { - "claims": [ - "Peopoly/Peopoly Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFqS8fzK": { - "claims": [ - "SecKit/SecKit Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFrO2zyn": { - "claims": [ - "Creality/Creality Generic PLA High Speed" - ], - "successor": "OFmpMwxS" - }, - "OFrQqCwL": { - "claims": [ - "InfiMech/InfiMech Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFrgGex8": { - "claims": [ - "Blocks/Blocks Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFrh4bBH": { - "claims": [ - "SecKit/SecKit Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFrnJJEa": { - "claims": [ - "Tiertime/Tiertime Generic EVA" - ], - "successor": "OFCD8qiU" - }, - "OFrnkURQ": { - "claims": [ - "Flashforge/Flashforge PLA Metal" - ], - "successor": "OFAebKg8" - }, - "OFrrMLDw": { - "claims": [ - "Flashforge/Flashforge Generic PLA-SILK" - ], - "successor": "OFkf1HHw" - }, - "OFs1sNLz": { - "claims": [ - "Chuanying/Chuanying ABS" - ], - "successor": "OFY9muEs" - }, - "OFs2ReMr": { - "claims": [ - "InfiMech/InfiMech Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFsCbVbN": { - "claims": [ - "Anker/Anker Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFsF96Gt": { - "claims": [ - "FLSun/FLSun Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFsGqI4y": { - "claims": [ - "Ginger Additive/Ginger Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFsNJjpc": { - "claims": [ - "Ratrig/RatRig BigNozzle TPU" - ], - "successor": "OF5tX6va" - }, - "OFsXOMoX": { - "claims": [ - "Qidi/Qidi Generic PLA+" - ], - "successor": "OFEWYR89" - }, - "OFsoQ12i": { - "claims": [ - "Comgrow/Comgrow Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFsxRSwr": { - "claims": [ - "Ratrig/RatRig Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFsxlLdo": { - "claims": [ - "Qidi/Qidi Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OFtP1rqJ": { - "claims": [ - "FLSun/FLSun Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFtTHNTj": { - "claims": [ - "Flashforge/Flashforge Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFtplX8o": { - "claims": [ - "Ratrig/RatRig BigNozzle ABS" - ], - "successor": "OFnyHfcv" - }, - "OFtsVp4v": { - "claims": [ - "SecKit/SecKit Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OFu79SUH": { - "claims": [ - "Qidi/Qidi Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFuFqYyG": { - "claims": [ - "Flashforge/Flashforge PAHT-CF" - ], - "successor": "OFKW5hEW" - }, - "OFuGRMNV": { - "claims": [ - "Flashforge/Flashforge PA-CF" - ], - "successor": "OFGxuI0n" - }, - "OFuSwKgm": { - "claims": [ - "Tiertime/Tiertime Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OFuUJyRx": { - "claims": [ - "FLSun/FLSun T1 PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFupq0tY": { - "claims": [ - "Blocks/Blocks Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFv4VZCd": { - "claims": [ - "Qidi/Qidi Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFvGc67v": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFvPomQf": { - "claims": [ - "Tiertime/Tiertime Generic PPS" - ], - "successor": "OFq9svOz" - }, - "OFvVxvzZ": { - "claims": [ - "Flashforge/Flashforge PLA Metal" - ], - "successor": "OFAebKg8" - }, - "OFwMFLAm": { - "claims": [ - "Artillery/Artillery Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFwpkvqf": { - "claims": [ - "OrcaArena/OrcaArena Generic PETG-CF" - ], - "successor": "OFoYSJKi" - }, - "OFwxpmHM": { - "claims": [ - "Flashforge/Polymaker Generic CoPA" - ], - "successor": "OFQyyO0l" - }, - "OFx0ZzFV": { - "claims": [ - "OrcaArena/OrcaArena Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OFxMGIPt": { - "claims": [ - "Flashforge/Flashforge PPS" - ], - "successor": "OFlgMgNM" - }, - "OFxTMg1d": { - "claims": [ - "FlyingBear/FlyingBear Generic PC" - ], - "successor": "OFLakOUI" - }, - "OFxg7n2G": { - "claims": [ - "Ratrig/RatRig Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFxvKuYW": { - "claims": [ - "Tiertime/Tiertime Generic PHA" - ], - "successor": "OF74KeQR" - }, - "OFy4ajVZ": { - "claims": [ - "Qidi/Qidi Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OFyCToOV": { - "claims": [ - "Sovol/Sovol SV08 TPU" - ], - "successor": "OFgbpcy9" - }, - "OFyFRojy": { - "claims": [ - "Ratrig/RatRig Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OFyK5Lt9": { - "claims": [ - "Artillery/Artillery Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFyeSq8A": { - "claims": [ - "Prusa/Prusa Generic ABS HF" - ], - "successor": "OF55jHAJ" - }, - "OFyySVby": { - "claims": [ - "SecKit/SecKit Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OFz3q5Xz": { - "claims": [ - "CONSTRUCT3D/C1 Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFzXURfo": { - "claims": [ - "FlyingBear/FlyingBear Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "OFzzmK2Q": { - "claims": [ - "FLSun/FLSun S1 ABS" - ], - "successor": "OFY9muEs" - }, - "OGFA00": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Basic" - ], - "successor": "OFoiVqVM" - }, - "OGFA01": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Matte" - ], - "successor": "OFXIbw5D" - }, - "OGFA02": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Metal" - ], - "successor": "OFrKLeE3" - }, - "OGFA03": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Impact" - ], - "successor": "OFTGHyNC" - }, - "OGFA05": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Silk" - ], - "successor": "OFEGNJD4" - }, - "OGFA06": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Silk+" - ], - "successor": "OFUanySo" - }, - "OGFA07": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Marble" - ], - "successor": "OF9OlTWH" - }, - "OGFA08": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Sparkle" - ], - "successor": "OFDxfPgH" - }, - "OGFA09": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Tough" - ], - "successor": "OFaQMgRH" - }, - "OGFA11": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Aero" - ], - "successor": "OFBSW57R" - }, - "OGFA12": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Glow" - ], - "successor": "OFEkPBwx" - }, - "OGFA13": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Dynamic" - ], - "successor": "OFDGigEI" - }, - "OGFA15": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Galaxy" - ], - "successor": "OFQ3e56w" - }, - "OGFA16": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA Wood" - ], - "successor": "OFMjtqTC" - }, - "OGFA50": { - "claims": [ - "OrcaFilamentLibrary/Bambu PLA-CF" - ], - "successor": "OFEswT5W" - }, - "OGFB00": { - "claims": [ - "OrcaFilamentLibrary/Bambu ABS" - ], - "successor": "OFhuaUQB" - }, - "OGFB01": { - "claims": [ - "OrcaFilamentLibrary/Bambu ASA" - ], - "successor": "OFfBpSRI" - }, - "OGFB02": { - "claims": [ - "OrcaFilamentLibrary/Bambu ASA-Aero" - ], - "successor": "OFXzQ4yL" - }, - "OGFB50": { - "claims": [ - "OrcaFilamentLibrary/Bambu ABS-GF" - ], - "successor": "OFknl9Iz" - }, - "OGFB51": { - "claims": [ - "OrcaFilamentLibrary/Bambu ASA-CF" - ], - "successor": "OF0wBGNx" - }, - "OGFB60": { - "claims": [ - "OrcaFilamentLibrary/PolyLite ABS" - ], - "successor": "OFOvv91M" - }, - "OGFB61": { - "claims": [ - "OrcaFilamentLibrary/PolyLite ASA" - ], - "successor": "OFvrXuV7" - }, - "OGFB98": { - "claims": [ - "Custom/Generic ASA", - "OrcaFilamentLibrary/Generic ASA" - ], - "successor": "OFLPAxz3" - }, - "OGFB99": { - "claims": [ - "Custom/Generic ABS", - "OrcaFilamentLibrary/Generic ABS", - "Sovol/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "OGFC00": { - "claims": [ - "OrcaFilamentLibrary/Bambu PC" - ], - "successor": "OFKhMPeX" - }, - "OGFC01": { - "claims": [ - "OrcaFilamentLibrary/Bambu PC FR" - ], - "successor": "OFg57Nmc" - }, - "OGFC99": { - "claims": [], - "successor": "OFLakOUI" - }, - "OGFG00": { - "claims": [ - "OrcaFilamentLibrary/Bambu PETG Basic" - ], - "successor": "OFFvzqcd" - }, - "OGFG01": { - "claims": [ - "OrcaFilamentLibrary/Bambu PETG Translucent" - ], - "successor": "OFwPrlCM" - }, - "OGFG02": { - "claims": [ - "OrcaFilamentLibrary/Bambu PETG HF" - ], - "successor": "OFovEIbw" - }, - "OGFG50": { - "claims": [ - "OrcaFilamentLibrary/Bambu PETG-CF" - ], - "successor": "OFTRZ8Y4" - }, - "OGFG60": { - "claims": [ - "OrcaFilamentLibrary/PolyLite PETG" - ], - "successor": "OF0UJcb6" - }, - "OGFG97": { - "claims": [ - "OrcaFilamentLibrary/Generic PCTG" - ], - "successor": "OFu1evlr" - }, - "OGFG99": { - "claims": [], - "successor": "OFYPdQJh" - }, - "OGFGG300": { - "claims": [ - "OrcaFilamentLibrary/GreenGate3D PETG" - ], - "successor": "OFhMO9Kh" - }, - "OGFL00": { - "claims": [ - "OrcaFilamentLibrary/PolyLite PLA" - ], - "successor": "OF5CgdDq" - }, - "OGFL00-1": { - "claims": [ - "OrcaFilamentLibrary/PolyTerra Dual PLA" - ], - "successor": "OFCccVrQ" - }, - "OGFL01": { - "claims": [ - "OrcaFilamentLibrary/PolyTerra PLA", - "Snapmaker/PolyTerra PLA" - ], - "successor": "OFW29a9R" - }, - "OGFL03": { - "claims": [ - "OrcaFilamentLibrary/eSUN PLA+" - ], - "successor": "OFqINlYj" - }, - "OGFL04": { - "claims": [ - "OrcaFilamentLibrary/Overture PLA" - ], - "successor": "OFc3xdm9" - }, - "OGFL05": { - "claims": [ - "OrcaFilamentLibrary/Overture Matte PLA" - ], - "successor": "OFBw6eEG" - }, - "OGFL06": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PETG-ESD" - ], - "successor": "OFcytyoA" - }, - "OGFL50": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PA6-CF" - ], - "successor": "OFkOviHk" - }, - "OGFL51": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PA6-GF" - ], - "successor": "OFwaYjL6" - }, - "OGFL52": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PA12-CF" - ], - "successor": "OFX0ycRQ" - }, - "OGFL53": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PA612-CF" - ], - "successor": "OFS2tn6G" - }, - "OGFL54": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PET-CF" - ], - "successor": "OFHWSM21" - }, - "OGFL55": { - "claims": [ - "OrcaFilamentLibrary/Fiberon PETG-rCF" - ], - "successor": "OFk8t9mz" - }, - "OGFL95": { - "claims": [ - "OrcaFilamentLibrary/Generic PLA High Speed" - ], - "successor": "OFmpMwxS" - }, - "OGFL96": { - "claims": [ - "Flashforge/Generic PLA Silk", - "Sovol/Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "OGFL98": { - "claims": [ - "OrcaFilamentLibrary/Generic PLA-CF" - ], - "successor": "OFWbdGsC" - }, - "OGFL99": { - "claims": [ - "Flashforge/Generic PLA", - "OrcaFilamentLibrary/Generic PLA", - "Sovol/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "OGFLC99": { - "claims": [ - "OrcaFilamentLibrary/Generic CoPE" - ], - "successor": "OFjt9en1" - }, - "OGFN03": { - "claims": [ - "OrcaFilamentLibrary/Bambu PA-CF" - ], - "successor": "OFFNYwWR" - }, - "OGFN04": { - "claims": [ - "OrcaFilamentLibrary/Bambu PAHT-CF" - ], - "successor": "OF2VFe4J" - }, - "OGFN05": { - "claims": [ - "OrcaFilamentLibrary/Bambu PA6-CF" - ], - "successor": "OFniMuTN" - }, - "OGFN06": { - "claims": [ - "OrcaFilamentLibrary/Bambu PPA-CF" - ], - "successor": "OF54B0S0" - }, - "OGFN08": { - "claims": [ - "OrcaFilamentLibrary/Bambu PA6-GF" - ], - "successor": "OFNk8bxk" - }, - "OGFN96": { - "claims": [ - "OrcaFilamentLibrary/Generic PPA-GF" - ], - "successor": "OF8Tlg47" - }, - "OGFN97": { - "claims": [ - "OrcaFilamentLibrary/Generic PPA-CF" - ], - "successor": "OF8tPByX" - }, - "OGFN98": { - "claims": [ - "Custom/Generic PA-CF", - "OrcaFilamentLibrary/Generic PA-CF" - ], - "successor": "OFQLcbps" - }, - "OGFN99": { - "claims": [], - "successor": "OFg8ndtj" - }, - "OGFNMK00": { - "claims": [ - "OrcaFilamentLibrary/Numakers PLA+" - ], - "successor": "OFqGRZyH" - }, - "OGFP97": { - "claims": [ - "OrcaFilamentLibrary/Generic PP" - ], - "successor": "OF1UNk9P" - }, - "OGFP99": { - "claims": [ - "OrcaFilamentLibrary/Generic PE" - ], - "successor": "OFPklMI1" - }, - "OGFPM001": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA" - ], - "successor": "OFsRDvTM" - }, - "OGFPM002": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Matte" - ], - "successor": "OFrOh600" - }, - "OGFPM003": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Marble" - ], - "successor": "OFKiSMWR" - }, - "OGFPM004": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Silk" - ], - "successor": "OFS8lTQt" - }, - "OGFPM005": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Satin" - ], - "successor": "OFPkCJKE" - }, - "OGFPM006": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Translucent" - ], - "successor": "OFCIUsWh" - }, - "OGFPM007": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Galaxy" - ], - "successor": "OFFkCLBl" - }, - "OGFPM008": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Celestial" - ], - "successor": "OFRiYgMK" - }, - "OGFPM009": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Starlight" - ], - "successor": "OFC78WGQ" - }, - "OGFPM010": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Glow" - ], - "successor": "OF0gGRWk" - }, - "OGFPM011": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Luminous" - ], - "successor": "OF0kCNDK" - }, - "OGFPM012": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Metallic" - ], - "successor": "OFjb0wos" - }, - "OGFPM013": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Neon" - ], - "successor": "OFO1GEq6" - }, - "OGFPM014": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA UV Shift" - ], - "successor": "OF1N1qMK" - }, - "OGFPM015": { - "claims": [ - "OrcaFilamentLibrary/Panchroma PLA Temp Shift" - ], - "successor": "OFhASRWj" - }, - "OGFPM016": { - "claims": [ - "OrcaFilamentLibrary/Panchroma CoPE" - ], - "successor": "OFqw2PQA" - }, - "OGFPM017": { - "claims": [ - "OrcaFilamentLibrary/Polymaker HT-PLA" - ], - "successor": "OFPoyiFs" - }, - "OGFPM018": { - "claims": [ - "OrcaFilamentLibrary/Polymaker HT-PLA-GF" - ], - "successor": "OFlwmqrC" - }, - "OGFPM019": { - "claims": [ - "OrcaFilamentLibrary/PolyLite PLA Pro" - ], - "successor": "OFF9OKoY" - }, - "OGFR00": { - "claims": [ - "OrcaFilamentLibrary/FusRock ABS-GF" - ], - "successor": "OFmn9NZL" - }, - "OGFR98": { - "claims": [ - "OrcaFilamentLibrary/Generic PHA" - ], - "successor": "OF74KeQR" - }, - "OGFR99": { - "claims": [ - "OrcaFilamentLibrary/Generic EVA" - ], - "successor": "OFCD8qiU" - }, - "OGFS00": { - "claims": [ - "OrcaFilamentLibrary/Bambu Support W" - ], - "successor": "OFsHSVZc" - }, - "OGFS01": { - "claims": [ - "OrcaFilamentLibrary/Bambu Support G" - ], - "successor": "OFQHiNJs" - }, - "OGFS02": { - "claims": [ - "OrcaFilamentLibrary/Bambu Support For PLA" - ], - "successor": "OFAnyRUI" - }, - "OGFS03": { - "claims": [ - "OrcaFilamentLibrary/Bambu Support For PA/PET" - ], - "successor": "OFMTK0UC" - }, - "OGFS04": { - "claims": [ - "OrcaFilamentLibrary/Bambu PVA" - ], - "successor": "OFzyIxba" - }, - "OGFS05": { - "claims": [ - "OrcaFilamentLibrary/Bambu Support For PLA/PETG" - ], - "successor": "OFIfnzxC" - }, - "OGFS06": { - "claims": [ - "OrcaFilamentLibrary/Bambu Support for ABS" - ], - "successor": "OFf6mfQO" - }, - "OGFS97": { - "claims": [ - "Flashforge/Generic BVOH", - "OrcaFilamentLibrary/Generic BVOH" - ], - "successor": "OFo2UF2C" - }, - "OGFS98": { - "claims": [ - "OrcaFilamentLibrary/Generic HIPS" - ], - "successor": "OFsFon5l" - }, - "OGFS99": { - "claims": [ - "OrcaFilamentLibrary/Generic PVA" - ], - "successor": "OFDvXujf" - }, - "OGFSNL02": { - "claims": [ - "OrcaFilamentLibrary/SUNLU PLA Matte" - ], - "successor": "OFpW4gdi" - }, - "OGFSNL03": { - "claims": [ - "OrcaFilamentLibrary/SUNLU PLA+" - ], - "successor": "OFMUWNkp" - }, - "OGFSNL04": { - "claims": [ - "OrcaFilamentLibrary/SUNLU PLA+ 2.0" - ], - "successor": "OFHmPYRy" - }, - "OGFSNL05": { - "claims": [ - "OrcaFilamentLibrary/SUNLU Silk PLA+" - ], - "successor": "OFpPGSKG" - }, - "OGFSNL06": { - "claims": [ - "OrcaFilamentLibrary/SUNLU PLA Marble" - ], - "successor": "OFxUwUeW" - }, - "OGFSNL07": { - "claims": [ - "OrcaFilamentLibrary/SUNLU Wood PLA" - ], - "successor": "OFLJ8S6I" - }, - "OGFSNL08": { - "claims": [ - "Sovol/SUNLU PETG" - ], - "successor": "OF2GCW1l" - }, - "OGFT01": { - "claims": [ - "OrcaFilamentLibrary/Bambu PET-CF" - ], - "successor": "OFHa48An" - }, - "OGFT02": { - "claims": [], - "successor": null - }, - "OGFU00": { - "claims": [ - "OrcaFilamentLibrary/Bambu TPU 95A HF" - ], - "successor": "OFvKUnLh" - }, - "OGFU01": { - "claims": [ - "OrcaFilamentLibrary/Bambu TPU 95A" - ], - "successor": "OFVCkX5w" - }, - "OGFU99": { - "claims": [ - "OrcaFilamentLibrary/Generic TPU", - "Sovol/Generic TPU" - ], - "successor": "OFgbpcy9" - }, - "OVTABS08": { - "claims": [ - "OrcaFilamentLibrary/Overture ABS Basic" - ], - "successor": "OFDvqJ6n" - }, - "P1001": { - "claims": [ - "Creality/Panchroma PLA Satin" - ], - "successor": "OFPkCJKE" - }, - "P1002": { - "claims": [ - "Creality/PolySonic PLA Pro" - ], - "successor": "OFKbdiiJ" - }, - "P1003": { - "claims": [ - "Creality/Panchroma PLA Matte" - ], - "successor": "OFrOh600" - }, - "P1004": { - "claims": [ - "Creality/PolySonic PLA" - ], - "successor": "OF66KUAN" - }, - "P11851ba": { - "claims": [ - "Artillery/Artillery PET" - ], - "successor": "OFb5EEM3" - }, - "P284941e": { - "claims": [ - "Artillery/Artillery PETG", - "Artillery/Artillery PETG Basic" - ], - "successor": "OFilnglt" - }, - "P2fbf0c0": { - "claims": [ - "Artillery/Artillery TPU" - ], - "successor": "OFcJDtTx" - }, - "P39db358": { - "claims": [ - "Artillery/Artillery ABS" - ], - "successor": "OFgXgt98" - }, - "P510cfa0": { - "claims": [ - "Cubicon/Cubicon PLA" - ], - "successor": "OFEwRwmL" - }, - "P510cfa1": { - "claims": [ - "Cubicon/Cubicon PLA+" - ], - "successor": "OF7tt7DO" - }, - "P510cfa2": { - "claims": [ - "Cubicon/Cubicon PLAi21" - ], - "successor": "OFRhBryT" - }, - "P510cfb0": { - "claims": [ - "Cubicon/Cubicon ABS" - ], - "successor": "OFpDo5Ix" - }, - "P510cfb1": { - "claims": [ - "Cubicon/Cubicon ABS-A100" - ], - "successor": "OFy8tYJE" - }, - "P510cfb2": { - "claims": [ - "Cubicon/Cubicon ABSk" - ], - "successor": "OForhBi4" - }, - "P510cfc0": { - "claims": [ - "Cubicon/Cubicon PETG" - ], - "successor": "OFoBTKmn" - }, - "P510cfd0": { - "claims": [ - "Cubicon/Cubicon PA-CF" - ], - "successor": "OFiEz2XI" - }, - "P510eff9": { - "claims": [ - "Ginger Additive/Ginger Generic PETG" - ], - "successor": "OFsGqI4y" - }, - "P8163162": { - "claims": [ - "Artillery/Artillery PVA" - ], - "successor": "OFkx5MEe" - }, - "P87436f6": { - "claims": [ - "Artillery/Artillery ASA" - ], - "successor": "OF5YVJXH" - }, - "Pfcf9c4c": { - "claims": [ - "Artillery/Artillery PLA" - ], - "successor": "OFzY7N3Z" - }, - "Prusa Generic ABS @CORE One": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ABS @CORE One 0.6": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ABS @CORE One 0.8": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ABS @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ABS @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ABS @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ABS @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusa Generic ABS" - ], - "successor": "OFvGc67v" - }, - "Prusa Generic ASA @CORE One": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic ASA @CORE One 0.6": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic ASA @CORE One 0.8": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic ASA @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic ASA @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic ASA @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic ASA @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusa Generic PETG @CORE One": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PETG @CORE One 0.6": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PETG @CORE One 0.8": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PETG @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PETG @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PETG @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PETG @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusa Generic PETG" - ], - "successor": "OF1Id239" - }, - "Prusa Generic PLA @CORE One": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA @CORE One 0.6": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA @CORE One 0.8": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusa Generic PLA" - ], - "successor": "OFiCHnso" - }, - "Prusa Generic PLA Silk @CORE One": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFHFZ1Te" - }, - "Prusa Generic PLA Silk @CORE One 0.6": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFHFZ1Te" - }, - "Prusa Generic PLA Silk @CORE One 0.8": { - "claims": [ - "Prusa/Prusa Generic PLA Silk" - ], - "successor": "OFHFZ1Te" - }, - "Prusa Generic TPU @CORE One": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFbenYUK" - }, - "Prusa Generic TPU @CORE One 0.6": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFbenYUK" - }, - "Prusa Generic TPU @CORE One 0.8": { - "claims": [ - "Prusa/Prusa Generic TPU" - ], - "successor": "OFbenYUK" - }, - "Prusament ASA @CORE One": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @CORE One 0.6": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @CORE One 0.8": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusament ASA" - ], - "successor": "OFmFwUWM" - }, - "Prusament ASA @MK4S": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament ASA @MK4S 0.6": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament ASA @MK4S 0.8": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament ASA @MK4S HF0.4": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament ASA @MK4S HF0.5": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament ASA @MK4S HF0.6": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament ASA @MK4S HF0.8": { - "claims": [ - "Prusa/Prusa Generic ASA" - ], - "successor": "OFoJcr46" - }, - "Prusament PA-CF @CORE One": { - "claims": [ - "Prusa/Prusament PA-CF" - ], - "successor": "OF54SvEe" - }, - "Prusament PA-CF @CORE One 0.6": { - "claims": [ - "Prusa/Prusament PA-CF" - ], - "successor": "OF54SvEe" - }, - "Prusament PA-CF @CORE One 0.8": { - "claims": [ - "Prusa/Prusament PA-CF" - ], - "successor": "OF54SvEe" - }, - "Prusament PC Blend @CORE One": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC Blend @CORE One 0.6": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC Blend @CORE One 0.8": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC Blend @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC Blend @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC Blend @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC Blend @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusament PC Blend" - ], - "successor": "OFvDbkFq" - }, - "Prusament PC-CF @CORE One": { - "claims": [ - "Prusa/Prusament PC-CF" - ], - "successor": "OFjz0a3m" - }, - "Prusament PC-CF @CORE One 0.6": { - "claims": [ - "Prusa/Prusament PC-CF" - ], - "successor": "OFjz0a3m" - }, - "Prusament PC-CF @CORE One 0.8": { - "claims": [ - "Prusa/Prusament PC-CF" - ], - "successor": "OFjz0a3m" - }, - "Prusament PETG @CORE One": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PETG @CORE One 0.6": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PETG @CORE One 0.8": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PETG @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PETG @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PETG @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PETG @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusament PETG" - ], - "successor": "OFkR8E2c" - }, - "Prusament PLA @CORE One": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PLA @CORE One 0.6": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PLA @CORE One 0.8": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PLA @CORE One HF 0.4": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PLA @CORE One HF 0.5": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PLA @CORE One HF 0.6": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PLA @CORE One HF 0.8": { - "claims": [ - "Prusa/Prusament PLA" - ], - "successor": "OFnO4wnf" - }, - "Prusament PVB @CORE One": { - "claims": [ - "Prusa/Prusament PVB" - ], - "successor": "OFh7mvPO" - }, - "Prusament PVB @CORE One 0.6": { - "claims": [ - "Prusa/Prusament PVB" - ], - "successor": "OFh7mvPO" - }, - "Prusament PVB @CORE One 0.8": { - "claims": [ - "Prusa/Prusament PVB" - ], - "successor": "OFh7mvPO" - }, - "Prusament rPLA @CORE One": { - "claims": [ - "Prusa/Prusament rPLA" - ], - "successor": "OFWRITFw" - }, - "Prusament rPLA @CORE One 0.6": { - "claims": [ - "Prusa/Prusament rPLA" - ], - "successor": "OFWRITFw" - }, - "Prusament rPLA @CORE One 0.8": { - "claims": [ - "Prusa/Prusament rPLA" - ], - "successor": "OFWRITFw" - }, - "QD_0_1_1": { - "claims": [ - "Qidi/QIDI PLA Rapido" - ], - "successor": "OFKIQO2W" - }, - "QD_0_1_15": { - "claims": [ - "Qidi/QIDI TPU-GF" - ], - "successor": "OF9EHUKK" - }, - "QD_0_1_2": { - "claims": [ - "Qidi/QIDI PLA Rapido Matte" - ], - "successor": "OF3IKafc" - }, - "QD_0_1_20": { - "claims": [ - "Qidi/QIDI ASA-CF" - ], - "successor": "OF1W7c76" - }, - "QD_0_1_24": { - "claims": [ - "Qidi/QIDI PA-Ultra" - ], - "successor": "OFlN6DL1" - }, - "QD_0_1_26": { - "claims": [ - "Qidi/QIDI UltraPA-CF25" - ], - "successor": "OF9PELEb" - }, - "QD_0_1_27": { - "claims": [ - "Qidi/QIDI PA12-CF" - ], - "successor": "OFn6WpN7" - }, - "QD_0_1_3": { - "claims": [ - "Qidi/QIDI PLA Rapido Metal" - ], - "successor": "OFANFm4x" - }, - "QD_0_1_30": { - "claims": [ - "Qidi/QIDI PAHT-CF" - ], - "successor": "OFKgmsqE" - }, - "QD_0_1_31": { - "claims": [ - "Qidi/QIDI PAHT-GF" - ], - "successor": "OFo15hcT" - }, - "QD_0_1_32": { - "claims": [ - "Qidi/QIDI Support For PAHT" - ], - "successor": "OFXn3Vd9" - }, - "QD_0_1_33": { - "claims": [ - "Qidi/QIDI Support For PET/PA" - ], - "successor": "OFqcL5UF" - }, - "QD_0_1_34": { - "claims": [ - "Qidi/Qidi PC-ABS-FR" - ], - "successor": "OFjD3YTW" - }, - "QD_0_1_36": { - "claims": [ - "Qidi/QIDI PEBA 95A" - ], - "successor": "OFhiJE7Y" - }, - "QD_0_1_37": { - "claims": [ - "Qidi/QIDI PET-CF" - ], - "successor": "OFKfJTwL" - }, - "QD_0_1_38": { - "claims": [ - "Qidi/QIDI PET-GF" - ], - "successor": "OFhx4uOG" - }, - "QD_0_1_39": { - "claims": [ - "Qidi/QIDI PETG Basic" - ], - "successor": "OFuJ49uH" - }, - "QD_0_1_4": { - "claims": [ - "Qidi/QIDI PLA Rapido Silk" - ], - "successor": "OFW5FqkL" - }, - "QD_0_1_40": { - "claims": [ - "Qidi/QIDI PETG Tough" - ], - "successor": "OFQ40XeN" - }, - "QD_0_1_41": { - "claims": [ - "Qidi/QIDI PETG Rapido" - ], - "successor": "OF4S3To3" - }, - "QD_0_1_42": { - "claims": [ - "Qidi/QIDI PETG-CF" - ], - "successor": "OF4iWKrE" - }, - "QD_0_1_43": { - "claims": [ - "Qidi/QIDI PETG-GF" - ], - "successor": "OFdtPd9h" - }, - "QD_0_1_44": { - "claims": [ - "Qidi/QIDI PPS-CF" - ], - "successor": "OFZOuWrO" - }, - "QD_0_1_45": { - "claims": [ - "Qidi/QIDI PETG Translucent" - ], - "successor": "OFf0MkD5" - }, - "QD_0_1_46": { - "claims": [ - "Qidi/QIDI PPS-GF" - ], - "successor": "OFp57RRC" - }, - "QD_0_1_49": { - "claims": [ - "Qidi/QIDI TPU-Aero" - ], - "successor": "OFMDHOcT" - }, - "QD_0_1_5": { - "claims": [ - "Qidi/QIDI PLA-CF" - ], - "successor": "OFhWrAuP" - }, - "QD_0_1_50": { - "claims": [ - "Qidi/Qidi TPU 95A-HF" - ], - "successor": "OFzAWsca" - }, - "QD_0_1_6": { - "claims": [ - "Qidi/QIDI WOOD Rapido" - ], - "successor": "OFvdZ2bx" - }, - "QD_0_1_7": { - "claims": [ - "Qidi/QIDI PLA Basic" - ], - "successor": "OF4nQ62r" - }, - "QD_0_1_8": { - "claims": [ - "Qidi/QIDI PLA Matte Basic" - ], - "successor": "OFughwKn" - }, - "QD_1_0_1": { - "claims": [ - "Qidi/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "QD_1_0_11": { - "claims": [ - "Qidi/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "QD_1_0_23": { - "claims": [ - "Qidi/Generic PC" - ], - "successor": "OFLakOUI" - }, - "QD_1_0_4": { - "claims": [ - "Qidi/Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "QD_1_0_41": { - "claims": [ - "Qidi/Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "QD_1_0_50": { - "claims": [ - "Qidi/Generic TPU 95A" - ], - "successor": "OFjiXpyf" - }, - "QD_1_1_1": { - "claims": [ - "Qidi/QIDI PLA Rapido" - ], - "successor": "OFKIQO2W" - }, - "QD_1_1_11": { - "claims": [ - "Qidi/QIDI ABS Rapido" - ], - "successor": "OFT2jon4" - }, - "QD_1_1_12": { - "claims": [ - "Qidi/QIDI ABS-GF" - ], - "successor": "OFt3HtlG" - }, - "QD_1_1_13": { - "claims": [ - "Qidi/QIDI ABS Rapido Metal" - ], - "successor": "OFeCJqxx" - }, - "QD_1_1_14": { - "claims": [ - "Qidi/QIDI ABS Odorless" - ], - "successor": "OFma5TXH" - }, - "QD_1_1_15": { - "claims": [ - "Qidi/QIDI TPU-GF" - ], - "successor": "OF9EHUKK" - }, - "QD_1_1_18": { - "claims": [ - "Qidi/QIDI ASA" - ], - "successor": "OFTsHNQi" - }, - "QD_1_1_19": { - "claims": [ - "Qidi/QIDI ASA-Aero" - ], - "successor": "OFu1E4ur" - }, - "QD_1_1_2": { - "claims": [ - "Qidi/QIDI PLA Rapido Matte" - ], - "successor": "OF3IKafc" - }, - "QD_1_1_20": { - "claims": [ - "Qidi/QIDI ASA-CF" - ], - "successor": "OF1W7c76" - }, - "QD_1_1_24": { - "claims": [ - "Qidi/QIDI UltraPA" - ], - "successor": "OFWDe7YH" - }, - "QD_1_1_26": { - "claims": [ - "Qidi/QIDI UltraPA-CF25" - ], - "successor": "OF9PELEb" - }, - "QD_1_1_27": { - "claims": [ - "Qidi/QIDI PA12-CF" - ], - "successor": "OFn6WpN7" - }, - "QD_1_1_3": { - "claims": [ - "Qidi/QIDI PLA Rapido Metal" - ], - "successor": "OFANFm4x" - }, - "QD_1_1_30": { - "claims": [ - "Qidi/QIDI PAHT-CF" - ], - "successor": "OFKgmsqE" - }, - "QD_1_1_31": { - "claims": [ - "Qidi/QIDI PAHT-GF" - ], - "successor": "OFo15hcT" - }, - "QD_1_1_32": { - "claims": [ - "Qidi/QIDI Support For PAHT" - ], - "successor": "OFXn3Vd9" - }, - "QD_1_1_33": { - "claims": [ - "Qidi/QIDI Support For PET/PA" - ], - "successor": "OFqcL5UF" - }, - "QD_1_1_34": { - "claims": [ - "Qidi/QIDI PC-ABS-FR" - ], - "successor": "OFFAWVf0" - }, - "QD_1_1_36": { - "claims": [ - "Qidi/QIDI PEBA 95A" - ], - "successor": "OFhiJE7Y" - }, - "QD_1_1_37": { - "claims": [ - "Qidi/QIDI PET-CF" - ], - "successor": "OFKfJTwL" - }, - "QD_1_1_38": { - "claims": [ - "Qidi/QIDI PET-GF" - ], - "successor": "OFhx4uOG" - }, - "QD_1_1_39": { - "claims": [ - "Qidi/QIDI PETG Basic" - ], - "successor": "OFuJ49uH" - }, - "QD_1_1_4": { - "claims": [ - "Qidi/QIDI PLA Rapido Silk" - ], - "successor": "OFW5FqkL" - }, - "QD_1_1_40": { - "claims": [ - "Qidi/QIDI PETG Tough" - ], - "successor": "OFQ40XeN" - }, - "QD_1_1_41": { - "claims": [ - "Qidi/QIDI PETG Rapido" - ], - "successor": "OF4S3To3" - }, - "QD_1_1_42": { - "claims": [ - "Qidi/QIDI PETG-CF" - ], - "successor": "OF4iWKrE" - }, - "QD_1_1_43": { - "claims": [ - "Qidi/QIDI PETG-GF" - ], - "successor": "OFdtPd9h" - }, - "QD_1_1_44": { - "claims": [ - "Qidi/QIDI PPS-CF" - ], - "successor": "OFZOuWrO" - }, - "QD_1_1_45": { - "claims": [ - "Qidi/QIDI PETG Translucent" - ], - "successor": "OFf0MkD5" - }, - "QD_1_1_46": { - "claims": [ - "Qidi/QIDI PPS-GF" - ], - "successor": "OFp57RRC" - }, - "QD_1_1_49": { - "claims": [ - "Qidi/QIDI TPU-Aero" - ], - "successor": "OFMDHOcT" - }, - "QD_1_1_5": { - "claims": [ - "Qidi/QIDI PLA-CF" - ], - "successor": "OFhWrAuP" - }, - "QD_1_1_50": { - "claims": [ - "Qidi/QIDI TPU 95A-HF" - ], - "successor": "OFR1XLMN" - }, - "QD_1_1_6": { - "claims": [ - "Qidi/QIDI WOOD Rapido" - ], - "successor": "OFvdZ2bx" - }, - "QD_1_1_7": { - "claims": [ - "Qidi/QIDI PLA Basic" - ], - "successor": "OF4nQ62r" - }, - "QD_1_1_8": { - "claims": [ - "Qidi/QIDI PLA Matte Basic" - ], - "successor": "OFughwKn" - }, - "QD_2_0_1": { - "claims": [ - "Qidi/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "QD_2_0_11": { - "claims": [ - "Qidi/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "QD_2_0_23": { - "claims": [ - "Qidi/Generic PC" - ], - "successor": "OFLakOUI" - }, - "QD_2_0_4": { - "claims": [ - "Qidi/Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "QD_2_0_41": { - "claims": [ - "Qidi/Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "QD_2_0_50": { - "claims": [ - "Qidi/Generic TPU 95A" - ], - "successor": "OFjiXpyf" - }, - "QD_2_1_1": { - "claims": [ - "Qidi/QIDI PLA Rapido" - ], - "successor": "OFKIQO2W" - }, - "QD_2_1_11": { - "claims": [ - "Qidi/QIDI ABS Rapido" - ], - "successor": "OFT2jon4" - }, - "QD_2_1_12": { - "claims": [ - "Qidi/QIDI ABS-GF" - ], - "successor": "OFt3HtlG" - }, - "QD_2_1_13": { - "claims": [ - "Qidi/QIDI ABS Rapido Metal" - ], - "successor": "OFeCJqxx" - }, - "QD_2_1_14": { - "claims": [ - "Qidi/QIDI ABS Odorless" - ], - "successor": "OFma5TXH" - }, - "QD_2_1_15": { - "claims": [ - "Qidi/QIDI TPU-GF" - ], - "successor": "OF9EHUKK" - }, - "QD_2_1_18": { - "claims": [ - "Qidi/QIDI ASA" - ], - "successor": "OFTsHNQi" - }, - "QD_2_1_19": { - "claims": [ - "Qidi/QIDI ASA-Aero" - ], - "successor": "OFu1E4ur" - }, - "QD_2_1_2": { - "claims": [ - "Qidi/QIDI PLA Rapido Matte" - ], - "successor": "OF3IKafc" - }, - "QD_2_1_20": { - "claims": [ - "Qidi/QIDI ASA-CF" - ], - "successor": "OF1W7c76" - }, - "QD_2_1_24": { - "claims": [ - "Qidi/QIDI UltraPA" - ], - "successor": "OFWDe7YH" - }, - "QD_2_1_26": { - "claims": [ - "Qidi/QIDI UltraPA-CF25" - ], - "successor": "OF9PELEb" - }, - "QD_2_1_27": { - "claims": [ - "Qidi/QIDI PA12-CF" - ], - "successor": "OFn6WpN7" - }, - "QD_2_1_3": { - "claims": [ - "Qidi/QIDI PLA Rapido Metal" - ], - "successor": "OFANFm4x" - }, - "QD_2_1_30": { - "claims": [ - "Qidi/QIDI PAHT-CF" - ], - "successor": "OFKgmsqE" - }, - "QD_2_1_31": { - "claims": [ - "Qidi/QIDI PAHT-GF" - ], - "successor": "OFo15hcT" - }, - "QD_2_1_32": { - "claims": [ - "Qidi/QIDI Support For PAHT" - ], - "successor": "OFXn3Vd9" - }, - "QD_2_1_33": { - "claims": [ - "Qidi/QIDI Support For PET/PA" - ], - "successor": "OFqcL5UF" - }, - "QD_2_1_34": { - "claims": [ - "Qidi/QIDI PC-ABS-FR" - ], - "successor": "OFFAWVf0" - }, - "QD_2_1_36": { - "claims": [ - "Qidi/QIDI PEBA 95A" - ], - "successor": "OFhiJE7Y" - }, - "QD_2_1_37": { - "claims": [ - "Qidi/QIDI PET-CF" - ], - "successor": "OFKfJTwL" - }, - "QD_2_1_38": { - "claims": [ - "Qidi/QIDI PET-GF" - ], - "successor": "OFhx4uOG" - }, - "QD_2_1_39": { - "claims": [ - "Qidi/QIDI PETG Basic" - ], - "successor": "OFuJ49uH" - }, - "QD_2_1_4": { - "claims": [ - "Qidi/QIDI PLA Rapido Silk" - ], - "successor": "OFW5FqkL" - }, - "QD_2_1_40": { - "claims": [ - "Qidi/QIDI PETG Tough" - ], - "successor": "OFQ40XeN" - }, - "QD_2_1_41": { - "claims": [ - "Qidi/QIDI PETG Rapido" - ], - "successor": "OF4S3To3" - }, - "QD_2_1_42": { - "claims": [ - "Qidi/QIDI PETG-CF" - ], - "successor": "OF4iWKrE" - }, - "QD_2_1_43": { - "claims": [ - "Qidi/QIDI PETG-GF" - ], - "successor": "OFdtPd9h" - }, - "QD_2_1_44": { - "claims": [ - "Qidi/QIDI PPS-CF" - ], - "successor": "OFZOuWrO" - }, - "QD_2_1_45": { - "claims": [ - "Qidi/QIDI PETG Translucent" - ], - "successor": "OFf0MkD5" - }, - "QD_2_1_46": { - "claims": [ - "Qidi/QIDI PPS-GF" - ], - "successor": "OFp57RRC" - }, - "QD_2_1_49": { - "claims": [ - "Qidi/QIDI TPU-Aero" - ], - "successor": "OFMDHOcT" - }, - "QD_2_1_5": { - "claims": [ - "Qidi/QIDI PLA-CF" - ], - "successor": "OFhWrAuP" - }, - "QD_2_1_50": { - "claims": [ - "Qidi/QIDI TPU 95A-HF" - ], - "successor": "OFR1XLMN" - }, - "QD_2_1_6": { - "claims": [ - "Qidi/QIDI WOOD Rapido" - ], - "successor": "OFvdZ2bx" - }, - "QD_2_1_7": { - "claims": [ - "Qidi/QIDI PLA Basic" - ], - "successor": "OF4nQ62r" - }, - "QD_2_1_8": { - "claims": [ - "Qidi/QIDI PLA Matte Basic" - ], - "successor": "OFughwKn" - }, - "QD_3_0_1": { - "claims": [ - "Qidi/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "QD_3_0_11": { - "claims": [ - "Qidi/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "QD_3_0_23": { - "claims": [ - "Qidi/Generic PC" - ], - "successor": "OFLakOUI" - }, - "QD_3_0_4": { - "claims": [ - "Qidi/Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "QD_3_0_41": { - "claims": [ - "Qidi/Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "QD_3_0_50": { - "claims": [ - "Qidi/Generic TPU 95A" - ], - "successor": "OFjiXpyf" - }, - "QD_3_1_1": { - "claims": [ - "Qidi/QIDI PLA Rapido" - ], - "successor": "OFKIQO2W" - }, - "QD_3_1_11": { - "claims": [ - "Qidi/QIDI ABS Rapido" - ], - "successor": "OFT2jon4" - }, - "QD_3_1_12": { - "claims": [ - "Qidi/QIDI ABS-GF" - ], - "successor": "OFt3HtlG" - }, - "QD_3_1_13": { - "claims": [ - "Qidi/QIDI ABS Rapido Metal" - ], - "successor": "OFeCJqxx" - }, - "QD_3_1_14": { - "claims": [ - "Qidi/QIDI ABS Odorless" - ], - "successor": "OFma5TXH" - }, - "QD_3_1_15": { - "claims": [ - "Qidi/QIDI TPU-GF" - ], - "successor": "OF9EHUKK" - }, - "QD_3_1_18": { - "claims": [ - "Qidi/QIDI ASA" - ], - "successor": "OFTsHNQi" - }, - "QD_3_1_19": { - "claims": [ - "Qidi/QIDI ASA-Aero" - ], - "successor": "OFu1E4ur" - }, - "QD_3_1_2": { - "claims": [ - "Qidi/QIDI PLA Rapido Matte" - ], - "successor": "OF3IKafc" - }, - "QD_3_1_20": { - "claims": [ - "Qidi/QIDI ASA-CF" - ], - "successor": "OF1W7c76" - }, - "QD_3_1_24": { - "claims": [ - "Qidi/QIDI UltraPA" - ], - "successor": "OFWDe7YH" - }, - "QD_3_1_26": { - "claims": [ - "Qidi/QIDI UltraPA-CF25" - ], - "successor": "OF9PELEb" - }, - "QD_3_1_27": { - "claims": [ - "Qidi/QIDI PA12-CF" - ], - "successor": "OFn6WpN7" - }, - "QD_3_1_3": { - "claims": [ - "Qidi/QIDI PLA Rapido Metal" - ], - "successor": "OFANFm4x" - }, - "QD_3_1_30": { - "claims": [ - "Qidi/QIDI PAHT-CF" - ], - "successor": "OFKgmsqE" - }, - "QD_3_1_31": { - "claims": [ - "Qidi/QIDI PAHT-GF" - ], - "successor": "OFo15hcT" - }, - "QD_3_1_32": { - "claims": [ - "Qidi/QIDI Support For PAHT" - ], - "successor": "OFXn3Vd9" - }, - "QD_3_1_33": { - "claims": [ - "Qidi/QIDI Support For PET/PA" - ], - "successor": "OFqcL5UF" - }, - "QD_3_1_34": { - "claims": [ - "Qidi/QIDI PC/ABS-FR" - ], - "successor": "OFDIrhbu" - }, - "QD_3_1_36": { - "claims": [ - "Qidi/QIDI PEBA 95A" - ], - "successor": "OFhiJE7Y" - }, - "QD_3_1_37": { - "claims": [ - "Qidi/QIDI PET-CF" - ], - "successor": "OFKfJTwL" - }, - "QD_3_1_38": { - "claims": [ - "Qidi/QIDI PET-GF" - ], - "successor": "OFhx4uOG" - }, - "QD_3_1_39": { - "claims": [ - "Qidi/QIDI PETG Basic" - ], - "successor": "OFuJ49uH" - }, - "QD_3_1_4": { - "claims": [ - "Qidi/QIDI PLA Rapido Silk" - ], - "successor": "OFW5FqkL" - }, - "QD_3_1_40": { - "claims": [ - "Qidi/QIDI PETG Tough" - ], - "successor": "OFQ40XeN" - }, - "QD_3_1_41": { - "claims": [ - "Qidi/QIDI PETG Rapido" - ], - "successor": "OF4S3To3" - }, - "QD_3_1_42": { - "claims": [ - "Qidi/QIDI PETG-CF" - ], - "successor": "OF4iWKrE" - }, - "QD_3_1_43": { - "claims": [ - "Qidi/QIDI PETG-GF" - ], - "successor": "OFdtPd9h" - }, - "QD_3_1_44": { - "claims": [ - "Qidi/QIDI PPS-CF" - ], - "successor": "OFZOuWrO" - }, - "QD_3_1_45": { - "claims": [ - "Qidi/QIDI PETG Translucent" - ], - "successor": "OFf0MkD5" - }, - "QD_3_1_46": { - "claims": [ - "Qidi/QIDI PPS-GF" - ], - "successor": "OFp57RRC" - }, - "QD_3_1_49": { - "claims": [ - "Qidi/QIDI TPU-Aero" - ], - "successor": "OFMDHOcT" - }, - "QD_3_1_5": { - "claims": [ - "Qidi/QIDI PLA-CF" - ], - "successor": "OFhWrAuP" - }, - "QD_3_1_50": { - "claims": [ - "Qidi/QIDI TPU 95A-HF" - ], - "successor": "OFR1XLMN" - }, - "QD_3_1_6": { - "claims": [ - "Qidi/QIDI WOOD Rapido" - ], - "successor": "OFvdZ2bx" - }, - "QD_3_1_7": { - "claims": [ - "Qidi/QIDI PLA Basic" - ], - "successor": "OF4nQ62r" - }, - "QD_3_1_8": { - "claims": [ - "Qidi/QIDI PLA Matte Basic" - ], - "successor": "OFughwKn" - }, - "QD_4_0_1": { - "claims": [ - "Qidi/Generic PLA" - ], - "successor": "OFDSrzZ8" - }, - "QD_4_0_11": { - "claims": [ - "Qidi/Generic ABS" - ], - "successor": "OFY9muEs" - }, - "QD_4_0_23": { - "claims": [ - "Qidi/Generic PC" - ], - "successor": "OFLakOUI" - }, - "QD_4_0_4": { - "claims": [ - "Qidi/Generic PLA Silk" - ], - "successor": "OFesA6rF" - }, - "QD_4_0_41": { - "claims": [ - "Qidi/Generic PETG" - ], - "successor": "OFYPdQJh" - }, - "QD_4_0_50": { - "claims": [ - "Qidi/Generic TPU 95A" - ], - "successor": "OFjiXpyf" - }, - "QD_4_1_1": { - "claims": [ - "Qidi/QIDI PLA Rapido" - ], - "successor": "OFKIQO2W" - }, - "QD_4_1_11": { - "claims": [ - "Qidi/QIDI ABS Rapido" - ], - "successor": "OFT2jon4" - }, - "QD_4_1_12": { - "claims": [ - "Qidi/QIDI ABS-GF" - ], - "successor": "OFt3HtlG" - }, - "QD_4_1_13": { - "claims": [ - "Qidi/QIDI ABS Rapido Metal" - ], - "successor": "OFeCJqxx" - }, - "QD_4_1_14": { - "claims": [ - "Qidi/QIDI ABS Odorless" - ], - "successor": "OFma5TXH" - }, - "QD_4_1_15": { - "claims": [ - "Qidi/QIDI TPU-GF" - ], - "successor": "OF9EHUKK" - }, - "QD_4_1_18": { - "claims": [ - "Qidi/QIDI ASA" - ], - "successor": "OFTsHNQi" - }, - "QD_4_1_19": { - "claims": [ - "Qidi/QIDI ASA-Aero" - ], - "successor": "OFu1E4ur" - }, - "QD_4_1_2": { - "claims": [ - "Qidi/QIDI PLA Rapido Matte" - ], - "successor": "OF3IKafc" - }, - "QD_4_1_20": { - "claims": [ - "Qidi/QIDI ASA-CF" - ], - "successor": "OF1W7c76" - }, - "QD_4_1_24": { - "claims": [ - "Qidi/QIDI UltraPA" - ], - "successor": "OFWDe7YH" - }, - "QD_4_1_25": { - "claims": [ - "Qidi/QIDI PA6-CF" - ], - "successor": "OFZ3K2cL" - }, - "QD_4_1_26": { - "claims": [ - "Qidi/QIDI UltraPA-CF25" - ], - "successor": "OF9PELEb" - }, - "QD_4_1_27": { - "claims": [ - "Qidi/QIDI PA12-CF" - ], - "successor": "OFn6WpN7" - }, - "QD_4_1_3": { - "claims": [ - "Qidi/QIDI PLA Rapido Metal" - ], - "successor": "OFANFm4x" - }, - "QD_4_1_30": { - "claims": [ - "Qidi/QIDI PAHT-CF" - ], - "successor": "OFKgmsqE" - }, - "QD_4_1_31": { - "claims": [ - "Qidi/QIDI PAHT-GF" - ], - "successor": "OFo15hcT" - }, - "QD_4_1_32": { - "claims": [ - "Qidi/QIDI Support For PAHT" - ], - "successor": "OFXn3Vd9" - }, - "QD_4_1_33": { - "claims": [ - "Qidi/QIDI Support For PET/PA" - ], - "successor": "OFqcL5UF" - }, - "QD_4_1_34": { - "claims": [ - "Qidi/QIDI PC/ABS-FR" - ], - "successor": "OFDIrhbu" - }, - "QD_4_1_36": { - "claims": [ - "Qidi/QIDI PEBA 95A" - ], - "successor": "OFhiJE7Y" - }, - "QD_4_1_37": { - "claims": [ - "Qidi/QIDI PET-CF" - ], - "successor": "OFKfJTwL" - }, - "QD_4_1_38": { - "claims": [ - "Qidi/QIDI PET-GF" - ], - "successor": "OFhx4uOG" - }, - "QD_4_1_39": { - "claims": [ - "Qidi/QIDI PETG Basic" - ], - "successor": "OFuJ49uH" - }, - "QD_4_1_4": { - "claims": [ - "Qidi/QIDI PLA Silk" - ], - "successor": "OF2TQpOO" - }, - "QD_4_1_40": { - "claims": [ - "Qidi/QIDI PETG Tough" - ], - "successor": "OFQ40XeN" - }, - "QD_4_1_41": { - "claims": [ - "Qidi/QIDI PETG Rapido" - ], - "successor": "OF4S3To3" - }, - "QD_4_1_42": { - "claims": [ - "Qidi/QIDI PETG-CF" - ], - "successor": "OF4iWKrE" - }, - "QD_4_1_43": { - "claims": [ - "Qidi/QIDI PETG-GF" - ], - "successor": "OFdtPd9h" - }, - "QD_4_1_44": { - "claims": [ - "Qidi/QIDI PPS-CF" - ], - "successor": "OFZOuWrO" - }, - "QD_4_1_45": { - "claims": [ - "Qidi/QIDI PETG Translucent" - ], - "successor": "OFf0MkD5" - }, - "QD_4_1_46": { - "claims": [ - "Qidi/QIDI PPS-GF" - ], - "successor": "OFp57RRC" - }, - "QD_4_1_49": { - "claims": [ - "Qidi/QIDI TPU-Aero" - ], - "successor": "OFMDHOcT" - }, - "QD_4_1_5": { - "claims": [ - "Qidi/QIDI PLA-CF" - ], - "successor": "OFhWrAuP" - }, - "QD_4_1_50": { - "claims": [ - "Qidi/QIDI TPU 95A-HF" - ], - "successor": "OFR1XLMN" - }, - "QD_4_1_6": { - "claims": [ - "Qidi/QIDI WOOD Rapido" - ], - "successor": "OFvdZ2bx" - }, - "QD_4_1_7": { - "claims": [ - "Qidi/QIDI PLA Basic" - ], - "successor": "OF4nQ62r" - }, - "QD_4_1_8": { - "claims": [ - "Qidi/QIDI PLA Matte Basic" - ], - "successor": "OFughwKn" - }, - "SMCFB001": { - "claims": [ - "SeeMeCNC/SeeMeCNC ABS" - ], - "successor": "OFR0gODA" - }, - "SMCFG001": { - "claims": [ - "SeeMeCNC/SeeMeCNC PETG" - ], - "successor": "OFxDHUX8" - }, - "SMCFG002": { - "claims": [ - "SeeMeCNC/SeeMeCNC PETG-CF" - ], - "successor": "OFaRjiIm" - }, - "SMCFL001": { - "claims": [ - "SeeMeCNC/SeeMeCNC PLA" - ], - "successor": "OFbSChKb" - }, - "SMCFN001": { - "claims": [ - "SeeMeCNC/SeeMeCNC PA-CF" - ], - "successor": "OF0b7M1z" - }, - "SMCFU001": { - "claims": [ - "SeeMeCNC/SeeMeCNC TPU" - ], - "successor": "OFyFyLIp" - }, - "VLMNT01": { - "claims": [ - "OrcaFilamentLibrary/Valment PLA" - ], - "successor": "OFDe8uyM" - }, - "VLMNT02": { - "claims": [ - "OrcaFilamentLibrary/Valment PLA Silk" - ], - "successor": "OFpBlrvY" - }, - "VLMNT03": { - "claims": [ - "OrcaFilamentLibrary/Valment PLA-CF" - ], - "successor": "OFfq5YXA" - }, - "VLMNT04": { - "claims": [ - "OrcaFilamentLibrary/Valment PLA Galaxy" - ], - "successor": "OFB5SmWk" - } - } -} diff --git a/scripts/assign_filament_ids.py b/scripts/assign_filament_ids.py index 34ff667ed0..46a138253c 100755 --- a/scripts/assign_filament_ids.py +++ b/scripts/assign_filament_ids.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ Mint deterministic filament_id values for OrcaSlicer system filament products and -validate the tree against the sanctioned-state ledger. +validate the tree against the sanctioned-state snapshot. -Policy (companion to assign_vendor_setting_ids.py; see filament_id_plan_v3.md): +Policy (companion to assign_vendor_setting_ids.py; see docs/HLSD/filament_id.md): * filament_id is a PRODUCT id: one commercial product line = one id, shared by all of that material's per-printer/per-nozzle variants in every bundle. OrcaFilamentLibrary (OFL) is the product catalog: a family's id is declared @@ -19,34 +19,22 @@ Policy (companion to assign_vendor_setting_ids.py; see filament_id_plan_v3.md): filament_id = "OF" + base62_6( uuid5(FILAMENT_ID_NAMESPACE, "filament_product///") ) 8 chars total, which satisfies the AMS length limit. Nobody invents ids by - hand; on the astronomically rare collision with any existing or retired id - the input is salted ("/1", "/2", ...) until free and the result is frozen - in file. Identity changes (a family rename, a filament_vendor/filament_type - fix) change the id BY DESIGN — the succession ledger keeps old ids - resolving. + hand; on the astronomically rare collision with an existing id the input + is salted ("/1", "/2", ...) until free and the result is frozen in file. + Identity changes (a family rename, a filament_vendor/filament_type fix) + change the id BY DESIGN. * Reserved id spaces that are never minted into or altered: - GF* Bambu AMS/RFID catalog (vendor BBL untouchable) - - QD_* Qidi device protocol (dissolved island, plan v4: - every shipped QD_* id is retired in the ledger and - the QidiPrinterAgent translates device-composed - QD_* ids through the succession walk; no preset - may ever declare one again) + - QD_* Qidi device protocol: the box composes these ids + at runtime, they are not preset ids, and no + preset may declare one - P + 7 hex chars (case-insensitive) and the literal "null" user-custom presets (CreatePresetsDialog.cpp) - every already-shipped id, grandfathered via scripts/filament_id_snapshot.json - * scripts/filament_id_snapshot.json is the sanctioned-state ledger (ids, + * scripts/filament_id_snapshot.json is the sanctioned-state snapshot (ids, claims and declared triples): it must exactly equal the tree-derived state at all times, so any id/claim/triple change shows up as a reviewable diff to - that file (the maintainer gate). Ids that fully vanish from the tree are - appended to the SHIPPED succession ledger - resources/profiles/retired_filament_ids.json as - {"claims": [...], "successor": } — successor picked by the mode - rule over the vanished id's old claims — and the runtime follows those - chains (plus cross-island "hints" for ids Orca cannot retire, e.g. GF*) - on resolution miss. A retired id may never be used again for anything. - Vanished ids in a FOREIGN island's space (GF*) are released with a - hint instead of retired: the island's catalog owns them and may - legitimately (re)ship them. + that file (the maintainer gate). The effective-id resolution below is loader-faithful (PresetBundle.cpp load_vendor_configs_from_json): own filament_id key, else walk `inherits` within @@ -59,16 +47,7 @@ Run from anywhere: python3 scripts/assign_filament_ids.py rewrites a valid existing id; a no-op on a fully-idded tree --mint "Vendor/Type/Family" print the id that triple would mint; touches nothing - --update-snapshot regenerate the snapshot from the tree; retire vanished ids - with a mode-rule successor (--forget-never-shipped FILE - drops listed never-shipped ids from lineage instead) - --add-hint "OLD=NEW" - record a cross-island succession hint (standalone) - --retire "OLD=NEW" - retire a vanished non-island shipped id with an explicit - successor (standalone; for lineage the claim vote can no - longer see because the claims migrated while another - declarer kept the id alive) + --update-snapshot regenerate the snapshot from the tree --remint VENDOR re-derive VENDOR's declared ids from their triples and rewrite mismatches in place (repeatable) --drop-redundant-ids VENDOR @@ -96,10 +75,6 @@ FILAMENT_ID_LENGTH = 6 # base62 digits after the "OF" prefix -> 8 chars total SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__)) PROFILES_DIR = os.path.normpath(os.path.join(SCRIPTS_DIR, "..", "resources", "profiles")) SNAPSHOT_PATH = os.path.join(SCRIPTS_DIR, "filament_id_snapshot.json") -# The succession ledger ships with the app (resources/) so the runtime can -# follow retired->successor chains and cross-island hints on resolution miss. -RETIRED_PATH = os.path.join(PROFILES_DIR, "retired_filament_ids.json") -RETIRED_REL = "resources/profiles/retired_filament_ids.json" OFL = "OrcaFilamentLibrary" @@ -171,7 +146,7 @@ def generate_filament_id(filament_vendor, filament_type, family, salt=0): def mint_filament_id(filament_vendor, filament_type, family, taken): - """Mint the product's id, salting past any id in `taken` (existing + retired).""" + """Mint the product's id, salting past any id in `taken`.""" for salt in range(10000): candidate = generate_filament_id(filament_vendor, filament_type, family, salt) if candidate not in taken: @@ -193,7 +168,7 @@ def list_vendor_names(profiles_dir): """Vendor bundles = subdirectories with a matching .json index file. (Ignores stray non-bundle entries such as the tracked "user" directory, - which has no user.json index, and the retired_filament_ids.json ledger.) + which has no user.json index.) """ profiles_dir = str(profiles_dir) return sorted( @@ -250,7 +225,7 @@ def resolve_filament_id(name, filaments, ofl_filaments, seen=None, in_ofl=False, vendor is re-tried against the OFL map keyed by its direct parent name. skip_own ignores the first preset's own filament_id key (used to compute the - id its inherits chain would resolve WITHOUT the declaration — check 7b drift). + id its inherits chain would resolve WITHOUT the declaration — check 6b drift). Returns (filament_id or None, source, ofl_entry) where source is one of "own"/"inherited"/"missing"/"dangling"/"cycle" and ofl_entry is the name of @@ -379,7 +354,6 @@ def analyze_tree(profiles_dir): triples = {} # fid -> set of triples of its non-island declarers declarer_triples = [] # (vendor, rec, fid, triple) per non-island declarer family_triples = {} # (vendor, family) -> {triple: [declarer names]} - renamed_claims = {} # "Vendor/OldFamily" -> set of ids the rename now carries for vendor, filaments in vendors.items(): occurring = vendor_ids.setdefault(vendor, set()) @@ -413,19 +387,12 @@ def analyze_tree(profiles_dir): continue occurring.add(eff) ids.setdefault(eff, set()).add(f"{vendor}/{base_name(rec['name'])}") - # A renamed preset keeps voting under the names it used to have, so - # the id its old family carried retires to the id the rename minted. - renamed = rec.get("renamed_from") - for old in (renamed if isinstance(renamed, str) else "").split(";"): - if old.strip(): - renamed_claims.setdefault( - f"{vendor}/{base_name(old.strip())}", set()).add(eff) if rec.get("filament_id"): instantiated_with_id.append(f"{vendor}/{rec['name']}") if vendor != OFL and rec["ofl_entry"] and eff in ofl_declared: alias_candidates.append((vendor, rec, rec["ofl_entry"], False)) - # Alias hygiene (check 5): a vendor preset that rides an OFL family (its id + # Alias hygiene (check 4): a vendor preset that rides an OFL family (its id # resolves through OFL) is matched to the OFL preset by ALIAS (base name); # renaming it re-exposes the OFL preset and creates a live duplicate, empty # compatible_printers cannot claim any printer, and declaring its own @@ -452,7 +419,7 @@ def analyze_tree(profiles_dir): "empty compatible_printers cannot shadow the OFL preset anywhere", rec["file"])) - # Cross-bundle triple divergence (check 8, warning only): the same family + # Cross-bundle triple divergence (check 7, warning only): the same family # name declared in several bundles with different triples cannot converge # on one id until the divergence is fixed. name_bundles = {} @@ -468,7 +435,6 @@ def analyze_tree(profiles_dir): "vendors": vendors, "read_errors": read_errors, "ids": {fid: sorted(claims) for fid, claims in ids.items()}, - "renamed_claims": {claim: sorted(fids) for claim, fids in renamed_claims.items()}, "vendor_ids": vendor_ids, "declared_ids": declared_ids, "instantiated_with_id": sorted(instantiated_with_id), @@ -488,7 +454,7 @@ def analyze_tree(profiles_dir): # --------------------------------------------------------------------------- -# Snapshot / succession-ledger IO +# Snapshot IO # --------------------------------------------------------------------------- def snapshot_from_analysis(analysis): @@ -515,102 +481,13 @@ def load_snapshot(path): return data -def load_ledger(path): - """The shipped succession ledger: - {"retired": {id: {"claims": [...], "successor": id|None}}, "hints": {id: id}}. - Empty maps when the file is absent. - """ - if not os.path.exists(path): - return {"retired": {}, "hints": {}} - data = load_json(path) - return {"retired": data.get("retired", {}), "hints": data.get("hints", {})} - - -def load_retired(path): - """The ledger's retired map (see load_ledger); {} when the file is absent.""" - return load_ledger(path)["retired"] - - -def write_ledger(path, obj): +def write_snapshot(path, obj): """Deterministic serialization: sorted keys, indent 1, LF, trailing newline.""" with open(path, "w", encoding="utf-8", newline="\n") as f: json.dump(obj, f, indent=1, ensure_ascii=False, sort_keys=True) f.write("\n") -def claim_effective_ids(analysis): - """Reverse of analysis["ids"]: claim "Vendor/Family" -> set of effective ids. - - A preset's "renamed_from" names count as claims of that preset too, so an id - whose family was renamed still finds a successor in the claim vote. - """ - claim_ids = {} - for fid, claims in analysis["ids"].items(): - for claim in claims: - claim_ids.setdefault(claim, set()).add(fid) - for claim, fids in analysis.get("renamed_claims", {}).items(): - if claim not in claim_ids: - claim_ids[claim] = set(fids) - return claim_ids - - -def successor_by_mode(claims, claim_ids): - """Mode rule for a retiring id's successor: every old claim whose family - still exists votes for the family's current effective id(s); most votes - wins, ties break to the lexicographically smallest id, no votes -> None. - Returns (successor_or_None, vote_counts). - """ - counts = {} - for claim in claims: - for fid in claim_ids.get(claim, ()): - counts[fid] = counts.get(fid, 0) + 1 - if not counts: - return None, counts - best = max(counts.values()) - return min(f for f, n in counts.items() if n == best), counts - - -def migrate_retired_entries(old_retired, claim_ids): - """One-shot v3 schema migration: list-valued entries {id: [claims]} become - {id: {"claims": [...], "successor": }}; object entries - pass through untouched. - """ - migrated = {} - for fid, entry in old_retired.items(): - if isinstance(entry, dict): - migrated[fid] = entry - continue - successor, _counts = successor_by_mode(entry, claim_ids) - migrated[fid] = {"claims": sorted(entry), "successor": successor} - return migrated - - -def follow_succession(fid, seen, retired, hints, live): - """Follow retired->successor / hints->target forwarding starting at `fid`. - - Returns (status, node): "null" (chain ended on an heirless retirement), - "live" (reached a live tree id), "cycle", or "dead-end" (an id that is - neither live nor forwarded anywhere). `seen` pre-seeds the cycle guard. - """ - seen = set(seen) - cur = fid - while True: - if cur is None: - return "null", None - if cur in seen: - return "cycle", cur - seen.add(cur) - if cur in live: - return "live", cur - entry = retired.get(cur) - if entry is not None: - cur = entry.get("successor") if isinstance(entry, dict) else None - elif cur in hints: - cur = hints[cur] - else: - return "dead-end", cur - - # --------------------------------------------------------------------------- # Reserved namespaces # --------------------------------------------------------------------------- @@ -631,7 +508,7 @@ def reserved_space_desc(fid, owner): if owner: return f"owned by {owner}" if fid.startswith("QD_"): - return "Qidi device protocol; dissolved island — ids are retired, never declarable" + return "Qidi device protocol; composed by the device, never a preset id" return "reserved for user-custom presets" @@ -639,8 +516,7 @@ def reserved_space_desc(fid, owner): # Checks (imported and called tree-wide by orca_extra_profile_check.py) # --------------------------------------------------------------------------- -def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, - retired_path=RETIRED_PATH): +def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH): """Validate filament_id state across every vendor. Returns the error count. 1. Format: every id occurring in the tree (declared or effective) must be in @@ -651,23 +527,19 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, 3. Mint conformance: a non-island OF-format declaration must equal the mint of the declarer's triple or a salted iteration, unless that exact (id, triple) pair is grandfathered in the snapshot. - 4. Retired ids may never occur again. - 5. Alias hygiene: a vendor preset riding an OFL family must keep the OFL + 4. Alias hygiene: a vendor preset riding an OFL family must keep the OFL base name, claim printers via non-empty compatible_printers, and declare no filament_id key of its own. - 6. Reserved namespaces (GF* for BBL; QD_*/P-hex/"null" for nobody) must not + 5. Reserved namespaces (GF* for BBL; QD_*/P-hex/"null" for nobody) must not be claimed by other vendors, except claims grandfathered in the snapshot. - 7. Structure ratchet: (a) no NEW instantiated preset carries its own + 6. Structure ratchet: (a) no NEW instantiated preset carries its own filament_id key; (b) no NEW declared-vs-inherited id drift; (c) every instantiated filament resolves an effective id (a hard load error in C++). - 8. Triple integrity: (a) every non-island declarer resolves non-empty + 7. Triple integrity: (a) every non-island declarer resolves non-empty filament_vendor and filament_type (hard error, no grandfathering); (b) declarers of one (bundle, family) resolve identical triples, unless grandfathered in snapshot triple_exceptions; cross-bundle divergence on the same family name is a warning only. - 9. Succession integrity: retired successor chains terminate at a live id or - null without cycles; hint keys are live island-declared ids and hint - chains terminate live. """ _utf8_console() errors = 0 @@ -676,10 +548,6 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, if snapshot is None: print_error(f"filament_id snapshot not found at {snapshot_path}; {UPDATE_HINT}") return 1 - ledger = load_ledger(retired_path) - retired = ledger["retired"] - hints = ledger["hints"] - for msg in analysis["read_errors"]: print_error(msg) errors += 1 @@ -717,8 +585,8 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, for fid in sorted(snap_ids): if fid not in tree_ids: print_error( - f'filament_id stability: snapshot id "{fid}" vanished from the tree ' - f"(shipped ids retire through the succession ledger); {UPDATE_HINT}") + f'filament_id stability: snapshot id "{fid}" vanished from the tree; ' + f"{UPDATE_HINT}") errors += 1 continue for claim in snap_ids[fid]: @@ -761,16 +629,7 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, f"family root (or, for an intentionally kept id, {UPDATE_HINT})") errors += 1 - # -- 4. retired ids may never come back --------------------------------- - for vendor in sorted(analysis["vendor_ids"]): - for fid in sorted(analysis["vendor_ids"][vendor]): - if fid in retired: - print_error( - f'filament_id "{fid}" ({vendor}) is retired ' - f"({RETIRED_REL}) and may never be reused") - errors += 1 - - # -- 5. alias hygiene for presets riding OFL families -------------------- + # -- 4. alias hygiene for presets riding OFL families -------------------- exceptions = set(snapshot["alias_exceptions"]) for vendor, name, entry, reason, file in analysis["alias_violations"]: if f"{vendor}/{name}" in exceptions: @@ -782,7 +641,7 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, f"gets its own minted id") errors += 1 - # -- 6. reserved namespaces ---------------------------------------------- + # -- 5. reserved namespaces ---------------------------------------------- for fid in sorted(tree_ids): is_reserved, owner = reserved_space_owner(fid) if not is_reserved: @@ -799,7 +658,7 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, f"({space}) and must not be claimed by system presets of other vendors") errors += 1 - # -- 7. structure ratchet ------------------------------------------------- + # -- 6. structure ratchet ------------------------------------------------- grandfathered = set(snapshot["instantiated_with_id"]) for key in analysis["instantiated_with_id"]: if key not in grandfathered: @@ -827,7 +686,7 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, f'"{vendor}/{base_name(name)}": "{expected}", salted if taken)') errors += 1 - # -- 8. triple integrity --------------------------------------------------- + # -- 7. triple integrity --------------------------------------------------- for vendor, rec, fid, triple in sorted( analysis["declarer_triples"], key=lambda x: (x[0], x[1]["file"])): if triple[0] and triple[1]: @@ -860,62 +719,6 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, f"({detail}); bundles of one product converge on one id only once " f"their triples agree") - # -- 9. succession integrity (ledger-internal) ----------------------------- - live = set(tree_ids) - for fid in sorted(retired): - entry = retired[fid] - if (not isinstance(entry, dict) - or not isinstance(entry.get("claims", []), list) - or not (entry.get("successor") is None - or isinstance(entry.get("successor"), str))): - print_error( - f'retired entry "{fid}" ({RETIRED_REL}) is not of the form ' - f'{{"claims": [...], "successor": }}') - errors += 1 - continue - status, node = follow_succession(entry.get("successor"), {fid}, retired, hints, live) - if status == "cycle": - print_error( - f'succession chain of retired id "{fid}" cycles at "{node}" ({RETIRED_REL})') - errors += 1 - elif status == "dead-end": - print_error( - f'succession chain of retired id "{fid}" dead-ends at "{node}", which is ' - f"neither a live tree id nor retired/hinted ({RETIRED_REL})") - errors += 1 - for key in sorted(hints): - target = hints[key] - _is_reserved, island = reserved_space_owner(key) - if island is None: - print_error( - f'hint key "{key}" ({RETIRED_REL}) is not in an island id space ' - f"(GF*); non-island ids are retired with a successor instead") - errors += 1 - continue - if key in retired: - print_error( - f'hint key "{key}" ({RETIRED_REL}) is also retired; an id is either ' - f"retired (Orca-owned) or hinted (island-owned), never both") - errors += 1 - continue - # A hint key may be absent from the tree (released to its island); when - # live, only island vendors may declare it. - outsiders = sorted( - v for v in analysis["declared_ids"] - if key in analysis["declared_ids"][v] and not is_island_declaration(v, key)) - if outsiders: - print_error( - f'hint key "{key}" ({RETIRED_REL}) is declared by non-island vendor(s) ' - f"{outsiders}; hints apply only once no non-island vendor declares the " - f"id — re-mint those declarers first") - errors += 1 - status, node = follow_succession(target, {key}, retired, hints, live) - if status != "live": - print_error( - f'hint "{key}" -> "{target}" ({RETIRED_REL}) must chain-terminate at a ' - f"live tree id (got {status} at \"{node}\")") - errors += 1 - return errors @@ -924,18 +727,8 @@ def check_filament_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, # --------------------------------------------------------------------------- def update_snapshot(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, - retired_path=RETIRED_PATH, allow_shared_catalog=False, - forget_path=None): - """Regenerate the snapshot from the tree; retire ids that fully vanished. - - Vanished ids gain a succession-ledger entry whose successor is picked by - the mode rule over their old claims (None when the family died heirless); - vanished ids in a foreign island's space (GF*) are released with a - hint instead — the island catalog owns them, so they must stay mintable - there and must never be blocked by check 4. - --forget-never-shipped lists ids to drop from lineage instead of retiring - (never shipped => nothing outside the tree references them); succession - chains through them are spliced to their mode-rule successor. + allow_shared_catalog=False): + """Regenerate the snapshot from the tree. Refuses to sanction NEW reserved-namespace ids (or new claims on them) for non-owner vendors unless --allow-shared-catalog is passed. Idempotent: a @@ -948,21 +741,6 @@ def update_snapshot(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, old_snap = load_snapshot(snapshot_path) old_ids = old_snap["ids"] if old_snap else {} - forget = set() - if forget_path: - data = load_json(forget_path) - if not isinstance(data, list) or not all(isinstance(x, str) for x in data): - print_error(f"--forget-never-shipped expects a JSON list of ids: {forget_path}") - return 1 - forget = set(data) - still_live = sorted(forget & set(new_snap["ids"])) - if still_live: - for fid in still_live: - print_error( - f'--forget-never-shipped: id "{fid}" is still in the tree and can ' - f"neither be forgotten nor retired") - return 1 - # Gate: new reserved-namespace ids / claims for non-owner vendors. refusals = [] for fid, claims in sorted(new_snap["ids"].items()): @@ -990,72 +768,6 @@ def update_snapshot(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, f"maintainer-approved shared-catalog families") return 1 - # Retirement is permanent: refuse to sanction a tree that resurrects a - # retired id (check 4 would reject the resulting snapshot forever anyway). - ledger = load_ledger(retired_path) - retired = ledger["retired"] - reused = sorted(set(new_snap["ids"]) & set(retired)) - if reused: - for fid in reused: - print_error( - f'refusing to sanction retired filament_id "{fid}" ' - f"({RETIRED_REL} is append-only; retired ids may " - f"never be reused — mint a fresh id for the family instead)") - return 1 - - # Retire ids that fully vanished from the tree (append-only ledger), with a - # mode-rule successor so devices and user presets keep resolving. Vanished - # ids in a FOREIGN island's reserved space (GF*) are never retired — - # the island's catalog still owns them and may legitimately (re)ship them — - # they are RELEASED, with a succession hint so they keep resolving on miss. - claim_ids = claim_effective_ids(analysis) - newly_retired = [] - forgotten = [] - released = [] - for fid in sorted(old_ids): - if fid in new_snap["ids"]: - continue - if fid in forget: - forgotten.append(fid) - print_info(f'forgotten (never shipped): "{fid}" — no retirement entry') - continue - _is_reserved, island = reserved_space_owner(fid) - if island is not None: - successor, _counts = successor_by_mode(old_ids[fid], claim_ids) - if successor and fid not in ledger["hints"]: - ledger["hints"][fid] = successor - released.append(fid) - print_info( - f'released to the {island} island space: "{fid}"' - + (f' — hint -> "{ledger["hints"][fid]}"' if fid in ledger["hints"] - else " (no successor computable; --add-hint if one is needed)")) - continue - entry = retired.get(fid) - if not isinstance(entry, dict): - successor, counts = successor_by_mode(old_ids[fid], claim_ids) - if len(counts) > 1: - losers = sorted(set(counts) - {successor}) - print_warning( - f'retiring "{fid}": successor "{successor}" won the claim vote ' - f"({counts[successor]}); losing candidate(s): {', '.join(losers)}") - entry = {"claims": list(entry) if entry else [], "successor": successor} - entry["claims"] = sorted(set(entry["claims"]) | set(old_ids[fid])) - retired[fid] = entry - newly_retired.append(fid) - - # Splice succession chains through forgotten ids so they stay live. - spliced = [] - for fid in sorted(retired): - entry = retired[fid] - successor = entry.get("successor") if isinstance(entry, dict) else None - if successor in forget: - new_successor, _counts = successor_by_mode(old_ids.get(successor, []), claim_ids) - entry["successor"] = new_successor - spliced.append(fid) - print_info( - f'spliced succession: "{fid}" -> {json.dumps(new_successor)} ' - f'(through forgotten "{successor}")') - # Diff summary. added_ids = sorted(set(new_snap["ids"]) - set(old_ids)) removed_ids = sorted(set(old_ids) - set(new_snap["ids"])) @@ -1071,22 +783,12 @@ def update_snapshot(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, changed = new_snap != old_snap if changed: - write_ledger(snapshot_path, new_snap) - if newly_retired or spliced or released or not os.path.exists(retired_path): - write_ledger(retired_path, {"retired": retired, "hints": ledger["hints"]}) + write_snapshot(snapshot_path, new_snap) print_info(f"snapshot ids : {len(new_snap['ids'])} (+{len(added_ids)} / -{len(removed_ids)})") print_info(f"claims added : {added_claims}") print_info(f"claims removed : {removed_claims}") print_info(f"declared triples : {len(new_snap['triples'])}") - print_info(f"ids retired now : {len(newly_retired)}" + - (f" ({', '.join(newly_retired)})" if newly_retired else "")) - if forgotten: - print_info(f"ids forgotten : {len(forgotten)} ({', '.join(forgotten)})") - if released: - print_info(f"ids released : {len(released)} ({', '.join(released)})") - if spliced: - print_info(f"chains spliced : {len(spliced)} ({', '.join(spliced)})") for section in ("instantiated_with_id", "id_overrides", "alias_exceptions", "triple_exceptions"): before, after = len(old_snap.get(section, [])), len(new_snap[section]) @@ -1098,125 +800,6 @@ def update_snapshot(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, return 0 -def add_hints(pairs, profiles_dir=PROFILES_DIR, retired_path=RETIRED_PATH): - """--add-hint "OLD=NEW": forward an island-space id to a live id. - - OLD must sit in an island id space (GF*), must not be retired, and — - when live in the tree — may be declared only by island vendors (Orca cannot - retire it). An absent OLD is fine: island catalogs own ids Orca never - shipped or has released. NEW must be live in the tree. All pairs validate - before anything is written. Returns 0 on success. - """ - _utf8_console() - analysis = analyze_tree(profiles_dir) - errors = 0 - for msg in analysis["read_errors"]: - print_error(msg) - errors += 1 - ledger = load_ledger(retired_path) - live = set(analysis["ids"]) - parsed = [] - for pair in pairs: - if "=" not in pair: - print_error(f'--add-hint expects "OLD=NEW", got "{pair}"') - errors += 1 - continue - old, new = pair.split("=", 1) - _is_reserved, island = reserved_space_owner(old) - outsiders = sorted( - v for v in analysis["declared_ids"] - if old in analysis["declared_ids"][v] and not is_island_declaration(v, old)) - if old in ledger["retired"]: - print_error(f'--add-hint: "{old}" is retired; its succession entry already forwards it') - errors += 1 - elif island is None: - print_error( - f'--add-hint: "{old}" is not in an island id space (GF*); ' - f"non-island ids are retired with a successor by --update-snapshot") - errors += 1 - elif outsiders: - print_error( - f'--add-hint: "{old}" is declared by non-island vendor(s) {outsiders}; ' - f"hints apply only once no non-island vendor declares the id — re-mint " - f"those declarers first") - errors += 1 - elif new not in live: - print_error(f'--add-hint: target "{new}" is not a live tree id') - errors += 1 - else: - parsed.append((old, new)) - if errors: - return 1 - for old, new in parsed: - ledger["hints"][old] = new - print_info(f'hint added: "{old}" -> "{new}"') - write_ledger(retired_path, {"retired": ledger["retired"], "hints": ledger["hints"]}) - return 0 - - -def retire_ids(pairs, profiles_dir=PROFILES_DIR, retired_path=RETIRED_PATH): - """--retire "OLD=NEW": retire a vanished non-island id with an explicit successor. - - For shipped ids whose lineage the claim vote can no longer see: the id - vanished from the tree in an earlier phase's shadow — another declarer kept - it alive while its claims migrated — so no --update-snapshot run ever saw - the id vanish while its claims could still vote. OLD must be absent from - the tree (declared or effective) and outside every reserved id space - (island ids forward via --add-hint; user-custom ids are never system ids), - and must not already be retired or hinted. NEW must be a live tree id. - Appends {"claims": [], "successor": NEW}. All pairs validate before - anything is written. Returns 0 on success. - """ - _utf8_console() - analysis = analyze_tree(profiles_dir) - errors = 0 - for msg in analysis["read_errors"]: - print_error(msg) - errors += 1 - ledger = load_ledger(retired_path) - live = set(analysis["ids"]) - declared_anywhere = set() - for vids in analysis["declared_ids"].values(): - declared_anywhere |= set(vids) - parsed = [] - for pair in pairs: - if "=" not in pair: - print_error(f'--retire expects "OLD=NEW", got "{pair}"') - errors += 1 - continue - old, new = pair.split("=", 1) - is_reserved, _island = reserved_space_owner(old) - if old in ledger["retired"]: - print_error(f'--retire: "{old}" is already retired') - errors += 1 - elif old in ledger["hints"]: - print_error(f'--retire: "{old}" is a hint key; it already forwards') - errors += 1 - elif is_reserved: - print_error( - f'--retire: "{old}" sits in a reserved id space; island ids ' - f"forward via --add-hint, user-custom ids are never retired") - errors += 1 - elif old in live or old in declared_anywhere: - print_error( - f'--retire: "{old}" still lives in the tree; re-mint or delete ' - f"its declarers first (an id that vanishes normally gets its " - f"successor voted by --update-snapshot)") - errors += 1 - elif new not in live: - print_error(f'--retire: successor "{new}" is not a live tree id') - errors += 1 - else: - parsed.append((old, new)) - if errors: - return 1 - for old, new in parsed: - ledger["retired"][old] = {"claims": [], "successor": new} - print_info(f'retired: "{old}" -> "{new}"') - write_ledger(retired_path, {"retired": ledger["retired"], "hints": ledger["hints"]}) - return 0 - - # --------------------------------------------------------------------------- # Byte-preserving profile edits # --------------------------------------------------------------------------- @@ -1313,8 +896,7 @@ def remove_filament_id(path, old_id): # Default run: mint + insert ids for id-less families # --------------------------------------------------------------------------- -def assign_missing_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, - retired_path=RETIRED_PATH): +def assign_missing_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH): """Mint + insert ids for id-less families. Never rewrites a valid existing id. A family = (vendor, base name) group over instantiated filaments with no @@ -1346,9 +928,9 @@ def assign_missing_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, "nothing to do (0 files changed)") return 0, errors - # Ids already spoken for: whole tree (declared or effective) + ledgers. + # Ids already spoken for: the whole tree (declared or effective) + snapshot. snapshot = load_snapshot(snapshot_path) or {"ids": {}} - taken = set(snapshot["ids"]) | set(load_retired(retired_path)) + taken = set(snapshot["ids"]) for occurring in analysis["vendor_ids"].values(): taken |= occurring @@ -1427,7 +1009,7 @@ def assign_missing_ids(profiles_dir=PROFILES_DIR, snapshot_path=SNAPSHOT_PATH, # --remint / --drop-redundant-ids (v3.1/v3.2 migration modes) # --------------------------------------------------------------------------- -def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR, retired_path=RETIRED_PATH): +def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR): """Re-derive every non-island declared id in the given vendors from its triple; rewrite mismatching declarations in place (byte-preserving). Never touches the snapshot — run --update-snapshot afterwards and review the diff. @@ -1435,8 +1017,8 @@ def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR, retired_path=RETIRED_ A declaration already equal to ANY salt iteration of its own triple is mint-conformant (check 3) and left alone — deliberate salt splits (distinct presets of one product kept apart for per-printer AMS matching) survive. - A candidate id is blocked when it is a retired key, a hints key, or occurs - in the tree with any triple set other than exactly {T}. Equal-triple reuse + A candidate id is blocked when it occurs in the tree with any triple set + other than exactly {T}. Equal-triple reuse is convergence: the same product must end up under the same id everywhere. Returns (rewritten, errors). """ @@ -1455,8 +1037,6 @@ def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR, retired_path=RETIRED_ print_error(f"--remint: unknown vendor {v!r}") return 0, errors + len(unknown) - ledger = load_ledger(retired_path) - blocked_keys = set(ledger["retired"]) | set(ledger["hints"]) occurring = set() for vids in analysis["vendor_ids"].values(): occurring |= vids @@ -1470,8 +1050,6 @@ def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR, retired_path=RETIRED_ return assigned[triple] for salt in range(10000): cand = generate_filament_id(*triple, salt=salt) - if cand in blocked_keys: - continue if cand in occurring and triple_sets.get(cand, set()) != {triple}: continue if run_taken.get(cand, triple) != triple: @@ -1570,27 +1148,13 @@ def main(argv=None): help="print the id the (filament_vendor, filament_type, " "family name) triple would mint; touches nothing") parser.add_argument("--update-snapshot", action="store_true", - help="regenerate scripts/filament_id_snapshot.json from the " - "tree; ids that fully vanished are retired with a " - "mode-rule successor") + help="regenerate scripts/filament_id_snapshot.json from " + "the tree") parser.add_argument("--check", action="store_true", help="run the filament_id checks; exit nonzero on errors") parser.add_argument("--allow-shared-catalog", action="store_true", help="with --update-snapshot: allow sanctioning new claims " "on reserved-namespace ids for non-owner vendors") - parser.add_argument("--forget-never-shipped", metavar="JSON_FILE", - help="with --update-snapshot: vanished ids listed in the file " - "(JSON list) are dropped without a retirement entry and " - "succession chains are spliced through them; refused if " - "a listed id is still in the tree") - parser.add_argument("--add-hint", metavar='"OLD=NEW"', action="append", - help="record a cross-island succession hint (island-owned " - "live id -> live id) in the shipped ledger; standalone, " - "repeatable") - parser.add_argument("--retire", metavar='"OLD=NEW"', action="append", - help="retire a vanished non-island shipped id with an " - "explicit successor (for lineage the claim vote can " - "no longer see); standalone, repeatable") parser.add_argument("--remint", metavar="VENDOR", action="append", help="re-derive VENDOR's declared filament_ids from their " "triples and rewrite mismatches in place; repeatable") @@ -1602,35 +1166,22 @@ def main(argv=None): help="profiles directory (default: resources/profiles)") args = parser.parse_args(argv) - if args.forget_never_shipped and not args.update_snapshot: - parser.error("--forget-never-shipped requires --update-snapshot") if args.mint: parts = args.mint.split("/", 2) if len(parts) != 3 or not all(parts): parser.error('--mint expects "filament_vendor/filament_type/family_name" ' - "with all three components non-empty (check 8 rejects empty " + "with all three components non-empty (check 7 rejects empty " "vendor/type in the tree)") snapshot = load_snapshot(SNAPSHOT_PATH) or {"ids": {}} - taken = set(snapshot["ids"]) | set(load_retired(RETIRED_PATH)) + taken = set(snapshot["ids"]) print(mint_filament_id(parts[0], parts[1], parts[2], taken)) return 0 - if args.add_hint: - if (args.update_snapshot or args.check or args.remint - or args.drop_redundant_ids or args.retire): - parser.error("--add-hint runs standalone") - return add_hints(args.add_hint, args.profiles, RETIRED_PATH) - - if args.retire: - if args.update_snapshot or args.check or args.remint or args.drop_redundant_ids: - parser.error("--retire runs standalone") - return retire_ids(args.retire, args.profiles, RETIRED_PATH) - if args.remint: if args.update_snapshot or args.check or args.drop_redundant_ids: parser.error("--remint cannot be combined with other modes") - _changed, errors = remint_vendors(args.remint, args.profiles, RETIRED_PATH) + _changed, errors = remint_vendors(args.remint, args.profiles) return 1 if errors else 0 if args.drop_redundant_ids: @@ -1640,19 +1191,18 @@ def main(argv=None): return 1 if errors else 0 if args.update_snapshot: - return update_snapshot(args.profiles, SNAPSHOT_PATH, RETIRED_PATH, - allow_shared_catalog=args.allow_shared_catalog, - forget_path=args.forget_never_shipped) + return update_snapshot(args.profiles, SNAPSHOT_PATH, + allow_shared_catalog=args.allow_shared_catalog) if args.check: - errors = check_filament_ids(args.profiles, SNAPSHOT_PATH, RETIRED_PATH) + errors = check_filament_ids(args.profiles, SNAPSHOT_PATH) if errors: print_error(f"filament_id check: {errors} error(s)") return 1 print_success("filament_id check: no errors") return 0 - _changed, errors = assign_missing_ids(args.profiles, SNAPSHOT_PATH, RETIRED_PATH) + _changed, errors = assign_missing_ids(args.profiles, SNAPSHOT_PATH) return 1 if errors else 0 diff --git a/scripts/check_profile.ps1 b/scripts/check_profile.ps1 index 9c9bb6c506..66ed33c31d 100644 --- a/scripts/check_profile.ps1 +++ b/scripts/check_profile.ps1 @@ -187,7 +187,7 @@ $ProfilesDir = (Resolve-Path -LiteralPath $ProfilesDir).Path # validator compares the .json stem case-sensitively, while Windows' case-insensitive # filesystem would let "creality" pass a path test and then match no vendor. A vendor is a # .json with a sibling directory; that pair is also what tells one apart from -# blacklist.json and retired_filament_ids.json, which sit in the same folder. +# blacklist.json, which sits in the same folder. if ($Vendor) { $found = $false $suggestion = '' diff --git a/scripts/check_profile.sh b/scripts/check_profile.sh index cb56258c9f..51c8599b60 100755 --- a/scripts/check_profile.sh +++ b/scripts/check_profile.sh @@ -138,7 +138,7 @@ PROFILES_DIR="$(cd -- "${PROFILES_DIR}" && pwd)" # validator compares the .json stem case-sensitively, while a case-insensitive filesystem # (macOS, Windows) would let "creality" pass a file test and then match no vendor. A vendor is a # .json with a sibling / directory; that pair is also what tells one apart from -# blacklist.json and retired_filament_ids.json, which sit in the same folder. +# blacklist.json, which sits in the same folder. if [ -n "${VENDOR}" ]; then wanted="$(printf '%s' "${VENDOR}" | tr '[:upper:]' '[:lower:]')" found="" diff --git a/scripts/tests/test_filament_id.py b/scripts/tests/test_filament_id.py index 41df9c582e..ef61992784 100644 --- a/scripts/tests/test_filament_id.py +++ b/scripts/tests/test_filament_id.py @@ -54,14 +54,13 @@ def preset(name, filament_id=None, inherits=None, instantiation=True, class SyntheticTree: - """A throwaway resources/profiles-shaped directory plus ledger paths.""" + """A throwaway resources/profiles-shaped directory plus a snapshot path.""" def __init__(self): self.dir = tempfile.mkdtemp(prefix="filament_id_test_") self.profiles = os.path.join(self.dir, "profiles") os.makedirs(self.profiles) self.snapshot = os.path.join(self.dir, "filament_id_snapshot.json") - self.retired = os.path.join(self.dir, "retired_filament_ids.json") def cleanup(self): shutil.rmtree(self.dir, ignore_errors=True) @@ -109,37 +108,31 @@ class SyntheticTree: with open(idx_path, "w", encoding="utf-8") as f: json.dump(index, f, indent=4, ensure_ascii=False) - def write_ledger(self, retired=None, hints=None): - afi.write_ledger(self.retired, {"retired": retired or {}, - "hints": hints or {}}) - # -- pipeline wrappers --------------------------------------------------- - def update_snapshot(self, allow_shared_catalog=False, forget_path=None): + def update_snapshot(self, allow_shared_catalog=False): buf = io.StringIO() with contextlib.redirect_stdout(buf): - rc = afi.update_snapshot(self.profiles, self.snapshot, self.retired, - allow_shared_catalog=allow_shared_catalog, - forget_path=forget_path) + rc = afi.update_snapshot(self.profiles, self.snapshot, + allow_shared_catalog=allow_shared_catalog) return rc, buf.getvalue() def check(self): buf = io.StringIO() with contextlib.redirect_stdout(buf): - errors = afi.check_filament_ids(self.profiles, self.snapshot, self.retired) + errors = afi.check_filament_ids(self.profiles, self.snapshot) return errors, buf.getvalue() def assign(self): buf = io.StringIO() with contextlib.redirect_stdout(buf): - changed, errors = afi.assign_missing_ids(self.profiles, self.snapshot, - self.retired) + changed, errors = afi.assign_missing_ids(self.profiles, self.snapshot) return changed, errors, buf.getvalue() def remint(self, vendors): buf = io.StringIO() with contextlib.redirect_stdout(buf): - changed, errors = afi.remint_vendors(vendors, self.profiles, self.retired) + changed, errors = afi.remint_vendors(vendors, self.profiles) return changed, errors, buf.getvalue() def drop_redundant(self, vendor): @@ -148,19 +141,6 @@ class SyntheticTree: dropped, errors = afi.drop_redundant_ids(vendor, self.profiles) return dropped, errors, buf.getvalue() - def add_hint(self, pairs): - buf = io.StringIO() - with contextlib.redirect_stdout(buf): - rc = afi.add_hints(pairs, self.profiles, self.retired) - return rc, buf.getvalue() - - def retire(self, pairs): - buf = io.StringIO() - with contextlib.redirect_stdout(buf): - rc = afi.retire_ids(pairs, self.profiles, self.retired) - return rc, buf.getvalue() - - def make_clean_tree(): """Baseline tree: OFL base+generic, a vendor family, a clean tuned generic.""" t = SyntheticTree() @@ -196,7 +176,7 @@ class SyntheticTreeCase(unittest.TestCase): class TestMint(unittest.TestCase): def test_namespace_literal(self): - # Frozen: derived from the setting_id namespace; baked into the ledger. + # Frozen: derived from the setting_id namespace; baked into the snapshot. self.assertEqual(afi.FILAMENT_ID_NAMESPACE, uuid.UUID("c4d3ff49-4c32-5534-a3e3-00894157ab97")) @@ -401,7 +381,7 @@ class TestTripleResolution(unittest.TestCase): class TestReservedSpaces(unittest.TestCase): def test_owners(self): self.assertEqual(afi.reserved_space_owner("GFL99"), (True, "BBL")) - # dissolved island (plan v4): reserved, but no vendor may declare it + # Qidi device protocol: reserved, but no vendor may declare it self.assertEqual(afi.reserved_space_owner("QD_X4_PLA"), (True, None)) self.assertEqual(afi.reserved_space_owner("P1234abc"), (True, None)) self.assertEqual(afi.reserved_space_owner("pAbCdEf1"), (True, None)) # case-insensitive @@ -412,7 +392,7 @@ class TestReservedSpaces(unittest.TestCase): def test_island_declarations(self): self.assertTrue(afi.is_island_declaration("BBL", "GFL99")) self.assertTrue(afi.is_island_declaration("BBL", "OF5CgdDq")) - # dissolved island (plan v4): Qidi declarations mint like any other + # Qidi presets mint like any other vendor's: self.assertFalse(afi.is_island_declaration("Qidi", "QD_X4_PLA")) self.assertFalse(afi.is_island_declaration("Qidi", "OF5CgdDq")) self.assertFalse(afi.is_island_declaration("VendorA", "GFL99")) @@ -503,15 +483,7 @@ class TestChecks(SyntheticTreeCase): errors, out = self.t.check() self.assertEqual(errors, 0, out) - def test_check4_retired_id_reuse(self): - self.t.write_ledger(retired={"AX01": {"claims": ["VendorA/APLA"], - "successor": None}}) - errors, out = self.t.check() - self.assertGreater(errors, 0) - self.assertIn("retired", out) - self.assertIn("AX01", out) - - def test_check5_renamed_tuned_generic(self): + def test_check4_renamed_tuned_generic(self): self.t.write_preset("VendorA", preset("Tuned PLA @P1", inherits="Generic PLA @System", compatible_printers=["P1"])) @@ -520,7 +492,7 @@ class TestChecks(SyntheticTreeCase): self.assertIn("rename re-exposes the OFL preset", out) self.assertIn("Tuned PLA @P1", out) - def test_check5_empty_compatible_printers(self): + def test_check4_empty_compatible_printers(self): self.t.write_preset("VendorA", preset("Generic PLA @P2", inherits="Generic PLA @System", compatible_printers=[])) @@ -528,7 +500,7 @@ class TestChecks(SyntheticTreeCase): self.assertGreater(errors, 0) self.assertIn("cannot shadow the OFL preset", out) - def test_check5_own_id_key_on_ofl_rider(self): + def test_check4_own_id_key_on_ofl_rider(self): # A vendor root that declares its own id while inheriting an OFL family # forks the family off the catalog id. self.t.write_preset("VendorA", preset("GPLA @base", filament_id="AX77", @@ -540,7 +512,7 @@ class TestChecks(SyntheticTreeCase): self.assertGreater(errors, 0) self.assertIn('declares its own filament_id "AX77"', out) - def test_check5_applies_to_non_generic_ofl_families(self): + def test_check4_applies_to_non_generic_ofl_families(self): fid = afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA") self.t.write_preset(OFL, preset("poly_pla_base", filament_id=fid, instantiation=False, @@ -559,7 +531,7 @@ class TestChecks(SyntheticTreeCase): self.assertIn("rename re-exposes the OFL preset", out) self.assertIn("PolyX PLA @P1", out) - def test_check5_exception_is_grandfathered(self): + def test_check4_exception_is_grandfathered(self): self.t.write_preset("VendorA", preset("Tuned PLA @P1", inherits="Generic PLA @System", compatible_printers=["P1"])) @@ -568,9 +540,9 @@ class TestChecks(SyntheticTreeCase): errors, out = self.t.check() self.assertEqual(errors, 0, out) - def test_check6_reserved_namespace_claims(self): + def test_check5_reserved_namespace_claims(self): for fid, marker in [("GFX99", "owned by BBL"), - ("QD_X_PLA", "dissolved island"), + ("QD_X_PLA", "composed by the device"), ("P1a2b3c4", "user-custom"), ("null", "user-custom")]: with self.subTest(fid=fid): @@ -586,7 +558,7 @@ class TestChecks(SyntheticTreeCase): self.assertIn("reserved id space", out) self.assertIn(marker, out) - def test_check7a_instantiated_preset_with_own_key(self): + def test_check6a_instantiated_preset_with_own_key(self): self.t.write_preset("VendorA", preset("APLA @P1", filament_id="AX01", inherits="APLA @base", compatible_printers=["P1 0.4 nozzle"]), @@ -595,7 +567,7 @@ class TestChecks(SyntheticTreeCase): self.assertGreater(errors, 0) self.assertIn("declares its own filament_id key", out) - def test_check7b_declared_vs_inherited_drift(self): + def test_check6b_declared_vs_inherited_drift(self): self.t.write_preset("VendorA", preset("APLA @P1", filament_id="AX02", inherits="APLA @base", compatible_printers=["P1 0.4 nozzle"]), @@ -604,7 +576,7 @@ class TestChecks(SyntheticTreeCase): self.assertGreater(errors, 0) self.assertIn('declares filament_id "AX02" but its inherits chain resolves "AX01"', out) - def test_check7c_unresolvable_instantiated_filament(self): + def test_check6c_unresolvable_instantiated_filament(self): self.t.write_preset("VendorA", preset("DNEW @P1", compatible_printers=["P1"])) errors, out = self.t.check() self.assertGreater(errors, 0) @@ -618,8 +590,8 @@ class TestChecks(SyntheticTreeCase): self.assertIn("snapshot not found", out) -class TestCheck8(SyntheticTreeCase): - def test_8a_empty_vendor_is_hard_error(self): +class TestCheck7(SyntheticTreeCase): + def test_7a_empty_vendor_is_hard_error(self): fid = afi.generate_filament_id("", "PLA", "NVPLA") self.t.write_preset("VendorA", preset("NVPLA @base", filament_id=fid, instantiation=False, @@ -637,7 +609,7 @@ class TestCheck8(SyntheticTreeCase): self.assertEqual(errors, 1, out) self.assertIn("resolves empty filament_vendor", out) - def test_8b_divergent_family_triples(self): + def test_7b_divergent_family_triples(self): id1 = afi.generate_filament_id("MV", "PLA", "MPLA") id2 = afi.generate_filament_id("MV", "PETG", "MPLA") self.t.write_preset("VendorA", preset("MPLA @base1", filament_id=id1, @@ -660,7 +632,7 @@ class TestCheck8(SyntheticTreeCase): errors, out = self.t.check() self.assertEqual(errors, 0, out) - def test_8_cross_bundle_divergence_is_warning_only(self): + def test_7_cross_bundle_divergence_is_warning_only(self): fid = afi.generate_filament_id("BV", "PETG", "APLA") self.t.add_vendor("VendorB", [ preset("APLA @base", filament_id=fid, instantiation=False, @@ -677,125 +649,6 @@ class TestCheck8(SyntheticTreeCase): self.assertIn('"APLA"', out) -class TestCheck9(SyntheticTreeCase): - def test_ok_chain_null_and_live(self): - self.t.write_ledger(retired={ - "R1": {"claims": ["VendorA/Gone1"], "successor": "AX01"}, - "R2": {"claims": ["VendorA/Gone2"], "successor": "R1"}, - "R3": {"claims": ["VendorA/Gone3"], "successor": None}, - }) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - - def test_cycle_is_an_error(self): - self.t.write_ledger(retired={ - "C1": {"claims": [], "successor": "C2"}, - "C2": {"claims": [], "successor": "C1"}, - }) - errors, out = self.t.check() - self.assertEqual(errors, 2, out) - self.assertIn("cycles", out) - - def test_dead_end_is_an_error(self): - self.t.write_ledger(retired={ - "D1": {"claims": [], "successor": "OFnope99"}, - }) - errors, out = self.t.check() - self.assertEqual(errors, 1, out) - self.assertIn("dead-ends", out) - self.assertIn("OFnope99", out) - - def test_hint_key_must_be_in_island_space(self): - self.t.write_ledger(hints={"AX01": "OGFL99"}) - errors, out = self.t.check() - self.assertEqual(errors, 1, out) - self.assertIn("not in an island id space", out) - - def test_hint_key_may_be_absent_island_id(self): - # Released / never-shipped island-catalog ids are valid hint keys. - self.t.write_ledger(hints={"GFXX99": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - - def test_hint_key_must_not_be_retired_too(self): - self.t.write_ledger(retired={"GFXX99": {"claims": [], "successor": "AX01"}}, - hints={"GFXX99": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 1, out) - self.assertIn("never both", out) - - def test_live_hint_key_with_non_island_declarer_is_an_error(self): - self.t.write_preset("VendorA", preset( - "GPLA @base", filament_id="GFZZ01", instantiation=False, - filament_vendor="Generic", filament_type="PLA")) - self.t.write_preset("VendorA", preset( - "GPLA @P1", inherits="GPLA @base", - compatible_printers=["P1 0.4 nozzle"])) - rc, _out = self.t.update_snapshot(allow_shared_catalog=True) - self.assertEqual(rc, 0) - self.t.write_ledger(hints={"GFZZ01": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 1, out) - self.assertIn("re-mint those declarers first", out) - - def test_island_hint_ok_and_value_must_chain_live(self): - self.t.add_vendor("BBL", [ - preset("BPLA @base", filament_id="GFB01", instantiation=False), - preset("BPLA @X1C", inherits="BPLA @base", - compatible_printers=["X1C 0.4 nozzle"]), - ]) - rc, _out = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.t.write_ledger(hints={"GFB01": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - # A hint chain ending on a null retirement is dead for the runtime. - self.t.write_ledger(retired={"R9": {"claims": [], "successor": None}}, - hints={"GFB01": "R9"}) - errors, out = self.t.check() - self.assertEqual(errors, 1, out) - self.assertIn("chain-terminate at a live tree id", out) - - -# --------------------------------------------------------------------------- -# successor mode rule + ledger schema migration -# --------------------------------------------------------------------------- - -class TestSuccessorMode(unittest.TestCase): - def test_majority_wins(self): - claim_ids = {"V/A": {"ID1"}, "V/B": {"ID1"}, "V/C": {"ID2"}} - succ, counts = afi.successor_by_mode(["V/A", "V/B", "V/C"], claim_ids) - self.assertEqual(succ, "ID1") - self.assertEqual(counts, {"ID1": 2, "ID2": 1}) - - def test_tie_breaks_lexicographically(self): - claim_ids = {"V/A": {"IDB"}, "V/B": {"IDA"}} - succ, counts = afi.successor_by_mode(["V/A", "V/B"], claim_ids) - self.assertEqual(succ, "IDA") - self.assertEqual(counts, {"IDA": 1, "IDB": 1}) - - def test_no_surviving_family_means_null(self): - self.assertEqual(afi.successor_by_mode(["V/GONE"], {}), (None, {})) - self.assertEqual(afi.successor_by_mode([], {"V/A": {"ID1"}}), (None, {})) - - -class TestLedgerMigration(SyntheticTreeCase): - def test_backfill_successors_via_mode_rule(self): - analysis = afi.analyze_tree(self.t.profiles) - claim_ids = afi.claim_effective_ids(analysis) - old = { - "X9": ["VendorA/APLA"], # family lives on - "Y9": ["VendorA/GONE"], # family died - "Z9": {"claims": ["kept"], "successor": "AX01"}, # already migrated - } - migrated = afi.migrate_retired_entries(old, claim_ids) - self.assertEqual(migrated["X9"], - {"claims": ["VendorA/APLA"], "successor": "AX01"}) - self.assertEqual(migrated["Y9"], - {"claims": ["VendorA/GONE"], "successor": None}) - self.assertEqual(migrated["Z9"], old["Z9"]) - - # --------------------------------------------------------------------------- # --update-snapshot # --------------------------------------------------------------------------- @@ -820,108 +673,6 @@ class TestUpdateSnapshot(SyntheticTreeCase): self.assertEqual(snap["triples"]["OGFL99"], [["Generic", "PLA", "fdm_pla"]]) self.assertEqual(snap["triple_exceptions"], []) - def test_vanished_island_id_released_with_hint_not_retired(self): - self.t.write_preset("VendorA", preset( - "GPLA @base", filament_id="GFZZ01", instantiation=False, - filament_vendor="Generic", filament_type="PLA")) - self.t.write_preset("VendorA", preset( - "GPLA @P1", inherits="GPLA @base", - compatible_printers=["P1 0.4 nozzle"])) - rc, _out = self.t.update_snapshot(allow_shared_catalog=True) - self.assertEqual(rc, 0) - # The family re-mints away from the island id: released, not retired. - self.t.write_preset("VendorA", preset( - "GPLA @base", filament_id="OFaaaaaa", instantiation=False, - filament_vendor="Generic", filament_type="PLA"), register=False) - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.assertIn("released to the BBL island space", out) - ledger = afi.load_ledger(self.t.retired) - self.assertNotIn("GFZZ01", ledger["retired"]) - self.assertEqual(ledger["hints"].get("GFZZ01"), "OFaaaaaa") - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - # The island may legitimately (re)ship a released id later. - self.t.add_vendor("BBL", [ - preset("BPLA @base", filament_id="GFZZ01", instantiation=False), - preset("BPLA @X1C", inherits="BPLA @base", - compatible_printers=["X1C 0.4 nozzle"]), - ]) - rc, _out = self.t.update_snapshot() - self.assertEqual(rc, 0) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - - def test_vanished_id_is_retired_with_null_successor(self): - self.t.remove_preset("VendorA", "APLA @base") - self.t.remove_preset("VendorA", "APLA @P1") - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.assertIn("AX01", out) - retired = load_json_file(self.t.retired)["retired"] - self.assertEqual(retired, - {"AX01": {"claims": ["VendorA/APLA"], "successor": None}}) - snap = load_json_file(self.t.snapshot) - self.assertNotIn("AX01", snap["ids"]) - self.assertNotIn("AX01", snap["triples"]) - # append-only + idempotent: a second run keeps the ledger intact - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.assertIn("nothing changed", out) - self.assertEqual(retired, load_json_file(self.t.retired)["retired"]) - # ... and a retired id may never be minted again - self.assertNotIn(afi.mint_filament_id("AVendor", "PLA", "APLA", - set(retired)), retired) - - def test_vanished_id_successor_follows_surviving_family(self): - # The family lives on under a new id (a re-mint): the mode rule finds it. - self.t.write_preset("VendorA", preset("APLA @base", filament_id="AXNEW", - instantiation=False, - filament_vendor="AVendor", - filament_type="PLA"), - register=False) - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 0) - retired = load_json_file(self.t.retired)["retired"] - self.assertEqual(retired["AX01"], - {"claims": ["VendorA/APLA"], "successor": "AXNEW"}) - - def test_multi_candidate_retirement_warns_and_ties_break(self): - self.t.write_preset("VendorA", preset("SHRD @base", filament_id="SH01", - instantiation=False, - filament_vendor="SV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("CH1 @P1", inherits="SHRD @base", - compatible_printers=["P1"])) - self.t.write_preset("VendorA", preset("CH2 @P1", inherits="SHRD @base", - compatible_printers=["P1"])) - rc, _out = self.t.update_snapshot() - self.assertEqual(rc, 0) - # Split the two families onto their own roots and drop the shared id. - self.t.write_preset("VendorA", preset("CH1 @base", filament_id="SA01", - instantiation=False, - filament_vendor="SV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("CH2 @base", filament_id="SB01", - instantiation=False, - filament_vendor="SV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("CH1 @P1", inherits="CH1 @base", - compatible_printers=["P1"]), - register=False) - self.t.write_preset("VendorA", preset("CH2 @P1", inherits="CH2 @base", - compatible_printers=["P1"]), - register=False) - self.t.remove_preset("VendorA", "SHRD @base") - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.assertIn("[WARNING]", out) - self.assertIn("SB01", out) # the losing candidate is named - retired = load_json_file(self.t.retired)["retired"] - self.assertEqual(retired["SH01"]["successor"], "SA01") - self.assertEqual(retired["SH01"]["claims"], - ["VendorA/CH1", "VendorA/CH2"]) - def test_refuses_new_reserved_namespace_claims(self): self.t.write_preset("VendorA", preset("CNEW @base", filament_id="GFX99", instantiation=False, @@ -942,181 +693,6 @@ class TestUpdateSnapshot(SyntheticTreeCase): self.assertEqual(snap["ids"]["GFX99"], ["VendorA/CNEW"]) -class TestForgetNeverShipped(SyntheticTreeCase): - def forget_file(self, ids): - path = os.path.join(self.t.dir, "forget.json") - with open(path, "w", encoding="utf-8") as f: - json.dump(ids, f) - return path - - def test_refuses_when_listed_id_is_live(self): - with open(self.t.snapshot, "rb") as f: - before = f.read() - rc, out = self.t.update_snapshot(forget_path=self.forget_file(["AX01"])) - self.assertEqual(rc, 1) - self.assertIn("still in the tree", out) - with open(self.t.snapshot, "rb") as f: - self.assertEqual(f.read(), before) - - def test_forgotten_id_is_not_retired(self): - self.t.write_preset("VendorA", preset("ZN @base", filament_id="Y999", - instantiation=False, - filament_vendor="ZV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("ZN @P1", inherits="ZN @base", - compatible_printers=["P1"])) - rc, _out = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.t.remove_preset("VendorA", "ZN @base") - self.t.remove_preset("VendorA", "ZN @P1") - rc, out = self.t.update_snapshot(forget_path=self.forget_file(["Y999"])) - self.assertEqual(rc, 0) - self.assertIn("forgotten (never shipped)", out) - self.assertNotIn("Y999", load_json_file(self.t.retired)["retired"]) - self.assertNotIn("Y999", load_json_file(self.t.snapshot)["ids"]) - - def test_splices_chains_through_forgotten_id(self): - self.t.write_preset("VendorA", preset("ZN @base", filament_id="Y999", - instantiation=False, - filament_vendor="ZV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("ZN @P1", inherits="ZN @base", - compatible_printers=["P1"])) - rc, _out = self.t.update_snapshot() - self.assertEqual(rc, 0) - # A shipped id already forwards to Y999, which turns out never shipped. - self.t.write_ledger(retired={ - "OLD1": {"claims": ["VendorA/ZN"], "successor": "Y999"}}) - # The family re-mints to NEWZ and Y999 vanishes. - self.t.write_preset("VendorA", preset("ZN @base", filament_id="NEWZ", - instantiation=False, - filament_vendor="ZV", - filament_type="PLA"), - register=False) - rc, out = self.t.update_snapshot(forget_path=self.forget_file(["Y999"])) - self.assertEqual(rc, 0) - self.assertIn("spliced succession", out) - retired = load_json_file(self.t.retired)["retired"] - self.assertNotIn("Y999", retired) - self.assertEqual(retired["OLD1"], - {"claims": ["VendorA/ZN"], "successor": "NEWZ"}) - - -# --------------------------------------------------------------------------- -# --add-hint -# --------------------------------------------------------------------------- - -class TestAddHint(SyntheticTreeCase): - def add_bbl(self): - self.t.add_vendor("BBL", [ - preset("BPLA @base", filament_id="GFB01", instantiation=False), - preset("BPLA @X1C", inherits="BPLA @base", - compatible_printers=["X1C 0.4 nozzle"]), - ]) - rc, _out = self.t.update_snapshot() - assert rc == 0 - - def test_adds_island_hint(self): - self.add_bbl() - rc, out = self.t.add_hint(["GFB01=AX01"]) - self.assertEqual(rc, 0, out) - self.assertEqual(load_json_file(self.t.retired)["hints"], - {"GFB01": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - - def test_rejects_non_island_old(self): - rc, out = self.t.add_hint(["AX01=OGFL99"]) - self.assertEqual(rc, 1) - self.assertIn("not in an island id space", out) - self.assertEqual(afi.load_ledger(self.t.retired)["hints"], {}) - - def test_accepts_absent_island_old(self): - # Island catalogs own ids Orca never shipped (or has released). - rc, out = self.t.add_hint(["GFXX99=AX01"]) - self.assertEqual(rc, 0, out) - self.assertEqual(load_json_file(self.t.retired)["hints"], - {"GFXX99": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - - def test_rejects_retired_old(self): - self.t.write_ledger(retired={"ROLD": {"claims": [], "successor": None}}) - rc, out = self.t.add_hint(["ROLD=AX01"]) - self.assertEqual(rc, 1) - self.assertIn("is retired", out) - - def test_rejects_dead_target(self): - self.add_bbl() - rc, out = self.t.add_hint(["GFB01=OFnope99"]) - self.assertEqual(rc, 1) - self.assertIn("not a live tree id", out) - - def test_rejects_malformed_pair(self): - rc, out = self.t.add_hint(["GFB01"]) - self.assertEqual(rc, 1) - self.assertIn('expects "OLD=NEW"', out) - - -class TestRetire(SyntheticTreeCase): - def test_retires_vanished_id_with_successor(self): - rc, out = self.t.retire(["OFOLD001=AX01"]) - self.assertEqual(rc, 0, out) - self.assertEqual(load_json_file(self.t.retired)["retired"]["OFOLD001"], - {"claims": [], "successor": "AX01"}) - errors, out = self.t.check() - self.assertEqual(errors, 0, out) - - def test_rejects_live_old(self): - rc, out = self.t.retire(["AX01=OGFL99"]) - self.assertEqual(rc, 1) - self.assertIn("still lives in the tree", out) - self.assertEqual(afi.load_ledger(self.t.retired)["retired"], {}) - - def test_rejects_declaration_only_old(self): - # The inert-fork case: an id declared by a claim-less root still lives - # in the tree; the declaration must be deleted before --retire. - self.t.write_preset("VendorA", preset("ZOLD @base", filament_id="ZDECL01", - instantiation=False, - filament_vendor="ZV", - filament_type="PLA")) - rc, out = self.t.retire(["ZDECL01=AX01"]) - self.assertEqual(rc, 1) - self.assertIn("still lives in the tree", out) - - def test_rejects_island_space_old(self): - rc, out = self.t.retire(["GFXX99=AX01"]) - self.assertEqual(rc, 1) - self.assertIn("reserved id space", out) - - def test_rejects_user_space_old(self): - rc, out = self.t.retire(["P1234ABC=AX01"]) - self.assertEqual(rc, 1) - self.assertIn("reserved id space", out) - - def test_rejects_already_retired_old(self): - self.t.write_ledger(retired={"ROLD": {"claims": [], "successor": None}}) - rc, out = self.t.retire(["ROLD=AX01"]) - self.assertEqual(rc, 1) - self.assertIn("already retired", out) - - def test_rejects_hinted_old(self): - self.t.write_ledger(hints={"HOLD": "AX01"}) - rc, out = self.t.retire(["HOLD=AX01"]) - self.assertEqual(rc, 1) - self.assertIn("hint key", out) - - def test_rejects_dead_successor(self): - rc, out = self.t.retire(["OFOLD001=OFnope99"]) - self.assertEqual(rc, 1) - self.assertIn("not a live tree id", out) - - def test_rejects_malformed_pair(self): - rc, out = self.t.retire(["OFOLD001"]) - self.assertEqual(rc, 1) - self.assertIn('expects "OLD=NEW"', out) - - # --------------------------------------------------------------------------- # default run: mint + insert # --------------------------------------------------------------------------- @@ -1373,24 +949,6 @@ class TestRemint(SyntheticTreeCase): a = load_json_file(self.t.preset_path("VendorA", "APLA @base")) self.assertEqual(a["filament_id"], want) - def test_blocked_by_retired_id(self): - want0 = afi.generate_filament_id(*self.TRIPLE) - self.t.write_ledger(retired={want0: {"claims": [], "successor": None}}) - changed, errors, _out = self.t.remint(["VendorA"]) - self.assertEqual((changed, errors), (1, 0)) - root = load_json_file(self.t.preset_path("VendorA", "APLA @base")) - self.assertEqual(root["filament_id"], - afi.generate_filament_id(*self.TRIPLE, salt=1)) - - def test_blocked_by_hint_key(self): - want0 = afi.generate_filament_id(*self.TRIPLE) - self.t.write_ledger(hints={want0: "AX01"}) - changed, errors, _out = self.t.remint(["VendorA"]) - self.assertEqual((changed, errors), (1, 0)) - root = load_json_file(self.t.preset_path("VendorA", "APLA @base")) - self.assertEqual(root["filament_id"], - afi.generate_filament_id(*self.TRIPLE, salt=1)) - def test_blocked_by_other_triple_occurrence(self): want0 = afi.generate_filament_id(*self.TRIPLE) self.t.add_vendor("VendorB", [ @@ -1481,63 +1039,11 @@ class TestRealTree(unittest.TestCase): self.assertEqual(analysis["missing_effective"], []) self.assertEqual(analysis["read_errors"], []) - # Code<->ledger lockstep for the Qidi box (plan v4 SS2): the QD_* literals - # QidiPrinterAgent::map_filament_type_to_setting_id returns must stay - # retired ledger keys whose chains end at a live preset id, because the - # agent resolves them through resolve_filament_id_succession() at runtime. - def test_qidi_agent_fallback_ids_resolve_through_ledger(self): - cpp = os.path.join(REPO_ROOT, "src", "slic3r", "Utils", "QidiPrinterAgent.cpp") - with open(cpp, encoding="utf-8") as f: - src = f.read() - m = re.search( - r"map_filament_type_to_setting_id\(const std::string& filament_type\)" - r"\s*\{(.*?)\n\}", src, re.DOTALL) - self.assertIsNotNone(m, "map_filament_type_to_setting_id not found in QidiPrinterAgent.cpp") - qd_ids = re.findall(r'return "(QD_[0-9_]+)";', m.group(1)) - self.assertTrue(qd_ids, "no QD_* literals parsed from map_filament_type_to_setting_id") - ledger = afi.load_ledger(afi.RETIRED_PATH) - live = set(afi.analyze_tree(REAL_PROFILES)["ids"]) - for fid in qd_ids: - self.assertIn(fid, ledger["retired"], f'"{fid}" must be a retired ledger key') - status, node = afi.follow_succession( - fid, set(), ledger["retired"], ledger["hints"], live) - self.assertEqual( - status, "live", - f'"{fid}" succession must end at a live id (got {status} at "{node}")') - - # --------------------------------------------------------------------------- # review-fix regressions # --------------------------------------------------------------------------- class TestReviewFixes(SyntheticTreeCase): - def test_update_snapshot_refuses_retired_id_reuse(self): - fid = afi.generate_filament_id("LV", "PLA", "LONER") - self.t.write_preset("VendorA", preset("LONER @base", filament_id=fid, - instantiation=False, - filament_vendor="LV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("LONER @P1", inherits="LONER @base", - compatible_printers=["P1 0.4 nozzle"])) - rc, _ = self.t.update_snapshot() - self.assertEqual(rc, 0) - self.t.remove_preset("VendorA", "LONER @base") - self.t.remove_preset("VendorA", "LONER @P1") - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 0, out) - self.assertIn(fid, afi.load_retired(self.t.retired)) - # Resurrect the same id: --update-snapshot must refuse, not sanction. - self.t.write_preset("VendorA", preset("LONER @base", filament_id=fid, - instantiation=False, - filament_vendor="LV", - filament_type="PLA")) - self.t.write_preset("VendorA", preset("LONER @P1", inherits="LONER @base", - compatible_printers=["P1 0.4 nozzle"])) - rc, out = self.t.update_snapshot() - self.assertEqual(rc, 1) - self.assertIn("retired", out) - self.assertNotIn(fid, load_json_file(self.t.snapshot)["ids"]) - def test_check3_skips_of_id_inherited_from_other_vendor(self): # An OFL family carries its own minted OF id and a vendor tunes it # correctly (same base name, non-empty printers). The new claim must @@ -1565,7 +1071,7 @@ class TestReviewFixes(SyntheticTreeCase): errors, out = self.t.check() self.assertEqual(errors, 0, out) - def test_check7c_prints_expected_mint(self): + def test_check6c_prints_expected_mint(self): self.t.write_preset("VendorA", preset("Orphan PLA @P1", compatible_printers=["P1 0.4 nozzle"], filament_vendor="OV", diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7306d0db07..304c8957d5 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -572,60 +572,6 @@ std::string generate_preset_setting_id(const std::string& vendor, const std::str return std::string(out, 16); } -std::string follow_filament_id_succession(const std::string& id, const std::map& forwards) -{ - auto it = forwards.find(id); - if (it == forwards.end()) - return std::string(); - std::set visited { id }; - std::string current = id; - while (it != forwards.end() && visited.insert(it->second).second) { - current = it->second; - it = forwards.find(current); - } - // it == end(): current is the live end of the chain; otherwise the ledger is - // cyclic and current is the last id reached before a repeat. - return current; -} - -const std::map& filament_id_succession_map() -{ - // Loaded once; C++11 magic-static initialization makes this thread-safe. - // Succession is a best-effort miss-path fallback, so a missing or corrupt - // ledger is not an error for the client - it just yields an empty map. - static const std::map forwards = []() { - std::map map; - const std::string path = Slic3r::resources_dir() + "/profiles/retired_filament_ids.json"; - boost::nowide::ifstream file(path.c_str()); - if (!file.is_open()) { - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": cannot open " << path; - return map; - } - try { - nlohmann::json ledger; - file >> ledger; - if (ledger.contains("retired") && ledger["retired"].is_object()) - for (auto& [old_id, entry] : ledger["retired"].items()) - if (entry.is_object() && entry.contains("successor") && entry["successor"].is_string()) - map[old_id] = entry["successor"].get(); - if (ledger.contains("hints") && ledger["hints"].is_object()) - for (auto& [old_id, target] : ledger["hints"].items()) - if (target.is_string()) - map[old_id] = target.get(); - } catch (...) { - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": failed to parse " << path; - map.clear(); - } - return map; - }(); - return forwards; -} - -std::string resolve_filament_id_succession(const std::string& id) -{ - return follow_filament_id_succession(id, filament_id_succession_map()); -} - std::string Preset::get_iot_type_string(Preset::Type type) { switch (type) { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index ca1513a4bb..c9b3197a6f 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -101,23 +101,6 @@ std::string generate_preset_setting_id(const std::string& vendor, const std::string& type, const std::string& name); -// Succession forwarding for retired/renamed filament_ids, backed by the shipped -// ledger resources/profiles/retired_filament_ids.json. Consulted on a filament_id -// resolution miss only, so live ids always resolve natively first. -// -// Pure chain walk over `forwards` (old id -> successor). Returns the final id of the -// chain, or an empty string when `id` has no entry. Cycle-guarded: on a malformed -// cyclic ledger it stops at the last id reached before any repeat (a self-loop -// returns `id` itself). -std::string follow_filament_id_succession(const std::string& id, - const std::map& forwards); -// Forwarding map loaded once (thread-safe) from the ledger, merging -// "retired" {id -> non-null successor} and "hints" {island-owned id -> live id}. -// A missing or corrupt file yields an empty map. -const std::map& filament_id_succession_map(); -// Convenience: follow_filament_id_succession(id, filament_id_succession_map()). -std::string resolve_filament_id_succession(const std::string& id); - enum ConfigFileType { CONFIG_FILE_TYPE_UNKNOWN, diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index c6cd6d28dd..5542191964 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -801,12 +801,6 @@ std::optional PresetBundle::get_filament_by_filament_id(const } } } - // Miss: a retired/renamed id may forward to a live successor through the shipped - // succession ledger. resolve() returns a chain-final id, so the retry below cannot - // recurse further (a malformed cyclic ledger yields a non-final id, rejected here). - const std::string successor = resolve_filament_id_succession(filament_id); - if (!successor.empty() && resolve_filament_id_succession(successor).empty()) - return get_filament_by_filament_id(successor, printer_name); return std::nullopt; } @@ -3420,15 +3414,6 @@ 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; }); warn_ambiguous_filament_id_match(filaments, iter, filament_id); - if (iter == filaments.end()) { - // Retired/renamed ids forward to a live successor through the shipped succession ledger. - const std::string successor = resolve_filament_id_succession(filament_id); - if (!successor.empty()) { - iter = std::find_if(filaments.begin(), filaments.end(), - [this, &successor](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == successor; }); - warn_ambiguous_filament_id_match(filaments, iter, successor); - } - } 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); @@ -3532,16 +3517,6 @@ unsigned int PresetBundle::sync_ams_list(std::vectorfilaments; @@ -103,12 +103,6 @@ static wxString get_preset_name_by_filament_id(std::string filament_id, bool fol } } } - if (preset_name.empty() && follow_succession) { - // Retired/renamed ids forward to a live successor through the shipped succession ledger. - const std::string successor = resolve_filament_id_succession(filament_id); - if (!successor.empty()) - preset_name = get_preset_name_by_filament_id(successor, false); - } return preset_name; } diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 2e814a84e2..b28e410557 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -2764,22 +2764,14 @@ std::string MachineObject::setting_id_to_type(std::string setting_id, std::strin std::string type; PresetBundle* preset_bundle = GUI::wxGetApp().preset_bundle; if (preset_bundle) { - auto lookup_system_type = [preset_bundle](const std::string &id) { - std::string display_filament_type; - for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - if (it->filament_id.compare(id) == 0 && it->is_system) { - it->config.get_filament_type(display_filament_type); - break; - } + for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { + + if (it->filament_id.compare(setting_id) == 0 && it->is_system) { + std::string display_filament_type; + it->config.get_filament_type(display_filament_type); + type = display_filament_type; + break; } - return display_filament_type; - }; - type = lookup_system_type(setting_id); - if (type.empty()) { - // Retired/renamed ids forward to a live successor through the shipped succession ledger. - const std::string successor = resolve_filament_id_succession(setting_id); - if (!successor.empty()) - type = lookup_system_type(successor); } } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 338095b462..837f4ebe0c 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -556,13 +556,6 @@ bool PresetComboBox::add_ams_filaments(std::string selected, bool alias_name) } auto iter = std::find_if(filaments.begin(), filaments.end(), [&filament_id, this](auto &f) { return f.is_compatible && m_collection->get_preset_base(f) == &f && f.filament_id == filament_id; }); - if (iter == filaments.end()) { - // Retired/renamed ids forward to a live successor through the shipped succession ledger. - const std::string successor = resolve_filament_id_succession(filament_id); - if (!successor.empty()) - iter = std::find_if(filaments.begin(), filaments.end(), - [&successor, this](auto &f) { return f.is_compatible && m_collection->get_preset_base(f) == &f && f.filament_id == successor; }); - } if (iter == filaments.end()) { auto filament_type = tray.opt_string("filament_type", 0u); if (!filament_type.empty()) { diff --git a/src/slic3r/Utils/QidiPrinterAgent.cpp b/src/slic3r/Utils/QidiPrinterAgent.cpp index 22b8d37735..6b05480194 100644 --- a/src/slic3r/Utils/QidiPrinterAgent.cpp +++ b/src/slic3r/Utils/QidiPrinterAgent.cpp @@ -188,13 +188,7 @@ bool QidiPrinterAgent::fetch_slot_info(const std::string& base_url, } else if (!setting_id.empty() && has_visible_base_preset(bundle->filaments, setting_id)) { tray.tray_info_idx = setting_id; } else { - // Retired QD_* protocol ids forward to their minted successors via the shipped ledger. - const std::string successor = setting_id.empty() ? std::string() - : resolve_filament_id_succession(setting_id); - if (!successor.empty() && has_visible_base_preset(bundle->filaments, successor)) - tray.tray_info_idx = successor; - else - tray.tray_info_idx = bundle->filaments.filament_id_by_type(tray.tray_type); + tray.tray_info_idx = bundle->filaments.filament_id_by_type(tray.tray_type); } // Look up color from dictionary @@ -330,10 +324,6 @@ void QidiPrinterAgent::parse_filament_sections(const std::string& content, std:: std::string QidiPrinterAgent::map_filament_type_to_setting_id(const std::string& filament_type) { - // These QD_* protocol ids no longer appear on any preset: they are retired ledger keys - // that parse_box_status resolves through resolve_filament_id_succession(). Each literal - // must stay a ledger key whose chain ends at a live preset id — - // scripts/tests/test_filament_id.py parses this function and enforces that. const std::string upper = trim_and_upper(filament_type); if (upper == "PLA") { diff --git a/task.md b/task.md deleted file mode 100644 index b563759767..0000000000 --- a/task.md +++ /dev/null @@ -1,13 +0,0 @@ -check commit c2e91cb86ce013dc0486419f21620975c220c3be and it's related PR. -currently most of the system profiles didn't follow the Bambu's profiles rule of filemant id. -we want to come up a rule for filament id generation for all vendors and a fixing plan for existing system profiles so that there is no ambiguous filament ids for any given printer. - -We have two major goals: -### goal 1 -figure out a strategy or filament id generation rule. we want to follow bambu's rule but they only care about their own profiles while we need to consider generic. and the filament id generation rule should be easy to understand and maintain for not just us but other profiles creators too. either a determistic rule by using script like setting_id rule. or a documented rule so developers can refer when manually creating profiles. and figure out a way to fix migrate/fix existing filament ids in current profiles. note: bambu profiles shouldn't be touch to maintain interoperbitly with their AMS. - -### goal 2 -in existing system profiles, the ambiguous can caused by different error patterns: -1. legit sub type don't have unique filament id due to copy-paste when creating profiles. in this case, we should assign filament unique filament id to it. -2. generic and specific filament with legit same filament id adds same printer into it's compatible_printers. in this case we should remove the printer from the generic filament's compatible_printers. -3. we need to figure out more error patterns and figure out the fix strategy diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 7093f44405..bf3981f519 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -16,7 +16,6 @@ add_executable(${_TEST_NAME}_tests test_toolordering_nozzle_group.cpp test_preset_bundle_loading.cpp test_preset_setting_id.cpp - test_filament_id_succession.cpp test_preset_diff.cpp test_vendor_cache.cpp test_elephant_foot_compensation.cpp diff --git a/tests/libslic3r/test_filament_id_succession.cpp b/tests/libslic3r/test_filament_id_succession.cpp deleted file mode 100644 index 12fa82f9bb..0000000000 --- a/tests/libslic3r/test_filament_id_succession.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include - -#include "libslic3r/Preset.hpp" - -using namespace Slic3r; - -// Pure-function coverage of the succession chain walk (follow_filament_id_succession). -// The ledger-file loader (filament_id_succession_map) is deliberately untested here so -// the test carries no resources-dir dependency. -TEST_CASE("filament_id succession follows forwarding chains", "[Preset][filament_id]") { - SECTION("empty map returns empty") { - const std::map empty; - CHECK(follow_filament_id_succession("OFabc123", empty).empty()); - CHECK(follow_filament_id_succession("", empty).empty()); - } - - const std::map forwards = { - {"OFold001", "OFnew001"}, - {"OFchainA", "OFchainB"}, - {"OFchainB", "OFchainC"}, - {"OFchainC", "OFlive00"}, - }; - - SECTION("absent id returns empty") { - CHECK(follow_filament_id_succession("GFL99", forwards).empty()); - } - - SECTION("single hop resolves to the successor") { - CHECK(follow_filament_id_succession("OFold001", forwards) == "OFnew001"); - } - - SECTION("multi-hop chain resolves to the live end") { - CHECK(follow_filament_id_succession("OFchainA", forwards) == "OFlive00"); - CHECK(follow_filament_id_succession("OFchainB", forwards) == "OFlive00"); - CHECK(follow_filament_id_succession("OFchainC", forwards) == "OFlive00"); - } - - // QidiPrinterAgent resolves device-composed QD_* protocol ids through this same walk; - // the map key format carries no OF assumption. - SECTION("device-protocol QD_* keys forward like any other id") { - const std::map qd = {{"QD_2_1_11", "OFnew001"}}; - CHECK(follow_filament_id_succession("QD_2_1_11", qd) == "OFnew001"); - CHECK(follow_filament_id_succession("QD_2_1_12", qd).empty()); - } -} - -// Cycles are a ledger-validation error (script check 9) and never ship, but the client -// walk must still terminate on corrupt data: it stops at the last id reached before any -// id would repeat. -TEST_CASE("filament_id succession terminates on malformed cycles", "[Preset][filament_id]") { - SECTION("two-node cycle returns the last id before revisiting") { - const std::map cycle = {{"A", "B"}, {"B", "A"}}; - CHECK(follow_filament_id_succession("A", cycle) == "B"); - CHECK(follow_filament_id_succession("B", cycle) == "A"); - } - - SECTION("cycle entered through a tail stops before re-entering it") { - // T -> A -> B -> C -> A: stops at C, the last id reached before repeating A. - const std::map cycle = { - {"T", "A"}, {"A", "B"}, {"B", "C"}, {"C", "A"}}; - CHECK(follow_filament_id_succession("T", cycle) == "C"); - CHECK(follow_filament_id_succession("A", cycle) == "C"); - } - - SECTION("self-loop returns the id itself") { - const std::map loop = {{"A", "A"}}; - CHECK(follow_filament_id_succession("A", loop) == "A"); - } -}