class GPONDashboard { constructor() { this.updateInterval = 5000; this.init(); } init() { this.updateCurrent(); this.updateAlerts(); setInterval(() => this.updateCurrent(), this.updateInterval); setInterval(() => this.updateAlerts(), 10000); console.log('[Dashboard] Initialized'); } async updateCurrent() { try { const response = await fetch('/api/current'); const data = await response.json(); console.log('[Dashboard] Data received:', data); this.updateMetrics(data); this.updateDeviceInfo(data); this.updateStatus(data); } catch (error) { console.error('[Dashboard] Error:', error); this.updateStatus({ status: 'error' }); } } updateMetrics(data) { if (data.rx_power !== undefined && data.rx_power !== null) { document.getElementById('rx-power').textContent = data.rx_power.toFixed(2); const rxPercent = ((data.rx_power + 30) / 22) * 100; const bar = document.getElementById('rx-power-bar'); bar.style.width = Math.max(0, Math.min(100, rxPercent)) + '%'; if (data.rx_power < -28) { bar.className = 'progress-bar bg-danger'; } else if (data.rx_power < -25) { bar.className = 'progress-bar bg-warning'; } else { bar.className = 'progress-bar bg-info'; } } if (data.tx_power !== undefined && data.tx_power !== null) { document.getElementById('tx-power').textContent = data.tx_power.toFixed(2); const txPercent = ((data.tx_power + 5) / 10) * 100; document.getElementById('tx-power-bar').style.width = Math.max(0, Math.min(100, txPercent)) + '%'; } if (data.temperature !== undefined && data.temperature !== null) { document.getElementById('temperature').textContent = data.temperature.toFixed(1); const tempPercent = (data.temperature / 100) * 100; const bar = document.getElementById('temp-bar'); bar.style.width = Math.max(0, Math.min(100, tempPercent)) + '%'; if (data.temperature > 80) { bar.className = 'progress-bar bg-danger'; } else if (data.temperature > 60) { bar.className = 'progress-bar bg-warning'; } else { bar.className = 'progress-bar bg-success'; } } const uptimeElem = document.getElementById('uptime'); if (uptimeElem) { if (data.uptime && data.uptime > 0) { const seconds = parseInt(data.uptime); const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); let uptimeStr = ''; if (days > 0) uptimeStr += days + 'd '; if (hours > 0 || days > 0) uptimeStr += hours + 'h '; uptimeStr += minutes + 'm'; uptimeElem.textContent = uptimeStr.trim(); } else { uptimeElem.textContent = '--'; } } } updateDeviceInfo(data) { this.setElementText('vendor-id', data.vendor_id); this.setElementText('model', data.model); this.setElementText('serial-number', data.serial_number); this.setElementText('version', data.version); this.setElementText('hw-version', data.hw_version); this.setElementText('mac-address', data.mac_address); this.setElementText('omcc-version', data.omcc_version); this.setElementText('olt-mode', data.olt_mode); this.setElementText('pon-mode', data.pon_mode); this.setElementText('lan-mode', data.lan_mode); this.setElementText('olt-vendor-info', data.olt_vendor_info); this.setElementText('olt-version-info', data.olt_version_info); this.setElementText('rx-packets', (data.rx_packets || 0).toLocaleString('en-US')); this.setElementText('tx-packets', (data.tx_packets || 0).toLocaleString('en-US')); this.setElementText('fec-corrected', (data.fec_corrected || 0).toLocaleString('en-US')); this.setElementText('fec-uncorrected', (data.fec_uncorrected || 0).toLocaleString('en-US')); if (data.voltage !== undefined && data.voltage !== null) { this.setElementText('voltage', data.voltage.toFixed(2) + ' V'); } else { this.setElementText('voltage', 'N/A'); } if (data.tx_bias_current !== undefined && data.tx_bias_current !== null) { this.setElementText('tx-bias', data.tx_bias_current.toFixed(2) + ' mA'); } else { this.setElementText('tx-bias', 'N/A'); } const volumeElem = document.getElementById('data-volume-total'); if (volumeElem) { if (data.rx_bytes && data.tx_bytes) { const totalBytes = parseInt(data.rx_bytes) + parseInt(data.tx_bytes); volumeElem.textContent = this.formatBytes(totalBytes); } else { volumeElem.textContent = '--'; } } const statusElem = document.getElementById('device-status'); if (statusElem) { if (data.status === 'online') { statusElem.innerHTML = ' Online'; } else { statusElem.innerHTML = ' Offline'; } } const lastUpdateElem = document.getElementById('last-update'); if (lastUpdateElem) { if (data.timestamp) { try { const date = new Date(data.timestamp); if (!isNaN(date.getTime())) { lastUpdateElem.textContent = date.toLocaleTimeString('en-US'); } else { lastUpdateElem.textContent = '--'; } } catch (e) { console.error('[Dashboard] Timestamp error:', e); lastUpdateElem.textContent = '--'; } } else { lastUpdateElem.textContent = '--'; } } } updateStatus(data) { const indicator = document.getElementById('status-indicator'); if (!indicator) return; if (data.status === 'online') { indicator.innerHTML = ' Online'; } else if (data.status === 'error') { indicator.innerHTML = ' Error'; } else { indicator.innerHTML = ' Unknown'; } } async updateAlerts() { try { const response = await fetch('/api/alerts'); const alerts = await response.json(); const container = document.getElementById('alerts-container'); if (!container || !alerts || alerts.length === 0) return; container.innerHTML = ''; alerts.forEach(alert => { const alertDiv = document.createElement('div'); let alertClass = 'alert-warning'; if (alert.severity === 'critical') alertClass = 'alert-danger'; else if (alert.severity === 'info') alertClass = 'alert-info'; alertDiv.className = `alert ${alertClass} alert-dismissible fade show`; const icon = alert.severity === 'critical' ? 'bi-exclamation-octagon' : (alert.severity === 'info' ? 'bi-info-circle' : 'bi-exclamation-triangle'); let timestamp = ''; if (alert.timestamp) { try { const date = new Date(alert.timestamp); timestamp = date.toLocaleTimeString('en-US'); } catch (e) {} } const category = (alert.category || 'system').toUpperCase(); alertDiv.innerHTML = ` ${category}: ${alert.message} ${timestamp ? `${timestamp}` : ''} `; container.appendChild(alertDiv); }); } catch (error) { console.error('[Dashboard] Error updating alerts:', error); } } setElementText(id, value) { const elem = document.getElementById(id); if (elem) { elem.textContent = (value !== undefined && value !== null && value !== '') ? value : '--'; } } formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 B'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } } document.addEventListener('DOMContentLoaded', () => { window.dashboard = new GPONDashboard(); });