/** * Live-Drucker-Monitor für MYP Platform * Verwaltet Live-Status-Updates mit Session-Caching und automatischer Aktualisierung */ class PrinterMonitor { constructor() { this.refreshInterval = 30000; // 30 Sekunden Standard-Intervall this.fastRefreshInterval = 5000; // 5 Sekunden für schnelle Updates this.currentInterval = null; this.printers = new Map(); this.callbacks = new Set(); this.isActive = false; this.useCache = true; this.lastUpdate = null; this.errorCount = 0; this.maxErrors = 3; // Status-Kategorien für bessere Übersicht this.statusCategories = { 'online': { label: 'Online', color: 'success', icon: '🟢' }, 'offline': { label: 'Offline', color: 'danger', icon: '🔴' }, 'standby': { label: 'Standby', color: 'warning', icon: '🟡' }, 'unreachable': { label: 'Nicht erreichbar', color: 'secondary', icon: '⚫' }, 'unconfigured': { label: 'Nicht konfiguriert', color: 'info', icon: '🔵' } }; console.log('🖨️ PrinterMonitor initialisiert'); } /** * Startet das Live-Monitoring */ start() { if (this.isActive) { console.log('⚠️ PrinterMonitor läuft bereits'); return; } this.isActive = true; this.errorCount = 0; console.log('🚀 Starte PrinterMonitor'); // Sofortige erste Aktualisierung this.updatePrinterStatus(); // Reguläres Intervall starten this.startInterval(); // Event-Listener für Sichtbarkeitsänderungen document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); } /** * Stoppt das Live-Monitoring */ stop() { if (!this.isActive) { return; } this.isActive = false; if (this.currentInterval) { clearInterval(this.currentInterval); this.currentInterval = null; } document.removeEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); console.log('⏹️ PrinterMonitor gestoppt'); } /** * Startet das Update-Intervall */ startInterval() { if (this.currentInterval) { clearInterval(this.currentInterval); } this.currentInterval = setInterval(() => { this.updatePrinterStatus(); }, this.refreshInterval); } /** * Behandelt Sichtbarkeitsänderungen der Seite */ handleVisibilityChange() { if (document.hidden) { // Seite nicht sichtbar - langsamere Updates this.refreshInterval = 60000; // 1 Minute } else { // Seite sichtbar - normale Updates this.refreshInterval = 30000; // 30 Sekunden // Sofortige Aktualisierung wenn Seite wieder sichtbar this.updatePrinterStatus(); } if (this.isActive) { this.startInterval(); } } /** * Holt aktuelle Drucker-Status-Daten */ async updatePrinterStatus() { if (!this.isActive) { return; } try { const response = await fetch(`/api/printers/monitor/live-status?use_cache=${this.useCache}`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.success) { this.processPrinterData(data); this.errorCount = 0; // Reset error count on success } else { throw new Error(data.error || 'Unbekannter Fehler'); } } catch (error) { this.errorCount++; console.error('❌ Fehler beim Abrufen des Drucker-Status:', error); // Bei wiederholten Fehlern weniger frequent versuchen if (this.errorCount >= this.maxErrors) { this.refreshInterval = Math.min(this.refreshInterval * 2, 300000); // Max 5 Minuten this.startInterval(); this.notifyCallbacks({ type: 'error', message: `Drucker-Status nicht verfügbar (${this.errorCount} Fehler)`, timestamp: new Date().toISOString() }); } } } /** * Verarbeitet empfangene Drucker-Daten */ processPrinterData(data) { const previousPrinters = new Map(this.printers); // Drucker-Daten aktualisieren this.printers.clear(); // Flexible Datenextraktion für verschiedene API-Response-Strukturen let printersData = null; if (data && data.printers && typeof data.printers === 'object') { // Alte Struktur: data.printers printersData = data.printers; } else if (data && data.status && typeof data.status === 'object') { // Neue Struktur: data.status printersData = data.status; } else if (data && typeof data === 'object' && !data.success && !data.error) { // Direkte Drucker-Daten ohne Wrapper printersData = data; } if (printersData && typeof printersData === 'object') { // Drucker-Daten verarbeiten Object.values(printersData).forEach(printer => { // Sichere Validierung der Drucker-Objekte if (printer && typeof printer === 'object' && printer.id) { this.printers.set(printer.id, { ...printer, statusInfo: this.statusCategories[printer.status] || this.statusCategories['offline'] }); } else { console.warn('⚠️ Ungültiges Drucker-Objekt übersprungen:', printer); } }); console.log(`✅ ${this.printers.size} Drucker erfolgreich verarbeitet`); } else { console.warn('⚠️ Keine gültigen Drucker-Daten in Response-Struktur gefunden'); console.debug('Response-Struktur:', data); // Benachrichtige Callbacks über fehlende Daten (aber nicht als Fehler) this.notifyCallbacks({ type: 'warning', message: 'Keine Drucker-Daten verfügbar', data: data }); return; } this.lastUpdate = new Date(data.timestamp || Date.now()); // Änderungen erkennen und benachrichtigen const changes = this.detectChanges(previousPrinters, this.printers); // Callbacks benachrichtigen this.notifyCallbacks({ type: 'update', printers: this.printers, summary: data.summary, changes: changes, timestamp: this.lastUpdate, cacheUsed: data.cache_used }); console.log(`🔄 Drucker-Status aktualisiert: ${this.printers.size} Drucker`); } /** * Erkennt Änderungen zwischen zwei Drucker-Status-Maps */ detectChanges(oldPrinters, newPrinters) { const changes = []; newPrinters.forEach((newPrinter, id) => { const oldPrinter = oldPrinters.get(id); if (!oldPrinter) { changes.push({ type: 'added', printer: newPrinter }); } else if (oldPrinter.status !== newPrinter.status) { changes.push({ type: 'status_change', printer: newPrinter, oldStatus: oldPrinter.status, newStatus: newPrinter.status }); } }); oldPrinters.forEach((oldPrinter, id) => { if (!newPrinters.has(id)) { changes.push({ type: 'removed', printer: oldPrinter }); } }); return changes; } /** * Benachrichtigt alle registrierten Callbacks */ notifyCallbacks(data) { this.callbacks.forEach(callback => { try { callback(data); } catch (error) { console.error('❌ Fehler in PrinterMonitor Callback:', error); } }); } /** * Registriert einen Callback für Status-Updates */ onUpdate(callback) { if (typeof callback === 'function') { this.callbacks.add(callback); } } /** * Entfernt einen Callback */ offUpdate(callback) { this.callbacks.delete(callback); } /** * Erzwingt eine sofortige Aktualisierung ohne Cache */ async forceUpdate() { const oldUseCache = this.useCache; this.useCache = false; try { await this.updatePrinterStatus(); } finally { this.useCache = oldUseCache; } } /** * Löscht den Cache und erzwingt eine Aktualisierung */ async clearCache() { try { const response = await fetch('/api/printers/monitor/clear-cache', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }); if (response.ok) { console.log('🧹 Drucker-Cache geleert'); await this.forceUpdate(); } else { throw new Error(`HTTP ${response.status}`); } } catch (error) { console.error('❌ Fehler beim Leeren des Caches:', error); } } /** * Holt eine schnelle Status-Zusammenfassung */ async getSummary() { try { const response = await fetch('/api/printers/monitor/summary', { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }); if (response.ok) { const data = await response.json(); return data.success ? data.summary : null; } } catch (error) { console.error('❌ Fehler beim Abrufen der Zusammenfassung:', error); } return null; } /** * Gibt den aktuellen Status eines Druckers zurück */ getPrinter(id) { return this.printers.get(id); } /** * Gibt alle Drucker zurück */ getAllPrinters() { return Array.from(this.printers.values()); } /** * Gibt Drucker nach Status gefiltert zurück */ getPrintersByStatus(status) { return this.getAllPrinters().filter(printer => printer.status === status); } /** * Gibt eine Status-Zusammenfassung zurück */ getStatusSummary() { const summary = { total: this.printers.size, online: 0, offline: 0, printing: 0, // Neuer Status: Drucker druckt gerade standby: 0, unreachable: 0, unconfigured: 0, error: 0 // Status für unbekannte Fehler }; this.printers.forEach(printer => { const status = printer.status; if (summary.hasOwnProperty(status)) { summary[status]++; } else { // Fallback für unbekannte Status summary.offline++; } }); return summary; } /** * Aktiviert schnelle Updates (für kritische Operationen) */ enableFastUpdates() { this.refreshInterval = this.fastRefreshInterval; if (this.isActive) { this.startInterval(); } console.log('⚡ Schnelle Updates aktiviert'); } /** * Deaktiviert schnelle Updates */ disableFastUpdates() { this.refreshInterval = 30000; // Zurück zu normal if (this.isActive) { this.startInterval(); } console.log('🐌 Normale Updates aktiviert'); } /** * Initialisiert alle Steckdosen (nur für Admins) */ async initializeAllOutlets() { try { const response = await fetch('/api/printers/monitor/initialize-outlets', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }); if (response.ok) { const data = await response.json(); if (data.success) { console.log('🔌 Steckdosen-Initialisierung erfolgreich:', data.statistics); // Benachrichtige über Initialisierung this.notifyCallbacks({ type: 'initialization', results: data.results, statistics: data.statistics, message: data.message }); // Erzwinge Update nach Initialisierung await this.forceUpdate(); return data; } else { throw new Error(data.error || 'Initialisierung fehlgeschlagen'); } } else { throw new Error(`HTTP ${response.status}`); } } catch (error) { console.error('❌ Fehler bei Steckdosen-Initialisierung:', error); throw error; } } } // Globale Instanz window.printerMonitor = new PrinterMonitor(); // Auto-Start wenn DOM bereit ist document.addEventListener('DOMContentLoaded', () => { // Nur starten wenn wir auf einer relevanten Seite sind const relevantPages = ['/printers', '/dashboard', '/admin', '/admin-dashboard']; const currentPath = window.location.pathname; if (relevantPages.some(page => currentPath.includes(page))) { console.log('🖨️ Auto-Start PrinterMonitor für Seite:', currentPath); window.printerMonitor.start(); } }); // Automatisches Cleanup bei Seitenverlassen window.addEventListener('beforeunload', () => { if (window.printerMonitor) { window.printerMonitor.stop(); } }); // Export für Module if (typeof module !== 'undefined' && module.exports) { module.exports = PrinterMonitor; }