It appears that you've made several changes to your project's directory structure and files. Here's a summary of the actions:
This commit is contained in:
@@ -998,12 +998,36 @@ class TapoController:
|
||||
# ===== PRINTER MONITOR =====
|
||||
|
||||
class PrinterMonitor:
|
||||
"""3D-Drucker Monitor"""
|
||||
"""3D-Drucker Monitor mit Status-Management und Session-Caching"""
|
||||
|
||||
# Status-Konstanten
|
||||
STATUS_ON = "on"
|
||||
STATUS_OFF = "off"
|
||||
STATUS_UNREACHABLE = "unreachable"
|
||||
|
||||
# Status-Mapping für UI
|
||||
STATUS_DISPLAY = {
|
||||
STATUS_ON: {"text": "An", "color": "green", "icon": "power"},
|
||||
STATUS_OFF: {"text": "Aus", "color": "gray", "icon": "power-off"},
|
||||
STATUS_UNREACHABLE: {"text": "Nicht erreichbar", "color": "red", "icon": "exclamation-triangle"}
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.cache = {}
|
||||
self._cache_timeout = 300 # 5 Minuten Cache
|
||||
hardware_logger.info("✅ Printer Monitor initialisiert")
|
||||
self._cache_lock = threading.RLock()
|
||||
self._last_check = {}
|
||||
self.check_interval = 30 # Sekunden zwischen Status-Checks
|
||||
|
||||
# Session-spezifischer Status-Cache für Benutzer-Sessions
|
||||
self._session_cache = {}
|
||||
self._session_cache_lock = threading.RLock()
|
||||
self._session_cache_ttl = 300 # 5 Minuten für Session-Cache
|
||||
|
||||
# Thread-Pool für asynchrone Operationen
|
||||
self._executor = ThreadPoolExecutor(max_workers=6)
|
||||
|
||||
hardware_logger.info("✅ Printer Monitor mit Session-Caching initialisiert")
|
||||
|
||||
def get_live_printer_status(self, use_session_cache: bool = True) -> Dict[int, Dict]:
|
||||
"""
|
||||
@@ -1148,8 +1172,343 @@ class PrinterMonitor:
|
||||
|
||||
def clear_all_caches(self):
|
||||
"""Leert alle Caches des Printer Monitors."""
|
||||
self.cache.clear()
|
||||
hardware_logger.debug("Printer Monitor Cache geleert")
|
||||
with self._cache_lock:
|
||||
self.cache.clear()
|
||||
self._last_check.clear()
|
||||
with self._session_cache_lock:
|
||||
self._session_cache.clear()
|
||||
hardware_logger.debug("Alle Printer Monitor Caches geleert")
|
||||
|
||||
def control_plug(self, printer_id: int, action: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Steuert eine Tapo-Steckdose über den TapoController
|
||||
|
||||
Args:
|
||||
printer_id: ID des Druckers
|
||||
action: "on" oder "off"
|
||||
|
||||
Returns:
|
||||
Tuple (Erfolg, Nachricht)
|
||||
"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
|
||||
|
||||
if not printer:
|
||||
return False, "Drucker nicht gefunden"
|
||||
|
||||
if not printer.plug_ip:
|
||||
return False, "Keine Steckdose konfiguriert"
|
||||
|
||||
# Tapo-Controller verwenden
|
||||
tapo_ctrl = get_tapo_controller()
|
||||
|
||||
if not tapo_ctrl:
|
||||
return False, "Tapo-Controller nicht verfügbar"
|
||||
|
||||
# Aktion ausführen
|
||||
success = False
|
||||
if action == "on":
|
||||
success = tapo_ctrl.toggle_plug(printer.plug_ip, True)
|
||||
elif action == "off":
|
||||
success = tapo_ctrl.turn_off(printer.plug_ip, printer_id=printer_id)
|
||||
else:
|
||||
return False, f"Ungültige Aktion: {action}"
|
||||
|
||||
if success:
|
||||
# Cache invalidieren
|
||||
with self._cache_lock:
|
||||
if printer_id in self.cache:
|
||||
del self.cache[printer_id]
|
||||
if printer_id in self._last_check:
|
||||
del self._last_check[printer_id]
|
||||
|
||||
db_session.close()
|
||||
return True, f"Steckdose erfolgreich {action}"
|
||||
else:
|
||||
db_session.close()
|
||||
return False, "Steckdose konnte nicht gesteuert werden"
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Steuern der Steckdose für Drucker {printer_id}: {str(e)}")
|
||||
return False, str(e)
|
||||
|
||||
def check_and_control_for_jobs(self):
|
||||
"""
|
||||
Prüft alle Jobs und steuert Steckdosen entsprechend
|
||||
|
||||
Diese Methode sollte regelmäßig vom Scheduler aufgerufen werden
|
||||
"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
now = datetime.now()
|
||||
|
||||
# Jobs die starten sollten
|
||||
jobs_to_start = db_session.query(Job).filter(
|
||||
Job.status == "scheduled",
|
||||
Job.start_at <= now
|
||||
).all()
|
||||
|
||||
for job in jobs_to_start:
|
||||
hardware_logger.info(f"Starte Job {job.id} für Drucker {job.printer_id}")
|
||||
success, msg = self.control_plug(job.printer_id, "on")
|
||||
if success:
|
||||
job.status = "running"
|
||||
hardware_logger.info(f"Steckdose für Job {job.id} eingeschaltet")
|
||||
else:
|
||||
hardware_logger.error(f"Fehler beim Einschalten für Job {job.id}: {msg}")
|
||||
|
||||
# Jobs die enden sollten
|
||||
jobs_to_end = db_session.query(Job).filter(
|
||||
Job.status == "running",
|
||||
Job.end_at <= now
|
||||
).all()
|
||||
|
||||
for job in jobs_to_end:
|
||||
hardware_logger.info(f"Beende Job {job.id} für Drucker {job.printer_id}")
|
||||
success, msg = self.control_plug(job.printer_id, "off")
|
||||
if success:
|
||||
job.status = "finished"
|
||||
job.actual_end_time = now
|
||||
hardware_logger.info(f"Steckdose für Job {job.id} ausgeschaltet")
|
||||
else:
|
||||
hardware_logger.error(f"Fehler beim Ausschalten für Job {job.id}: {msg}")
|
||||
|
||||
db_session.commit()
|
||||
db_session.close()
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler bei der automatischen Job-Steuerung: {str(e)}")
|
||||
|
||||
def get_session_status(self, session_id: str, printer_ids: List[int] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Holt den gecachten Status für eine Session
|
||||
|
||||
Args:
|
||||
session_id: Session-ID
|
||||
printer_ids: Optional - spezifische Drucker-IDs
|
||||
|
||||
Returns:
|
||||
Dict mit Session-spezifischen Status-Daten
|
||||
"""
|
||||
try:
|
||||
with self._session_cache_lock:
|
||||
session_data = self._session_cache.get(session_id, {})
|
||||
|
||||
# Prüfe Cache-Gültigkeit
|
||||
cache_time = session_data.get('timestamp', datetime.min)
|
||||
if (datetime.now() - cache_time).total_seconds() > self._session_cache_ttl:
|
||||
# Cache abgelaufen
|
||||
self._session_cache.pop(session_id, None)
|
||||
return self._create_fresh_session_status(session_id, printer_ids)
|
||||
|
||||
# Wenn spezifische Drucker angefragt, filtere diese
|
||||
if printer_ids:
|
||||
filtered_status = {}
|
||||
for printer_id in printer_ids:
|
||||
if str(printer_id) in session_data.get('printers', {}):
|
||||
filtered_status[str(printer_id)] = session_data['printers'][str(printer_id)]
|
||||
|
||||
return {
|
||||
'timestamp': session_data['timestamp'],
|
||||
'session_id': session_id,
|
||||
'printers': filtered_status,
|
||||
'from_cache': True
|
||||
}
|
||||
|
||||
return session_data
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Abrufen des Session-Status: {str(e)}")
|
||||
return self._create_fresh_session_status(session_id, printer_ids)
|
||||
|
||||
def update_session_status(self, session_id: str, printer_id: int = None) -> bool:
|
||||
"""
|
||||
Aktualisiert den Session-Status-Cache
|
||||
|
||||
Args:
|
||||
session_id: Session-ID
|
||||
printer_id: Optional - spezifischer Drucker
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich
|
||||
"""
|
||||
try:
|
||||
with self._session_cache_lock:
|
||||
if printer_id:
|
||||
# Einzelnen Drucker aktualisieren
|
||||
printer_status = self.get_live_printer_status(use_session_cache=False)
|
||||
|
||||
if session_id not in self._session_cache:
|
||||
self._session_cache[session_id] = {
|
||||
'timestamp': datetime.now(),
|
||||
'session_id': session_id,
|
||||
'printers': {}
|
||||
}
|
||||
|
||||
self._session_cache[session_id]['printers'][str(printer_id)] = printer_status.get(printer_id, {})
|
||||
self._session_cache[session_id]['timestamp'] = datetime.now()
|
||||
else:
|
||||
# Alle Drucker aktualisieren
|
||||
self._session_cache[session_id] = self._create_fresh_session_status(session_id)
|
||||
|
||||
hardware_logger.debug(f"Session-Status für {session_id} aktualisiert")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Aktualisieren des Session-Status: {str(e)}")
|
||||
return False
|
||||
|
||||
def clear_session_cache(self, session_id: str = None) -> bool:
|
||||
"""
|
||||
Löscht Session-Cache
|
||||
|
||||
Args:
|
||||
session_id: Optional - spezifische Session, sonst alle
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich
|
||||
"""
|
||||
try:
|
||||
with self._session_cache_lock:
|
||||
if session_id:
|
||||
self._session_cache.pop(session_id, None)
|
||||
hardware_logger.debug(f"Session-Cache für {session_id} gelöscht")
|
||||
else:
|
||||
self._session_cache.clear()
|
||||
hardware_logger.debug("Kompletter Session-Cache gelöscht")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Löschen des Session-Cache: {str(e)}")
|
||||
return False
|
||||
|
||||
def _create_fresh_session_status(self, session_id: str, printer_ids: List[int] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Erstellt frischen Session-Status
|
||||
|
||||
Args:
|
||||
session_id: Session-ID
|
||||
printer_ids: Optional - spezifische Drucker-IDs
|
||||
|
||||
Returns:
|
||||
Dict mit frischen Status-Daten
|
||||
"""
|
||||
try:
|
||||
db_session = get_db_session()
|
||||
|
||||
# Alle oder spezifische Drucker laden
|
||||
if printer_ids:
|
||||
printers = db_session.query(Printer).filter(Printer.id.in_(printer_ids)).all()
|
||||
else:
|
||||
printers = db_session.query(Printer).all()
|
||||
|
||||
session_data = {
|
||||
'timestamp': datetime.now(),
|
||||
'session_id': session_id,
|
||||
'printers': {},
|
||||
'from_cache': False
|
||||
}
|
||||
|
||||
# Status für jeden Drucker abrufen
|
||||
status_dict = self.get_live_printer_status(use_session_cache=False)
|
||||
for printer in printers:
|
||||
session_data['printers'][str(printer.id)] = status_dict.get(printer.id, {})
|
||||
|
||||
# In Session-Cache speichern
|
||||
with self._session_cache_lock:
|
||||
self._session_cache[session_id] = session_data
|
||||
|
||||
db_session.close()
|
||||
return session_data
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Erstellen frischen Session-Status: {str(e)}")
|
||||
return {
|
||||
'timestamp': datetime.now(),
|
||||
'session_id': session_id,
|
||||
'printers': {},
|
||||
'error': str(e),
|
||||
'from_cache': False
|
||||
}
|
||||
|
||||
def invalidate_cache(self, printer_id: int = None) -> bool:
|
||||
"""
|
||||
Invalidiert Cache für spezifischen Drucker oder alle
|
||||
|
||||
Args:
|
||||
printer_id: Optional - spezifischer Drucker, None = alle Drucker
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich
|
||||
"""
|
||||
try:
|
||||
with self._cache_lock:
|
||||
if printer_id is not None:
|
||||
# Spezifischen Drucker-Cache löschen
|
||||
self.cache.pop(printer_id, None)
|
||||
self._last_check.pop(printer_id, None)
|
||||
hardware_logger.debug(f"Cache für Drucker {printer_id} invalidiert")
|
||||
else:
|
||||
# Alle Caches löschen
|
||||
self.cache.clear()
|
||||
self._last_check.clear()
|
||||
hardware_logger.info("Kompletter Status-Cache invalidiert")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Invalidieren des Cache: {str(e)}")
|
||||
return False
|
||||
|
||||
def force_network_refresh(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Forciert komplette Netzwerk-Neuprüfung aller Drucker
|
||||
Invalidiert alle Caches und führt echte Netzwerk-Tests durch
|
||||
|
||||
Returns:
|
||||
Dict mit Refresh-Ergebnissen
|
||||
"""
|
||||
try:
|
||||
hardware_logger.info("Starte Force-Network-Refresh für alle Drucker")
|
||||
|
||||
# Alle Caches invalidieren
|
||||
self.invalidate_cache()
|
||||
self.clear_session_cache()
|
||||
|
||||
# Tapo-Controller Cache leeren
|
||||
try:
|
||||
tapo_ctrl = get_tapo_controller()
|
||||
if tapo_ctrl and hasattr(tapo_ctrl, 'clear_cache'):
|
||||
tapo_ctrl.clear_cache()
|
||||
hardware_logger.debug("Tapo-Controller Cache geleert")
|
||||
except Exception as e:
|
||||
hardware_logger.warning(f"Tapo-Controller Cache konnte nicht geleert werden: {str(e)}")
|
||||
|
||||
# Frischen Status für alle Drucker abrufen
|
||||
fresh_status = self.get_live_printer_status(use_session_cache=False)
|
||||
|
||||
# Ergebnisse zusammenfassen
|
||||
results = {
|
||||
"success": True,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"printers_refreshed": len(fresh_status),
|
||||
"printers": fresh_status,
|
||||
"message": f"Netzwerk-Status für {len(fresh_status)} Drucker erfolgreich aktualisiert"
|
||||
}
|
||||
|
||||
hardware_logger.info(f"Force-Network-Refresh abgeschlossen: {len(fresh_status)} Drucker aktualisiert")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
hardware_logger.error(f"Fehler beim Force-Network-Refresh: {str(e)}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"message": "Fehler beim Aktualisieren der Netzwerk-Status"
|
||||
}
|
||||
|
||||
# ===== GLOBALE INSTANZEN =====
|
||||
|
||||
|
Reference in New Issue
Block a user