mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-01 06:16:58 +00:00
v3.0: content-addressed mint tooling, succession runtime, W1 hardening
Implements phase v3.0 of filament_id_plan_v3.md - tooling and client
prerequisites. No filament_id values change in this commit.
Tooling (scripts/assign_filament_ids.py; 106 unit tests):
- The mint key becomes "filament_product/<filament_vendor>/<filament_type>/
<family_name>", resolved loader-faithfully from the declarer's flattened
config - bundle-independent and content-addressed; identity fixes re-id
by design and are made safe by the succession ledger. --mint now takes
the triple.
- The snapshot gains "triples" and "triple_exceptions" sections, folded
into the equality gate: any vendor/type/name change surfaces as a
reviewable snapshot diff. Check 3 rewritten to triple-mint conformance
with (id, triple) grandfathering; check 5 generalized from OFL generics
to every OFL-riding vendor preset; new check 8 (triple integrity) and
check 9 (succession integrity).
- The retired ledger moves to resources/profiles/retired_filament_ids.json
so it ships with the app; schema {claims, successor} plus cross-island
"hints". The 14 v1 entries are migrated with mode-rule successors.
Vanished ids in a foreign island's space (GF*/QD_*) are RELEASED with a
hint instead of retired: the island catalog owns them and may
legitimately (re)ship them, which check 4 must never block.
- New maintenance modes: --remint VENDOR, --drop-redundant-ids VENDOR,
--add-hint "OLD=NEW", and --update-snapshot --forget-never-shipped FILE
(never-shipped ids drop from lineage; chains splice through them).
Runtime (C++):
- Succession helpers in libslic3r/Preset (pure chain-follow + lazy ledger
load from resources), consulted only on resolution miss in
get_filament_by_filament_id, both AMS sync predicates,
add_ams_filaments, setting_id_to_type, and the calibration-history
lookup; behavior is byte-identical while the ledger has no matching
entry. Catch2 coverage in tests/libslic3r.
- W1 hardening: check_ams_filament_valid no longer remote-wipes trays or
rewrites temps for P-shaped ids that a system preset carries (ten such
system ids ship today); the size==8 && [0]=='P' assert is relaxed; the
unguarded filament_list find deref in the temp-equation check returns
non-destructively on a miss.
- MoonrakerPrinterAgent: the 23 hardcoded OFL generic ids are replaced by
a runtime lookup of "Generic <family> @System" (id-equivalent on
today's shipped profiles, follows future re-mints automatically);
scripts/test_moonraker_lane_data.py derives its expectations from the
shipped profiles and gains --check-ofl-map.
Profile data (W3 - type is now a key component, so type bugs are fixed
before any re-mint):
- 17 same-name-different-type groups corrected across 50 files (OFL
Generic PETG-CF/PE-CF/PP-CF; Flashforge ASA Basic/ASA-CF/ABS-CF/HIPS/
PAHT-CF/PLA Silk; FusRock PAHT; Creality Generic PA6-CF; InfiMech PETG;
Anycubic TPU 95A / TPU for ACE; Prusa Generic PA-CF/PLA-CF) - every
value validated against MaterialType::all(); 3 Snapmaker U1 roots gain
their missing filament_vendor. Flattened-config equivalence vs the
previous commit: exactly the 54 intended diffs, zero BBL.
- doc/developer-reference/filament_id.md rewritten for the v3 rule.
Ambiguous type divergences (Prusa Generic TPU/TPU HF FLEX-vs-TPU, FusRock
S-Multi/S-PAHT) and all same-type vendor-tag divergences are deferred to
the v3.2 vendor worksheets, catalogued with analysis.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"retired": {
|
||||
"GFABS": [
|
||||
"Anycubic/Anycubic ABS"
|
||||
],
|
||||
"GFL92": [
|
||||
"Anycubic/Anycubic PLA"
|
||||
],
|
||||
"GFL93": [
|
||||
"Anycubic/Anycubic PLA+"
|
||||
],
|
||||
"GFPLA": [
|
||||
"Anycubic/Anycubic PLA"
|
||||
],
|
||||
"GFPLA High Speed": [
|
||||
"Anycubic/Anycubic PLA High Speed"
|
||||
],
|
||||
"GFPLA Matte": [
|
||||
"Anycubic/Anycubic PLA Matte"
|
||||
],
|
||||
"GFPLA Silk": [
|
||||
"Anycubic/Anycubic PLA Silk"
|
||||
],
|
||||
"GFPLA+": [
|
||||
"Anycubic/Anycubic PLA+"
|
||||
],
|
||||
"P11851ba": [
|
||||
"Artillery/Artillery PET"
|
||||
],
|
||||
"P284941e": [
|
||||
"Artillery/Artillery PETG",
|
||||
"Artillery/Artillery PETG Basic"
|
||||
],
|
||||
"P2fbf0c0": [
|
||||
"Artillery/Artillery TPU"
|
||||
],
|
||||
"P39db358": [
|
||||
"Artillery/Artillery ABS"
|
||||
],
|
||||
"P8163162": [
|
||||
"Artillery/Artillery PVA"
|
||||
],
|
||||
"P87436f6": [
|
||||
"Artillery/Artillery ASA"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,15 @@ Inserts/deletes/modifies random lane data in Moonraker database,
|
||||
then reads back and displays with colored output.
|
||||
"""
|
||||
|
||||
import requests
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None # only needed for live-printer operations, not --check-ofl-map
|
||||
import random
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import sys
|
||||
|
||||
@@ -16,6 +21,11 @@ import sys
|
||||
DEFAULT_HOST = "192.168.88.9"
|
||||
DEFAULT_PORT = 7125
|
||||
NAMESPACE = "lane_data"
|
||||
|
||||
# Repo-relative paths for the offline generic-map check
|
||||
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
MOONRAKER_AGENT_CPP = os.path.join(REPO_ROOT, "src", "slic3r", "Utils", "MoonrakerPrinterAgent.cpp")
|
||||
OFL_FILAMENT_DIR = os.path.join(REPO_ROOT, "resources", "profiles", "OrcaFilamentLibrary", "filament")
|
||||
LANE_KEYS = [f"lane{i}" for i in range(1, 9)] # lane1-lane8
|
||||
MATERIALS = ["PLA", "ABS", "PETG", "ASA", "ASA Sparkle", "TPU", ""]
|
||||
|
||||
@@ -30,6 +40,95 @@ MATERIAL_TEMPS = {
|
||||
"": {"nozzle": None, "bed": None},
|
||||
}
|
||||
|
||||
def parse_cpp_type_map():
|
||||
"""Extract the normalized-type -> OFL generic family table from MoonrakerPrinterAgent.cpp.
|
||||
|
||||
Reads MoonrakerPrinterAgent::map_filament_type_to_generic_id's type_to_ofl_family
|
||||
initializer so the check tracks the C++ normalization without a duplicated list.
|
||||
"""
|
||||
with open(MOONRAKER_AGENT_CPP, encoding="utf-8") as f:
|
||||
src = f.read()
|
||||
m = re.search(r"type_to_ofl_family\s*=\s*\{(.*?)\n\s*\};", src, re.DOTALL)
|
||||
if not m:
|
||||
raise RuntimeError(f"type_to_ofl_family table not found in {MOONRAKER_AGENT_CPP}")
|
||||
pairs = re.findall(r'\{\s*"([^"]+)"\s*,\s*"([^"]+)"\s*\}', m.group(1))
|
||||
if not pairs:
|
||||
raise RuntimeError("type_to_ofl_family table parsed empty")
|
||||
return dict(pairs)
|
||||
|
||||
def load_ofl_presets():
|
||||
"""Map preset name -> parsed JSON for every OrcaFilamentLibrary filament profile."""
|
||||
presets = {}
|
||||
for root, _dirs, files in os.walk(OFL_FILAMENT_DIR):
|
||||
for fn in files:
|
||||
if not fn.endswith(".json"):
|
||||
continue
|
||||
try:
|
||||
with open(os.path.join(root, fn), encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
name = data.get("name")
|
||||
if isinstance(name, str) and name:
|
||||
presets[name] = data
|
||||
return presets
|
||||
|
||||
def resolve_ofl_filament_id(presets, name):
|
||||
"""Follow the inherits chain (within OFL) until a filament_id is declared."""
|
||||
seen = set()
|
||||
while name and name not in seen:
|
||||
seen.add(name)
|
||||
preset = presets.get(name)
|
||||
if preset is None:
|
||||
return None
|
||||
fid = preset.get("filament_id")
|
||||
if fid:
|
||||
return fid
|
||||
name = preset.get("inherits")
|
||||
return None
|
||||
|
||||
def check_ofl_generic_map():
|
||||
"""Assert every material type the C++ normalization handles resolves to a shipped
|
||||
OrcaFilamentLibrary generic preset carrying a filament_id.
|
||||
|
||||
Expectations are derived from the shipped profiles, not pinned id literals, so the
|
||||
check stays valid across filament_id re-mints.
|
||||
"""
|
||||
print("Checking C++ generic-type map against shipped OrcaFilamentLibrary presets...")
|
||||
try:
|
||||
type_map = parse_cpp_type_map()
|
||||
except (OSError, RuntimeError) as e:
|
||||
print(f" FAIL: {e}")
|
||||
return False
|
||||
presets = load_ofl_presets()
|
||||
if not presets:
|
||||
print(f" FAIL: no OFL filament profiles found under {OFL_FILAMENT_DIR}")
|
||||
return False
|
||||
errors = []
|
||||
for family in sorted(set(type_map.values())):
|
||||
preset_name = f"Generic {family} @System"
|
||||
if preset_name not in presets:
|
||||
errors.append(f"{preset_name}: no such OFL preset")
|
||||
continue
|
||||
if str(presets[preset_name].get("instantiation", "")).lower() != "true":
|
||||
errors.append(f"{preset_name}: not instantiated — the C++ runtime lookup "
|
||||
f"only sees presets loaded into the PresetBundle")
|
||||
continue
|
||||
fid = resolve_ofl_filament_id(presets, preset_name)
|
||||
if not fid:
|
||||
errors.append(f"{preset_name}: no filament_id resolvable through inherits")
|
||||
continue
|
||||
aliases = ", ".join(sorted(t for t, fam in type_map.items() if fam == family))
|
||||
print(f" {fid:10s} {preset_name:32s} <- {aliases}")
|
||||
if errors:
|
||||
for e in errors:
|
||||
print(f" FAIL: {e}")
|
||||
print(f"OFL generic map check FAILED ({len(errors)} error(s))")
|
||||
return False
|
||||
print(f"OFL generic map check passed: {len(type_map)} type aliases, "
|
||||
f"{len(set(type_map.values()))} OFL generic presets\n")
|
||||
return True
|
||||
|
||||
def test_connection(host, port, api_key=None, verbose=False):
|
||||
"""Test basic connectivity to Moonraker."""
|
||||
url = f"http://{host}:{port}/server/info"
|
||||
@@ -404,11 +503,25 @@ def main():
|
||||
help="Only read and display current lane data")
|
||||
parser.add_argument("--load", metavar="FILE",
|
||||
help="Load lane data from JSON file and overwrite printer lanes")
|
||||
parser.add_argument("--check-ofl-map", action="store_true",
|
||||
help="Only run the offline check that the C++ generic-type map "
|
||||
"resolves against shipped OrcaFilamentLibrary presets")
|
||||
parser.add_argument("--verbose", "-v", action="store_true",
|
||||
help="Verbose output for debugging")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Offline check first: the C++ type normalization must resolve against shipped
|
||||
# OFL presets (no printer needed).
|
||||
if not check_ofl_generic_map():
|
||||
return 1
|
||||
if args.check_ofl_map:
|
||||
return 0
|
||||
|
||||
if requests is None:
|
||||
print("The 'requests' module is required for live printer operations (pip install requests).")
|
||||
return 1
|
||||
|
||||
print(f"\nConnecting to Moonraker at {args.host}:{args.port}...")
|
||||
|
||||
# First test basic connectivity
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user