From a29ab6d6b49c58603846ead499015728d677468a Mon Sep 17 00:00:00 2001 From: s4luorth Date: Thu, 1 Oct 2026 13:41:22 +0200 Subject: [PATCH] Lineare Warteschlange: nach position abarbeiten + Formatwechsel-Anforderung - _naechste_jobs: alle queued Jobs der Maschine nach -priority,position,date_created,id (kein Format-Filter mehr); erster Job bestimmt das naechste Format - Hauptschleife: passt Format -> drucken; sonst machines.pending_format setzen und auf Board-Bestaetigung warten (kein Auto-Anschluss) - _sende_batch: nur der zusammenhaengende Lauf vom Anfang (gleiches Format+Platzierung), damit ein Format an spaeterer Position nicht vorgezogen wird - Maschinen-Fetch um pending_format erweitert; _set_pending-Helfer Braucht neue Directus-Felder/Rechte (Bootstrap zuerst laufen lassen). Co-Authored-By: Claude Opus 4.8 --- agent.py | 57 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/agent.py b/agent.py index b969e2e..0d220e3 100644 --- a/agent.py +++ b/agent.py @@ -230,7 +230,7 @@ def main(): pruefe_befehle(dx, machine_id, cfg) maschine = dx.get(f"/items/machines/{machine_id}", - fields="base_url,mid,username,password,active,current_format") + fields="base_url,mid,username,password,active,current_format,pending_format") if not maschine or not maschine.get("active", True): beat("offline") time.sleep(poll) @@ -282,11 +282,24 @@ def main(): # Nächsten Job nur senden, wenn nichts (mehr) läuft. if laufend is None: current_format = (maschine.get("current_format") or "").strip() - # Format-Warteschlange: nur passendes Format, kein Auto-Anschluss. - if current_format: - jobs = _naechste_jobs(dx, machine_id, current_format) - if jobs: + pending_format = (maschine.get("pending_format") or "").strip() + # Lineare Warteschlange: der nächste Job nach Position bestimmt das + # Format. Stimmt es mit dem eingelegten Format überein → drucken. + # Sonst Formatwechsel im Board anfordern (pending_format) und warten – + # kein Auto-Anschluss ohne Bestätigung. + jobs = _naechste_jobs(dx, machine_id) + if jobs: + next_format = (jobs[0].get("format") or "").strip() + if next_format and next_format == current_format: + if pending_format: + _set_pending(dx, machine_id, None) laufend = _sende_batch(dx, plotter, jobs, KIND) + elif pending_format != next_format: + _set_pending(dx, machine_id, next_format) + log(dx, machine_id, "info", "format", + f"Formatwechsel noetig: bitte Format '{next_format}' einlegen und im Board bestaetigen.") + elif pending_format: + _set_pending(dx, machine_id, None) time.sleep(poll) @@ -371,21 +384,28 @@ def _heartbeat(dx, machine_id, status): pass -def _naechste_jobs(dx, machine_id, current_format): - """ALLE eingereihten Jobs des aktuell eingelegten Formats – in stabiler - Warteschlangen-Reihenfolge (Priorität, dann Erstellzeit, dann ID als - eindeutiger Tiebreak). Diese Reihenfolge = Erstell-/Auftragsnummern-Reihenfolge - (enqueue-bulk legt die Jobs nach Auftragsnummer an) und MUSS exakt erhalten - bleiben, damit Schriftstücke, Kuverts und die Sammel-PDF zueinander passen.""" +def _set_pending(dx, machine_id, fmt): + """Angefordertes nächstes Format setzen/löschen (für Board-Bestätigung).""" + try: + dx.patch(f"/items/machines/{machine_id}", {"pending_format": fmt}) + except Exception: + pass + + +def _naechste_jobs(dx, machine_id): + """ALLE eingereihten Jobs der Maschine – in der EXPLIZITEN Plan-Reihenfolge + (Priorität für Direktdruck, dann die lineare Position, dann Erstellzeit/ID als + Tiebreak). Der erste Job bestimmt das als Nächstes zu druckende Format; ein + Format darf mehrfach an verschiedenen Positionen stehen. Diese Reihenfolge MUSS + exakt erhalten bleiben, damit Schriftstücke, Kuverts und Sammel-PDF zusammenpassen.""" return dx.get( "/items/jobs", **{ "filter[machine][_eq]": machine_id, "filter[status][_eq]": "queued", - "filter[format][_eq]": current_format, - "sort": "-priority,date_created,id", + "sort": "-priority,position,date_created,id", "limit": -1, - "fields": "id,type,output_ref,params,numbers,spaltenweise," + "fields": "id,type,format,output_ref,params,numbers,spaltenweise," "order.id,order.order_number,order.needs_envelope," "template.width,template.height," "template.xpos,template.ypos,template.scale,template.rotation", @@ -450,7 +470,14 @@ def _sende_batch(dx, plotter, jobs, KIND): Dokument). Markiert alle als „printing". Gibt einen Sammel-Kontext zum späteren Abschluss zurück (oder None, wenn nichts gesendet wurde).""" ref = _batch_key(jobs[0]) - batch = [j for j in jobs if _batch_key(j) == ref] + ref_fmt = (jobs[0].get("format") or "").strip() + # Nur der zusammenhängende Lauf VOM ANFANG mit gleichem Format + gleicher + # Platzierung – an einer Format-/Platzierungsgrenze stoppen (lineare Reihenfolge). + batch = [] + for j in jobs: + if (j.get("format") or "").strip() != ref_fmt or _batch_key(j) != ref: + break + batch.append(j) tpl = _eff_tpl(batch[0]) dateien = [] infos = []