From f75957c15e6cb7029b5220d22d7c777233d8840a Mon Sep 17 00:00:00 2001 From: s4luorth Date: Fri, 11 Sep 2026 19:58:14 +0200 Subject: [PATCH] Erkannte Plotter-Fehler an Directus melden (agent_logs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Directus-Zugang aus eigener config.json, der Agenten-config.json oder Env-Vars. Bei erkanntem Error-Fenster + bei Erreichen der max. Recover-Versuche wird die Fehlermeldung (inkl. Fenstertext) nach agent_logs (source=recover) gesendet. requests ist optional – ohne läuft AutoRecover wie bisher (nur ohne Directus-Log). Co-Authored-By: Claude Opus 4.8 --- auto_recover.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/auto_recover.py b/auto_recover.py index 43a3fa6..f11af72 100644 --- a/auto_recover.py +++ b/auto_recover.py @@ -1,16 +1,87 @@ """ AutoRecover - Drucker Fehler Handler Erkennt das "Error"-Fenster (Java AWT) und klickt automatisch "Recover" (max. 3x). + +Zusätzlich werden die erkannten Fehlermeldungen (best-effort) an Directus gemeldet +(Collection agent_logs, source="recover"). Die Directus-Zugangsdaten kommen aus +einer config.json neben diesem Skript ODER aus der des Agenten +(../skrift-agent/config.json) ODER aus Umgebungsvariablen +(SKRIFT_DIRECTUS_URL / SKRIFT_DIRECTUS_TOKEN / SKRIFT_MACHINE_ID). """ import win32gui import win32con import ctypes import time +import os +import json + +try: + import requests +except Exception: # requests optional – ohne läuft AutoRecover trotzdem (nur ohne Directus-Log) + requests = None MAX_VERSUCHE = 3 PRUEF_INTERVAL = 2 # Sekunden zwischen den Checks +_HIER = os.path.dirname(os.path.abspath(__file__)) + + +def _lade_directus_cfg(): + """Directus-Zugang aus AutoRecover- oder Agenten-config.json bzw. Env-Variablen.""" + for pfad in (os.path.join(_HIER, "config.json"), + os.path.join(_HIER, "..", "skrift-agent", "config.json")): + try: + with open(pfad, "r", encoding="utf-8") as f: + c = json.load(f) + if c.get("directus_url") and c.get("directus_token"): + return c.get("directus_url"), c.get("directus_token"), c.get("machine_id") + except Exception: + pass + url = os.environ.get("SKRIFT_DIRECTUS_URL") + token = os.environ.get("SKRIFT_DIRECTUS_TOKEN") + mid = os.environ.get("SKRIFT_MACHINE_ID") + return url, token, (int(mid) if mid and str(mid).isdigit() else None) + + +_DX_URL, _DX_TOKEN, _DX_MID = _lade_directus_cfg() + + +def dx_log(level, message): + """Meldet ein Ereignis an Directus (agent_logs). Fehler nie fatal.""" + if not (requests and _DX_URL and _DX_TOKEN): + return + try: + requests.post( + _DX_URL.rstrip("/") + "/items/agent_logs", + headers={"Authorization": f"Bearer {_DX_TOKEN}"}, + json={"machine": _DX_MID, "level": level, "source": "recover", + "message": str(message)[:2000]}, + timeout=15, + ) + except Exception: + pass + + +def fenster_text(hwnd): + """Liest die Beschriftungen der Kind-Fenster (die eigentliche Fehlermeldung).""" + teile = [] + + def _cb(h, _): + try: + t = win32gui.GetWindowText(h) + if t and t.strip(): + teile.append(t.strip()) + except Exception: + pass + return True + + try: + win32gui.EnumChildWindows(hwnd, _cb, None) + except Exception: + pass + return " / ".join(teile)[:500] + def click_recover_button(hwnd): """ Holt die Fensterposition und klickt auf den Recover-Button. @@ -47,13 +118,16 @@ def main(): if hwnd and win32gui.IsWindowVisible(hwnd): versuche += 1 + meldung = fenster_text(hwnd) or "Error" print(f"[Versuch {versuche}/{MAX_VERSUCHE}] Fehlerfenster gefunden!") + dx_log("error", f"Plotter-Fehlerfenster erkannt (Versuch {versuche}/{MAX_VERSUCHE}): {meldung}") time.sleep(0.5) click_recover_button(hwnd) if versuche >= MAX_VERSUCHE: print(f"\nMaximale Versuche ({MAX_VERSUCHE}) erreicht. Programm beendet.") + dx_log("error", f"Maximale Recover-Versuche ({MAX_VERSUCHE}) erreicht – manueller Eingriff nötig.") break time.sleep(3)