// Dashboard JavaScript for Calejo Control Adapter // Tab management function showTab(tabName) { // Hide all tabs document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); document.querySelectorAll('.tab-button').forEach(button => { button.classList.remove('active'); }); // Show selected tab document.getElementById(tabName + '-tab').classList.add('active'); event.target.classList.add('active'); // Load data for the tab if (tabName === 'status') { loadStatus(); } else if (tabName === 'scada') { loadSCADAStatus(); } else if (tabName === 'signals') { loadSignals(); } else if (tabName === 'logs') { loadLogs(); } } // Status loading async function loadStatus() { try { const response = await fetch('/api/v1/status'); const data = await response.json(); // Update status cards updateStatusCard('service-status', data.service_status || 'Unknown'); updateStatusCard('database-status', data.database_status || 'Unknown'); updateStatusCard('scada-status', data.scada_status || 'Unknown'); updateStatusCard('optimization-status', data.optimization_status || 'Unknown'); // Update metrics if (data.metrics) { document.getElementById('connected-devices').textContent = data.metrics.connected_devices || 0; document.getElementById('active-signals').textContent = data.metrics.active_signals || 0; document.getElementById('data-points').textContent = data.metrics.data_points || 0; } } catch (error) { console.error('Error loading status:', error); showAlert('Failed to load status', 'error'); } } function updateStatusCard(elementId, status) { const element = document.getElementById(elementId); if (element) { element.textContent = status; element.className = 'status-card'; if (status.toLowerCase() === 'running' || status.toLowerCase() === 'healthy') { element.classList.add('running'); } else if (status.toLowerCase() === 'error' || status.toLowerCase() === 'failed') { element.classList.add('error'); } else if (status.toLowerCase() === 'warning') { element.classList.add('warning'); } } } // SCADA status loading async function loadSCADAStatus() { try { const response = await fetch('/api/v1/scada/status'); const data = await response.json(); const scadaStatusDiv = document.getElementById('scada-status-details'); if (scadaStatusDiv) { scadaStatusDiv.innerHTML = `
OPC UA: ${data.opcua_enabled ? 'Enabled' : 'Disabled'}
Modbus: ${data.modbus_enabled ? 'Enabled' : 'Disabled'}
Connected Devices: ${data.connected_devices || 0}
`; } } catch (error) { console.error('Error loading SCADA status:', error); showAlert('Failed to load SCADA status', 'error'); } } // Signal discovery and management let isScanning = false; async function scanSignals() { if (isScanning) { showAlert('Scan already in progress', 'warning'); return; } try { isScanning = true; const scanButton = document.getElementById('scan-signals-btn'); if (scanButton) { scanButton.disabled = true; scanButton.textContent = 'Scanning...'; } showAlert('Starting signal discovery scan...', 'info'); const response = await fetch('/api/v1/dashboard/discovery/scan', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { showAlert('Discovery scan started successfully', 'success'); // Poll for scan completion pollScanStatus(result.scan_id); } else { showAlert('Failed to start discovery scan', 'error'); } } catch (error) { console.error('Error starting scan:', error); showAlert('Failed to start discovery scan', 'error'); } finally { isScanning = false; const scanButton = document.getElementById('scan-signals-btn'); if (scanButton) { scanButton.disabled = false; scanButton.textContent = 'Scan for Signals'; } } } async function pollScanStatus(scanId) { try { const response = await fetch('/api/v1/dashboard/discovery/status'); const data = await response.json(); if (data.status && !data.status.is_scanning) { // Scan completed, load signals loadSignals(); showAlert('Discovery scan completed', 'success'); } else { // Still scanning, check again in 2 seconds setTimeout(() => pollScanStatus(scanId), 2000); } } catch (error) { console.error('Error polling scan status:', error); } } async function loadSignals() { try { const response = await fetch('/api/v1/dashboard/discovery/recent'); const data = await response.json(); const signalsDiv = document.getElementById('signals-list'); if (signalsDiv && data.success) { if (data.recent_endpoints && data.recent_endpoints.length > 0) { signalsDiv.innerHTML = data.recent_endpoints.map(endpoint => `
${endpoint.device_name} ${endpoint.protocol_type}
Address: ${endpoint.address}
Response Time: ${endpoint.response_time ? endpoint.response_time.toFixed(3) + 's' : 'N/A'}
Capabilities: ${endpoint.capabilities ? endpoint.capabilities.join(', ') : 'N/A'}
Discovered: ${new Date(endpoint.discovered_at).toLocaleString()}
`).join(''); } else { signalsDiv.innerHTML = '
No signals discovered yet. Click "Scan for Signals" to start discovery.
'; } } } catch (error) { console.error('Error loading signals:', error); showAlert('Failed to load signals', 'error'); } } // Logs loading async function loadLogs() { try { const response = await fetch('/api/v1/logs/recent'); const data = await response.json(); const logsDiv = document.getElementById('logs-content'); if (logsDiv && data.success) { if (data.logs && data.logs.length > 0) { logsDiv.innerHTML = data.logs.map(log => `
${new Date(log.timestamp).toLocaleString()} ${log.level} ${log.message}
`).join(''); } else { logsDiv.innerHTML = '
No recent logs available.
'; } } } catch (error) { console.error('Error loading logs:', error); showAlert('Failed to load logs', 'error'); } } // Configuration management async function saveConfiguration() { try { const formData = new FormData(document.getElementById('config-form')); const config = {}; for (let [key, value] of formData.entries()) { config[key] = value; } const response = await fetch('/api/v1/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); if (response.ok) { showAlert('Configuration saved successfully', 'success'); } else { showAlert('Failed to save configuration', 'error'); } } catch (error) { console.error('Error saving configuration:', error); showAlert('Failed to save configuration', 'error'); } } // Alert system function showAlert(message, type = 'info') { const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type}`; alertDiv.textContent = message; const container = document.querySelector('.container'); container.insertBefore(alertDiv, container.firstChild); // Auto-remove after 5 seconds setTimeout(() => { if (alertDiv.parentNode) { alertDiv.parentNode.removeChild(alertDiv); } }, 5000); } // Export functionality async function exportSignals() { try { const response = await fetch('/api/v1/dashboard/discovery/recent'); const data = await response.json(); if (data.success && data.recent_endpoints) { // Convert to CSV const csvHeaders = ['Device Name', 'Protocol Type', 'Address', 'Response Time', 'Capabilities', 'Discovered At']; const csvData = data.recent_endpoints.map(endpoint => [ endpoint.device_name, endpoint.protocol_type, endpoint.address, endpoint.response_time || '', endpoint.capabilities ? endpoint.capabilities.join(';') : '', endpoint.discovered_at ]); const csvContent = [csvHeaders, ...csvData] .map(row => row.map(field => `"${field}"`).join(',')) .join('\n'); // Download CSV const blob = new Blob([csvContent], { type: 'text/csv' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'calejo-signals.csv'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); showAlert('Signals exported successfully', 'success'); } else { showAlert('No signals to export', 'warning'); } } catch (error) { console.error('Error exporting signals:', error); showAlert('Failed to export signals', 'error'); } } // Initialize dashboard on load document.addEventListener('DOMContentLoaded', function() { // Load initial status loadStatus(); // Set up event listeners const scanButton = document.getElementById('scan-signals-btn'); if (scanButton) { scanButton.addEventListener('click', scanSignals); } const exportButton = document.getElementById('export-signals-btn'); if (exportButton) { exportButton.addEventListener('click', exportSignals); } const saveConfigButton = document.getElementById('save-config-btn'); if (saveConfigButton) { saveConfigButton.addEventListener('click', saveConfiguration); } });