Multiple printers, printer/slot in Spoolman, kept in sync
- One worker per printer (name, IP, CC2 Control URL, own pinned host key); a spool sits in at most one slot of one printer, moves between printers - Spoolman: location "<printer> Slot N" plus extra fields "drucker"/"slot" (created by the bridge), cleared and "Lager" on removal - Full comparison every minute: the printer wins over manual edits of loaded spools; stale printer locations go to storage; own locations untouched - Automatic migration of v1 settings, state and host key - Status hint when a tag is still in a tray but unmapped (e.g. after "Take out" in CC2 Control) - SSH test: connection drops during exec give a clean error, not a 500 - Mock environment with several printers; tests for all of the above Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GcEk5tkfFSYD6NTF66NkC
This commit is contained in:
1 parent
bf32667f77
commit
a9041eb538
15 files changed
+1400
-684
No files matched your search
+34
-5
@@ -113,12 +113,13 @@ class FakeSpoolman:
|
||||
self.calls: list[tuple] = []
|
||||
self.fail = False
|
||||
self.fail_use_after_apply = False
|
||||
self.field_defs: dict[str, dict] = {}
|
||||
|
||||
def add(self, sid, vendor="SUNLU", material="PLA+", color="000000", remaining=800.0, lot=None, **extra):
|
||||
self.db[sid] = {
|
||||
"id": sid, "lot_nr": lot if lot is not None else f"{vendor.upper()}{sid:03d}",
|
||||
"remaining_weight": remaining, "initial_weight": 1000.0, "spool_weight": 150.0, "used_weight": 0.0,
|
||||
"location": "", "archived": False,
|
||||
"location": "", "archived": False, "extra": {},
|
||||
"filament": {"id": sid, "name": f"{material} {color}", "material": material, "color_hex": color,
|
||||
"diameter": 1.75, "density": 1.24, "weight": 1000.0, "vendor": {"name": vendor}},
|
||||
**extra,
|
||||
@@ -139,11 +140,39 @@ class FakeSpoolman:
|
||||
self._check()
|
||||
return [copy.deepcopy(s) for s in self.db.values() if archived or not s["archived"]]
|
||||
|
||||
def set_location(self, sid, location):
|
||||
def update_spool(self, sid, body):
|
||||
self._check()
|
||||
self.calls.append(("location", sid, location))
|
||||
self.db[sid]["location"] = location
|
||||
return self.db[sid]
|
||||
if sid not in self.db:
|
||||
raise SpoolmanNotFound("nicht gefunden")
|
||||
sp = self.db[sid]
|
||||
for key, value in (body.get("extra") or {}).items():
|
||||
if value is not None and key not in self.field_defs:
|
||||
raise SpoolmanError("Spoolman PATCH: HTTP 400")
|
||||
self.calls.append(("location", sid, body.get("location")))
|
||||
if "extra" in body:
|
||||
self.calls.append(("extra", sid, dict(body["extra"])))
|
||||
if "location" in body:
|
||||
sp["location"] = body["location"]
|
||||
for key, value in (body.get("extra") or {}).items():
|
||||
if value is None:
|
||||
sp["extra"].pop(key, None)
|
||||
else:
|
||||
sp["extra"][key] = value
|
||||
return sp
|
||||
|
||||
def fields(self):
|
||||
self._check()
|
||||
return [{"key": k, **v} for k, v in self.field_defs.items()]
|
||||
|
||||
def add_field(self, key, params):
|
||||
self._check()
|
||||
self.calls.append(("field", key))
|
||||
self.field_defs[key] = dict(params)
|
||||
return self.fields()
|
||||
|
||||
def extra(self, sid):
|
||||
import json
|
||||
return {k: json.loads(v) for k, v in self.db[sid]["extra"].items()}
|
||||
|
||||
def use(self, sid, grams):
|
||||
self._check()
|
||||
|
||||
Reference in new issue
Block a user