Recognise rolls put back, use CC2 Control's log for bookings
- A roll put back into the slot it left, with the tag data unchanged, is mapped again and CC2 Control's question answered; if the touchscreen then replaces the id, it was another roll and goes to storage. - A touchscreen edit of a scanned roll keeps the mapping and re-answers CC2 Control's question so its use is counted. - Bookings name the print and its result from CC2 Control's log. - A weigh-in in CC2 Control sets Spoolman's remaining weight instead of being booked as use. - After "Take out" with the roll still in the tray, the tray's logged use is booked to that roll until it leaves. 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
a9041eb538
commit
67c3b54903
7 files changed
+607
-40
No files matched your search
+60
-4
@@ -3,8 +3,11 @@
|
||||
python tools/mock_env.py --host mock --printers 2
|
||||
|
||||
Per printer i: SSH 2222+i (keys added via POST /authorize), CC2 Control 8081+i. Spoolman 7912, control 8099.
|
||||
Control endpoints (port 8099, optional &printer=i): POST /insert?slot=&spool=, /remove?slot=,
|
||||
/consume?slot=&grams=, /job?active=0|1, /touch?slot= (touchscreen edit: weight falls back to 1000).
|
||||
Control endpoints (port 8099, optional &printer=i): POST /insert?slot=&spool= (scanned roll in),
|
||||
/remove?slot= (roll out, the record keeps its tag data), /putback?slot= (roll in without a scan),
|
||||
/takeout?slot= (CC2 Control's "Take out"), /consume?slot=&grams=, /job?active=0|1,
|
||||
/touch?slot= (touchscreen edit: weight falls back to 1000), /print?slot=&mm=&job=&result= (logged use of
|
||||
a tray, charged to its spool if it has one), /weigh?spool=&remaining= (weigh-in in CC2 Control).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -15,6 +18,7 @@ import json
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import paramiko
|
||||
@@ -32,6 +36,7 @@ def printer_state():
|
||||
"slots": [{"slot": i, "spool": "", "last": "", "question": "", "printer": {"status": 0}} for i in range(5)],
|
||||
"spools": [],
|
||||
"job": {"active": False, "file": "", "slots": []},
|
||||
"log": [],
|
||||
"ids": (f"m{i:04d}" for i in itertools.count(1)),
|
||||
}
|
||||
st["slots"][4]["printer"] = None
|
||||
@@ -91,7 +96,7 @@ def make_cc2(host_header: str, P: dict) -> FastAPI:
|
||||
with LOCK:
|
||||
STATE["requests"].append("GET /api/spools")
|
||||
return {"available": True, "enabled": P["enabled"], "slots": P["slots"], "spools": P["spools"],
|
||||
"job": P["job"], "log": []}
|
||||
"job": P["job"], "log": P["log"]}
|
||||
|
||||
@app.get("/api/canvas")
|
||||
def canvas():
|
||||
@@ -178,6 +183,9 @@ def make_spoolman() -> FastAPI:
|
||||
if value is not None and key not in STATE["fields"]:
|
||||
raise HTTPException(400, f"Unknown extra field {key}")
|
||||
sp.update({k: v for k, v in body.items() if k in ("location",)})
|
||||
if "used_weight" in body:
|
||||
sp["used_weight"] = float(body["used_weight"])
|
||||
sp["remaining_weight"] = (sp.get("initial_weight") or 1000.0) - sp["used_weight"]
|
||||
for key, value in (body.get("extra") or {}).items():
|
||||
if value is None:
|
||||
sp["extra"].pop(key, None)
|
||||
@@ -235,7 +243,55 @@ def make_control() -> FastAPI:
|
||||
P = PRINTERS[printer]
|
||||
with LOCK:
|
||||
P["slots"][slot]["printer"] = {"status": 0}
|
||||
P["record"][f"channels{slot}"] = channel("")
|
||||
if P["slots"][slot]["spool"]:
|
||||
P["slots"][slot]["last"] = P["slots"][slot]["spool"]
|
||||
P["slots"][slot]["spool"] = ""
|
||||
P["slots"][slot]["question"] = ""
|
||||
return {"ok": True}
|
||||
|
||||
@app.post("/putback")
|
||||
def putback(slot: int, printer: int = 0):
|
||||
P = PRINTERS[printer]
|
||||
with LOCK:
|
||||
P["slots"][slot]["printer"] = {"status": 1}
|
||||
P["slots"][slot]["spool"] = ""
|
||||
P["slots"][slot]["question"] = "inserted"
|
||||
return {"ok": True}
|
||||
|
||||
@app.post("/takeout")
|
||||
def takeout(slot: int, printer: int = 0):
|
||||
P = PRINTERS[printer]
|
||||
with LOCK:
|
||||
P["slots"][slot]["last"] = P["slots"][slot]["spool"]
|
||||
P["slots"][slot]["spool"] = ""
|
||||
P["slots"][slot]["question"] = ""
|
||||
return {"ok": True}
|
||||
|
||||
def log(P, kind, spool, slot, grams, mm, job="", result=""):
|
||||
P["log"].insert(0, {"time": int(time.time()), "spool": spool, "slot": slot, "kind": kind, "grams": grams,
|
||||
"mm": mm, "job": job, "result": result})
|
||||
del P["log"][150:]
|
||||
|
||||
@app.post("/print")
|
||||
def print_(slot: int, mm: float, job: str = "test.gcode", result: str = "complete", printer: int = 0):
|
||||
P = PRINTERS[printer]
|
||||
with LOCK:
|
||||
sid = P["slots"][slot]["spool"]
|
||||
sp = next((s for s in P["spools"] if s["id"] == sid), None)
|
||||
d, rho = (sp["diameter"], sp["density"]) if sp else (1.75, 1.24)
|
||||
grams = mm * 3.141592653589793 * (d / 2) ** 2 * rho / 1000
|
||||
if sp:
|
||||
sp["remaining"] -= grams
|
||||
log(P, "print", sid, slot, -round(grams, 3), mm, job, result)
|
||||
return {"ok": True, "grams": grams}
|
||||
|
||||
@app.post("/weigh")
|
||||
def weigh(spool: str, remaining: float, printer: int = 0):
|
||||
P = PRINTERS[printer]
|
||||
with LOCK:
|
||||
sp = next(s for s in P["spools"] if s["id"] == spool)
|
||||
log(P, "correction", spool, -1, remaining - sp["remaining"], 0, "", "weighed")
|
||||
sp["remaining"] = remaining
|
||||
return {"ok": True}
|
||||
|
||||
@app.post("/touch")
|
||||
|
||||
Reference in new issue
Block a user