CalejoControl/fixed-dashboard.js

326 lines
12 KiB
JavaScript
Raw Permalink Normal View History

// 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 = `
<div class="status-item">
<strong>OPC UA:</strong> <span class="status-${data.opcua_enabled ? 'running' : 'error'}">${data.opcua_enabled ? 'Enabled' : 'Disabled'}</span>
</div>
<div class="status-item">
<strong>Modbus:</strong> <span class="status-${data.modbus_enabled ? 'running' : 'error'}">${data.modbus_enabled ? 'Enabled' : 'Disabled'}</span>
</div>
<div class="status-item">
<strong>Connected Devices:</strong> ${data.connected_devices || 0}
</div>
`;
}
} 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 => `
<div class="signal-item">
<div class="signal-header">
<strong>${endpoint.device_name}</strong>
<span class="protocol-badge">${endpoint.protocol_type}</span>
</div>
<div class="signal-details">
<div><strong>Address:</strong> ${endpoint.address}</div>
<div><strong>Response Time:</strong> ${endpoint.response_time ? endpoint.response_time.toFixed(3) + 's' : 'N/A'}</div>
<div><strong>Capabilities:</strong> ${endpoint.capabilities ? endpoint.capabilities.join(', ') : 'N/A'}</div>
<div><strong>Discovered:</strong> ${new Date(endpoint.discovered_at).toLocaleString()}</div>
</div>
</div>
`).join('');
} else {
signalsDiv.innerHTML = '<div class="no-signals">No signals discovered yet. Click "Scan for Signals" to start discovery.</div>';
}
}
} 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 => `
<div class="log-entry">
<span class="log-time">${new Date(log.timestamp).toLocaleString()}</span>
<span class="log-level log-${log.level}">${log.level}</span>
<span class="log-message">${log.message}</span>
</div>
`).join('');
} else {
logsDiv.innerHTML = '<div class="no-logs">No recent logs available.</div>';
}
}
} 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);
}
});