Grotti 3.5.2: Zu wenig Rest blockiert den Start nicht mehr, und ein anlaufender Druck wird nicht als Neustart neu eingereiht.
This commit is contained in:
1 parent
5166ed6530
commit
d1eb87ba25
13 files changed
+98
-77
No files matched your search
+5
-1
@@ -255,13 +255,17 @@ def build(conn, watcher_latest, printers=None, send_jobs=None):
|
||||
live_by_host[host]["display_step_index"] = job.get("display_step_index")
|
||||
live_by_host[host]["step_index"] = job.get("step_index")
|
||||
else:
|
||||
phase = (job.get("phase") or "").lower()
|
||||
starting = phase in ("starting", "await_print")
|
||||
running.append({
|
||||
"host": host,
|
||||
"name": job.get("printer_name") or host,
|
||||
"state": "running",
|
||||
"state": "starting" if starting else "running",
|
||||
"starting": starting,
|
||||
"progress": job.get("progress") or 0,
|
||||
"remain": job.get("remain") or 0,
|
||||
"filename": job.get("filename") or "",
|
||||
"message": job.get("message") or "",
|
||||
"queue_id": job.get("id"),
|
||||
"is_ablauf": bool(job.get("is_ablauf")),
|
||||
})
|
||||
|
||||
+2
-38
@@ -415,44 +415,8 @@ def sync_history(conn, cfg):
|
||||
|
||||
|
||||
def assert_filament_enough(conn, file_row, spool_id=None, ace_map=None, host=""):
|
||||
"""Dateien-Gewicht gegen Spule oder ACE-Plätze prüfen."""
|
||||
need = float((file_row or {}).get("weight_g") or 0)
|
||||
if need <= 0:
|
||||
return
|
||||
mapping = acm.normalize_mapping(ace_map)
|
||||
if mapping and host:
|
||||
leftovers = []
|
||||
for item in mapping:
|
||||
slot = int(item["ams_index"]) + 1
|
||||
row = conn.execute(
|
||||
"""
|
||||
SELECT remaining_g, name FROM spools
|
||||
WHERE printer_host = %s AND ace_slot = %s
|
||||
ORDER BY id LIMIT 1
|
||||
""",
|
||||
(host, slot),
|
||||
).fetchone()
|
||||
if row:
|
||||
leftovers.append(float(row["remaining_g"] or 0))
|
||||
if leftovers and sum(leftovers) + 0.5 < need:
|
||||
raise GrottiError(
|
||||
"ACE-Plätze haben nur %.0f g, Datei braucht %.0f g."
|
||||
% (sum(leftovers), need),
|
||||
"spule",
|
||||
)
|
||||
return
|
||||
if not spool_id:
|
||||
return
|
||||
spool = store.get_spool_row(conn, spool_id)
|
||||
if not spool:
|
||||
return
|
||||
left = float(spool["remaining_g"] or 0)
|
||||
if left + 0.5 < need:
|
||||
raise GrottiError(
|
||||
"Spule „%s“ hat nur %.0f g, Datei braucht %.0f g."
|
||||
% (spool["name"], left, need),
|
||||
"spule",
|
||||
)
|
||||
"""Zu wenig Rest bricht den Start nicht ab. Die Oberfläche warnt vorher."""
|
||||
return
|
||||
|
||||
|
||||
def prepare_send(conn, file_id, host, confirm, override, spool_id, ace_map=None):
|
||||
|
||||
+24
-6
@@ -30,7 +30,9 @@ BUSY = {"printing", "paused", "heating", "error"}
|
||||
KEEPALIVE_S = 120
|
||||
# Nach Start ohne print_active: Upload/MQTT/LeviQ brauchen Zeit, aber nicht ewig
|
||||
PRINT_START_TIMEOUT_S = 600
|
||||
# Einfacher Queue-Start ohne Worker (typisch Grotti-Neustart)
|
||||
# Upload-Worker weg (typisch Grotti-Neustart mitten im Hochladen).
|
||||
# Nach erfolgreichem Senden ist die Phase await_print — dort gilt das nicht,
|
||||
# sonst wird ein noch anlaufender Druck als „Neustart“ neu eingereiht.
|
||||
SIMPLE_START_STUCK_S = 45
|
||||
|
||||
|
||||
@@ -421,16 +423,18 @@ class PrintQueueDispatcher:
|
||||
from grotti import orders
|
||||
|
||||
orders.attach_order_item_to_live_run(wconn, host, name, oid)
|
||||
# Phase „starting“ lassen, bis Moonraker printing meldet.
|
||||
# Sofort print_active + alter complete/standby → fälschlich „Fertig.“
|
||||
# await_print: Datei ist raus, Drucker meldet printing oft erst
|
||||
# nach einer Minute. Nicht bei starting lassen — sonst gilt nach
|
||||
# 45 s der Neustart-Retry und der zweite Start trifft „druckt schon“.
|
||||
store.update_print_queue(
|
||||
wconn,
|
||||
item_id,
|
||||
{
|
||||
"status": "running",
|
||||
"message": "Startet auf %s: %s" % (printer_copy["name"], name),
|
||||
"message": "Datei ist auf %s. Druckstart kann etwa eine Minute dauern."
|
||||
% printer_copy["name"],
|
||||
"host": host,
|
||||
"phase": "starting",
|
||||
"phase": "await_print",
|
||||
"phase_started_at": "now",
|
||||
},
|
||||
)
|
||||
@@ -487,7 +491,7 @@ class PrintQueueDispatcher:
|
||||
"""Einfach-Druck: running bleiben bis Moonraker fertig/abgebrochen."""
|
||||
phase = (item.get("phase") or "").lower()
|
||||
state = (live.get("state") or "").lower()
|
||||
if phase == "starting":
|
||||
if phase in ("starting", "await_print"):
|
||||
if state in ("printing", "paused", "heating") or live.get("print_active"):
|
||||
store.update_print_queue(
|
||||
conn,
|
||||
@@ -498,6 +502,20 @@ class PrintQueueDispatcher:
|
||||
"message": "Druckt…",
|
||||
},
|
||||
)
|
||||
return
|
||||
if phase == "await_print" and self._print_start_timed_out(item):
|
||||
store.update_print_queue(
|
||||
conn,
|
||||
item["id"],
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Druckstart-Timeout: Drucker hat nach %d min nicht angefangen."
|
||||
% (PRINT_START_TIMEOUT_S // 60),
|
||||
"phase": "",
|
||||
"phase_started_at": None,
|
||||
},
|
||||
)
|
||||
return
|
||||
# complete/standby vom vorherigen Auftrag: noch warten, nicht „Fertig.“
|
||||
return
|
||||
if phase != "print_active":
|
||||
|
||||
+4
-11
@@ -2858,7 +2858,10 @@ def _match_spools_for_need(need, have, used_ids=None):
|
||||
|
||||
|
||||
def material_fit_score(conn, host, file_id):
|
||||
"""Höher = besser. -1 = warten (falsche Sorte mit Profil, oder zu wenig Rest)."""
|
||||
"""Höher = besser. -1 = warten (falsche Sorte mit Profil).
|
||||
|
||||
Zu wenig Rest blockiert nicht; genug Rest gibt Zusatzpunkte.
|
||||
"""
|
||||
need, weight = _file_needs(conn, file_id)
|
||||
have = _spool_material_set(conn, host)
|
||||
if not need:
|
||||
@@ -2883,11 +2886,6 @@ def material_fit_score(conn, host, file_id):
|
||||
score += 5
|
||||
if weight and best["remaining_g"] + 1e-6 >= weight:
|
||||
score += 5
|
||||
elif weight and all(h["remaining_g"] + 1e-6 < weight for h in matched):
|
||||
return -1, "braucht %s (%s g fehlen)" % (
|
||||
_need_label(n),
|
||||
int(max(0, weight - max(h["remaining_g"] for h in matched))),
|
||||
)
|
||||
return score, ""
|
||||
|
||||
have_types = {h["material_type"] for h in have if h["material_type"]}
|
||||
@@ -2900,11 +2898,6 @@ def material_fit_score(conn, host, file_id):
|
||||
score += 5
|
||||
if weight and any(h["remaining_g"] + 1e-6 >= weight for h in matched):
|
||||
score += 5
|
||||
elif weight and all(h["remaining_g"] + 1e-6 < weight for h in matched):
|
||||
return -1, "braucht %s (%s g fehlen)" % (
|
||||
_need_label(n),
|
||||
int(max(0, weight - max(h["remaining_g"] for h in matched))),
|
||||
)
|
||||
return score, ""
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
"""Version von Grotti (SemVer). Bei jeder Änderung CHANGELOG.md mitführen."""
|
||||
|
||||
__version__ = "3.5.1"
|
||||
__version__ = "3.5.2"
|
||||
Reference in new issue
Block a user