Erkannte Plotter-Fehler an Directus melden (agent_logs)
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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,87 @@
|
|||||||
"""
|
"""
|
||||||
AutoRecover - Drucker Fehler Handler
|
AutoRecover - Drucker Fehler Handler
|
||||||
Erkennt das "Error"-Fenster (Java AWT) und klickt automatisch "Recover" (max. 3x).
|
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 win32gui
|
||||||
import win32con
|
import win32con
|
||||||
import ctypes
|
import ctypes
|
||||||
import time
|
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
|
MAX_VERSUCHE = 3
|
||||||
PRUEF_INTERVAL = 2 # Sekunden zwischen den Checks
|
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):
|
def click_recover_button(hwnd):
|
||||||
"""
|
"""
|
||||||
Holt die Fensterposition und klickt auf den Recover-Button.
|
Holt die Fensterposition und klickt auf den Recover-Button.
|
||||||
@@ -47,13 +118,16 @@ def main():
|
|||||||
|
|
||||||
if hwnd and win32gui.IsWindowVisible(hwnd):
|
if hwnd and win32gui.IsWindowVisible(hwnd):
|
||||||
versuche += 1
|
versuche += 1
|
||||||
|
meldung = fenster_text(hwnd) or "Error"
|
||||||
print(f"[Versuch {versuche}/{MAX_VERSUCHE}] Fehlerfenster gefunden!")
|
print(f"[Versuch {versuche}/{MAX_VERSUCHE}] Fehlerfenster gefunden!")
|
||||||
|
dx_log("error", f"Plotter-Fehlerfenster erkannt (Versuch {versuche}/{MAX_VERSUCHE}): {meldung}")
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
click_recover_button(hwnd)
|
click_recover_button(hwnd)
|
||||||
|
|
||||||
if versuche >= MAX_VERSUCHE:
|
if versuche >= MAX_VERSUCHE:
|
||||||
print(f"\nMaximale Versuche ({MAX_VERSUCHE}) erreicht. Programm beendet.")
|
print(f"\nMaximale Versuche ({MAX_VERSUCHE}) erreicht. Programm beendet.")
|
||||||
|
dx_log("error", f"Maximale Recover-Versuche ({MAX_VERSUCHE}) erreicht – manueller Eingriff nötig.")
|
||||||
break
|
break
|
||||||
|
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|||||||
Reference in New Issue
Block a user