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 <noreply@anthropic.com>
This commit is contained in:
s4luorth
2026-10-01 13:41:22 +02:00
parent e40a6ff096
commit a29ab6d6b4

View File

@@ -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 = []