"Update template files and add windows fixes"

This commit is contained in:
2025-05-29 12:54:24 +02:00
parent 5707d577cc
commit ad92d3d978
7 changed files with 443 additions and 98 deletions

View File

@@ -9,6 +9,8 @@ import logging
import subprocess
import os
import requests
import signal
import atexit
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Tuple
from contextlib import contextmanager
@@ -65,44 +67,85 @@ def check_printer_status(ip_address: str, timeout: int = 5) -> Tuple[str, bool]:
class PrinterQueueManager:
"""
Verwaltet die Warteschlangen für offline Drucker und überwacht deren Status.
Verbesserte Version mit ordnungsgemäßem Thread-Management für Windows.
"""
def __init__(self):
self.is_running = False
self.monitor_thread = None
self.shutdown_event = threading.Event() # Sauberes Shutdown-Signal
self.check_interval = 120 # 2 Minuten zwischen Status-Checks
self.last_status_cache = {} # Cache für letzten bekannten Status
self.notification_cooldown = {} # Verhindert Spam-Benachrichtigungen
self._lock = threading.Lock() # Thread-Sicherheit
# Windows-spezifische Signal-Handler registrieren
if os.name == 'nt':
signal.signal(signal.SIGINT, self._signal_handler)
signal.signal(signal.SIGTERM, self._signal_handler)
def _signal_handler(self, signum, frame):
"""Signal-Handler für ordnungsgemäßes Shutdown."""
queue_logger.warning(f"🛑 Signal {signum} empfangen - stoppe Queue Manager...")
self.stop()
def start(self):
"""Startet den Queue-Manager."""
if not self.is_running:
self.is_running = True
self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
self.monitor_thread.start()
queue_logger.info("✅ Printer Queue Manager erfolgreich gestartet")
"""Startet den Queue-Manager mit verbessertem Thread-Management."""
with self._lock:
if not self.is_running:
self.is_running = True
self.shutdown_event.clear()
self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=False)
self.monitor_thread.start()
queue_logger.info("✅ Printer Queue Manager erfolgreich gestartet")
def stop(self):
"""Stoppt den Queue-Manager."""
self.is_running = False
if self.monitor_thread and self.monitor_thread.is_alive():
self.monitor_thread.join(timeout=5)
queue_logger.info("❌ Printer Queue Manager gestoppt")
"""Stoppt den Queue-Manager ordnungsgemäß."""
with self._lock:
if self.is_running:
queue_logger.info("🔄 Beende Queue Manager...")
self.is_running = False
self.shutdown_event.set()
if self.monitor_thread and self.monitor_thread.is_alive():
queue_logger.debug("⏳ Warte auf Thread-Beendigung...")
self.monitor_thread.join(timeout=10)
if self.monitor_thread.is_alive():
queue_logger.warning("⚠️ Thread konnte nicht ordnungsgemäß beendet werden")
else:
queue_logger.info("✅ Monitor-Thread erfolgreich beendet")
self.monitor_thread = None
queue_logger.info("❌ Printer Queue Manager gestoppt")
def _monitor_loop(self):
"""Hauptschleife für die Überwachung der Drucker."""
"""Hauptschleife für die Überwachung der Drucker mit verbessertem Shutdown-Handling."""
queue_logger.info(f"🔄 Queue-Überwachung gestartet (Intervall: {self.check_interval} Sekunden)")
while self.is_running:
while self.is_running and not self.shutdown_event.is_set():
try:
self._check_waiting_jobs()
time.sleep(self.check_interval)
# Verwende Event.wait() statt time.sleep() für unterbrechbares Warten
if self.shutdown_event.wait(timeout=self.check_interval):
# Shutdown-Signal erhalten
queue_logger.info("🛑 Shutdown-Signal empfangen - beende Monitor-Loop")
break
except Exception as e:
queue_logger.error(f"❌ Fehler in Monitor-Schleife: {str(e)}")
time.sleep(30) # Kürzere Wartezeit bei Fehlern
# Kürzere Wartezeit bei Fehlern, aber auch unterbrechbar
if self.shutdown_event.wait(timeout=30):
break
queue_logger.info("🔚 Monitor-Loop beendet")
def _check_waiting_jobs(self):
"""Überprüft alle wartenden Jobs und aktiviert sie bei verfügbaren Druckern."""
if self.shutdown_event.is_set():
return
db_session = get_db_session()
try:
@@ -119,6 +162,10 @@ class PrinterQueueManager:
activated_jobs = []
for job in waiting_jobs:
# Shutdown-Check zwischen Jobs
if self.shutdown_event.is_set():
break
# Drucker-Status prüfen
printer = db_session.query(Printer).get(job.printer_id)
if not printer:
@@ -181,9 +228,10 @@ class PrinterQueueManager:
db_session.commit()
queue_logger.info(f"{len(activated_jobs)} Jobs erfolgreich aktiviert")
# Benachrichtigungen versenden
for item in activated_jobs:
self._send_job_activation_notification(item["job"], item["printer"])
# Benachrichtigungen versenden (nur wenn nicht im Shutdown)
if not self.shutdown_event.is_set():
for item in activated_jobs:
self._send_job_activation_notification(item["job"], item["printer"])
else:
# Auch offline-Status speichern
db_session.commit()
@@ -196,6 +244,9 @@ class PrinterQueueManager:
def _send_job_activation_notification(self, job: Job, printer: Printer):
"""Sendet eine Benachrichtigung, wenn ein Job aktiviert wird."""
if self.shutdown_event.is_set():
return
try:
# Cooldown prüfen (keine Spam-Benachrichtigungen)
cooldown_key = f"job_{job.id}_activated"
@@ -284,16 +335,25 @@ class PrinterQueueManager:
}
finally:
db_session.close()
def is_healthy(self) -> bool:
"""Prüft, ob der Queue Manager ordnungsgemäß läuft."""
return (self.is_running and
self.monitor_thread is not None and
self.monitor_thread.is_alive() and
not self.shutdown_event.is_set())
# Globale Instanz des Queue-Managers
_queue_manager_instance = None
_queue_manager_lock = threading.Lock()
def get_queue_manager() -> PrinterQueueManager:
"""Gibt die globale Instanz des Queue-Managers zurück."""
global _queue_manager_instance
if _queue_manager_instance is None:
_queue_manager_instance = PrinterQueueManager()
return _queue_manager_instance
with _queue_manager_lock:
if _queue_manager_instance is None:
_queue_manager_instance = PrinterQueueManager()
return _queue_manager_instance
def start_queue_manager():
"""Startet den globalen Queue-Manager."""
@@ -304,6 +364,10 @@ def start_queue_manager():
def stop_queue_manager():
"""Stoppt den globalen Queue-Manager."""
global _queue_manager_instance
if _queue_manager_instance:
_queue_manager_instance.stop()
_queue_manager_instance = None
with _queue_manager_lock:
if _queue_manager_instance:
_queue_manager_instance.stop()
_queue_manager_instance = None
# Automatisches Cleanup bei Prozess-Ende registrieren
atexit.register(stop_queue_manager)