"feat: Implement live Drucker-System monitoring
This commit is contained in:
parent
0e5ac61135
commit
1d3bb5ea8f
@ -6198,6 +6198,24 @@ if __name__ == "__main__":
|
||||
# Template-Hilfsfunktionen registrieren
|
||||
register_template_helpers(app)
|
||||
|
||||
# Drucker-Monitor Steckdosen-Initialisierung beim Start
|
||||
try:
|
||||
app_logger.info("🖨️ Starte automatische Steckdosen-Initialisierung...")
|
||||
initialization_results = printer_monitor.initialize_all_outlets_on_startup()
|
||||
|
||||
if initialization_results:
|
||||
success_count = sum(1 for success in initialization_results.values() if success)
|
||||
total_count = len(initialization_results)
|
||||
app_logger.info(f"✅ Steckdosen-Initialisierung: {success_count}/{total_count} Drucker erfolgreich")
|
||||
|
||||
if success_count < total_count:
|
||||
app_logger.warning(f"⚠️ {total_count - success_count} Drucker konnten nicht initialisiert werden")
|
||||
else:
|
||||
app_logger.info("ℹ️ Keine Drucker zur Initialisierung gefunden")
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ Fehler bei automatischer Steckdosen-Initialisierung: {str(e)}")
|
||||
|
||||
# Queue-Manager für automatische Drucker-Überwachung starten
|
||||
# Nur im Produktionsmodus starten (nicht im Debug-Modus)
|
||||
if not debug_mode:
|
||||
|
1
backend/app/docs/live_drucker_system.md
Normal file
1
backend/app/docs/live_drucker_system.md
Normal file
@ -0,0 +1 @@
|
||||
|
456
backend/app/static/js/printer_monitor.js
Normal file
456
backend/app/static/js/printer_monitor.js
Normal file
@ -0,0 +1,456 @@
|
||||
/**
|
||||
* 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();
|
||||
|
||||
Object.values(data.printers).forEach(printer => {
|
||||
this.printers.set(printer.id, {
|
||||
...printer,
|
||||
statusInfo: this.statusCategories[printer.status] || this.statusCategories['offline']
|
||||
});
|
||||
});
|
||||
|
||||
this.lastUpdate = new Date(data.timestamp);
|
||||
|
||||
// Ä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,
|
||||
standby: 0,
|
||||
unreachable: 0,
|
||||
unconfigured: 0
|
||||
};
|
||||
|
||||
this.printers.forEach(printer => {
|
||||
const status = printer.status;
|
||||
if (summary.hasOwnProperty(status)) {
|
||||
summary[status]++;
|
||||
} else {
|
||||
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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user