2025-10-30 07:22:00 +00:00
|
|
|
// 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();
|
2025-11-01 11:12:12 +00:00
|
|
|
} else if (tabName === 'scada') {
|
|
|
|
|
loadSCADAStatus();
|
2025-11-01 12:06:23 +00:00
|
|
|
} else if (tabName === 'signals') {
|
|
|
|
|
loadSignals();
|
2025-10-30 07:22:00 +00:00
|
|
|
} else if (tabName === 'logs') {
|
|
|
|
|
loadLogs();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Alert management
|
|
|
|
|
function showAlert(message, type) {
|
|
|
|
|
const alertsDiv = document.getElementById('alerts');
|
|
|
|
|
const alertDiv = document.createElement('div');
|
|
|
|
|
alertDiv.className = `alert ${type}`;
|
|
|
|
|
alertDiv.textContent = message;
|
|
|
|
|
alertsDiv.appendChild(alertDiv);
|
|
|
|
|
|
|
|
|
|
// Auto-remove after 5 seconds
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
alertDiv.remove();
|
|
|
|
|
}, 5000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Status functions
|
|
|
|
|
async function loadStatus() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/status');
|
|
|
|
|
const status = await response.json();
|
|
|
|
|
|
|
|
|
|
const statusGrid = document.getElementById('status-grid');
|
|
|
|
|
statusGrid.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
for (const [key, value] of Object.entries(status)) {
|
|
|
|
|
const statusCard = document.createElement('div');
|
|
|
|
|
statusCard.className = `status-card ${value}`;
|
|
|
|
|
statusCard.innerHTML = `
|
|
|
|
|
<h3>${key.replace('_', ' ').toUpperCase()}</h3>
|
|
|
|
|
<p>${value.toUpperCase()}</p>
|
|
|
|
|
`;
|
|
|
|
|
statusGrid.appendChild(statusCard);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading status:', error);
|
|
|
|
|
showAlert('Failed to load system status', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshStatus() {
|
|
|
|
|
loadStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configuration functions
|
|
|
|
|
async function loadConfiguration() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/config');
|
|
|
|
|
const config = await response.json();
|
|
|
|
|
|
|
|
|
|
// Populate form fields
|
|
|
|
|
document.getElementById('db_host').value = config.database.db_host;
|
|
|
|
|
document.getElementById('db_port').value = config.database.db_port;
|
|
|
|
|
document.getElementById('db_name').value = config.database.db_name;
|
|
|
|
|
document.getElementById('db_user').value = config.database.db_user;
|
|
|
|
|
|
|
|
|
|
document.getElementById('opcua_enabled').checked = config.opcua.enabled;
|
|
|
|
|
document.getElementById('opcua_port').value = config.opcua.port;
|
|
|
|
|
|
|
|
|
|
document.getElementById('modbus_enabled').checked = config.modbus.enabled;
|
|
|
|
|
document.getElementById('modbus_port').value = config.modbus.port;
|
|
|
|
|
|
|
|
|
|
document.getElementById('rest_api_host').value = config.rest_api.host;
|
|
|
|
|
document.getElementById('rest_api_port').value = config.rest_api.port;
|
|
|
|
|
document.getElementById('rest_api_cors_enabled').checked = config.rest_api.cors_enabled;
|
|
|
|
|
|
|
|
|
|
document.getElementById('health_monitor_port').value = config.monitoring.health_monitor_port;
|
|
|
|
|
|
|
|
|
|
showAlert('Configuration loaded successfully', 'success');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading configuration:', error);
|
|
|
|
|
showAlert('Failed to load configuration', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveConfiguration() {
|
|
|
|
|
try {
|
|
|
|
|
const config = {
|
|
|
|
|
database: {
|
|
|
|
|
db_host: document.getElementById('db_host').value,
|
|
|
|
|
db_port: parseInt(document.getElementById('db_port').value),
|
|
|
|
|
db_name: document.getElementById('db_name').value,
|
|
|
|
|
db_user: document.getElementById('db_user').value,
|
|
|
|
|
db_password: document.getElementById('db_password').value
|
|
|
|
|
},
|
|
|
|
|
opcua: {
|
|
|
|
|
enabled: document.getElementById('opcua_enabled').checked,
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: parseInt(document.getElementById('opcua_port').value)
|
|
|
|
|
},
|
|
|
|
|
modbus: {
|
|
|
|
|
enabled: document.getElementById('modbus_enabled').checked,
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: parseInt(document.getElementById('modbus_port').value),
|
|
|
|
|
unit_id: 1
|
|
|
|
|
},
|
|
|
|
|
rest_api: {
|
|
|
|
|
enabled: true,
|
|
|
|
|
host: document.getElementById('rest_api_host').value,
|
|
|
|
|
port: parseInt(document.getElementById('rest_api_port').value),
|
|
|
|
|
cors_enabled: document.getElementById('rest_api_cors_enabled').checked
|
|
|
|
|
},
|
|
|
|
|
monitoring: {
|
|
|
|
|
health_monitor_port: parseInt(document.getElementById('health_monitor_port').value),
|
|
|
|
|
metrics_enabled: true
|
|
|
|
|
},
|
|
|
|
|
security: {
|
|
|
|
|
jwt_secret_key: '',
|
|
|
|
|
api_key: ''
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/config', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(config)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
if (result.valid) {
|
|
|
|
|
showAlert('Configuration saved successfully', 'success');
|
|
|
|
|
if (result.warnings.length > 0) {
|
|
|
|
|
showAlert('Warnings: ' + result.warnings.join(', '), 'warning');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
showAlert('Configuration validation failed: ' + result.errors.join(', '), 'error');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error saving configuration:', error);
|
|
|
|
|
showAlert('Failed to save configuration', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function validateConfiguration() {
|
|
|
|
|
try {
|
|
|
|
|
const config = {
|
|
|
|
|
database: {
|
|
|
|
|
db_host: document.getElementById('db_host').value,
|
|
|
|
|
db_port: parseInt(document.getElementById('db_port').value),
|
|
|
|
|
db_name: document.getElementById('db_name').value,
|
|
|
|
|
db_user: document.getElementById('db_user').value,
|
|
|
|
|
db_password: document.getElementById('db_password').value
|
|
|
|
|
},
|
|
|
|
|
opcua: {
|
|
|
|
|
enabled: document.getElementById('opcua_enabled').checked,
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: parseInt(document.getElementById('opcua_port').value)
|
|
|
|
|
},
|
|
|
|
|
modbus: {
|
|
|
|
|
enabled: document.getElementById('modbus_enabled').checked,
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: parseInt(document.getElementById('modbus_port').value),
|
|
|
|
|
unit_id: 1
|
|
|
|
|
},
|
|
|
|
|
rest_api: {
|
|
|
|
|
enabled: true,
|
|
|
|
|
host: document.getElementById('rest_api_host').value,
|
|
|
|
|
port: parseInt(document.getElementById('rest_api_port').value),
|
|
|
|
|
cors_enabled: document.getElementById('rest_api_cors_enabled').checked
|
|
|
|
|
},
|
|
|
|
|
monitoring: {
|
|
|
|
|
health_monitor_port: parseInt(document.getElementById('health_monitor_port').value),
|
|
|
|
|
metrics_enabled: true
|
|
|
|
|
},
|
|
|
|
|
security: {
|
|
|
|
|
jwt_secret_key: '',
|
|
|
|
|
api_key: ''
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/config', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(config)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
if (result.valid) {
|
|
|
|
|
showAlert('Configuration is valid', 'success');
|
|
|
|
|
if (result.warnings.length > 0) {
|
|
|
|
|
showAlert('Warnings: ' + result.warnings.join(', '), 'warning');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
showAlert('Configuration validation failed: ' + result.errors.join(', '), 'error');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error validating configuration:', error);
|
|
|
|
|
showAlert('Failed to validate configuration', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Logs functions
|
|
|
|
|
async function loadLogs() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/logs?limit=50');
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
const logsContainer = document.getElementById('logs-container');
|
|
|
|
|
logsContainer.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
data.logs.forEach(log => {
|
|
|
|
|
const logEntry = document.createElement('div');
|
|
|
|
|
logEntry.className = `log-entry ${log.level.toLowerCase()}`;
|
|
|
|
|
logEntry.innerHTML = `
|
|
|
|
|
<span style="color: #666;">${log.timestamp}</span>
|
|
|
|
|
<span style="font-weight: bold; margin: 0 10px;">${log.level}</span>
|
|
|
|
|
<span>${log.message}</span>
|
|
|
|
|
`;
|
|
|
|
|
logsContainer.appendChild(logEntry);
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading logs:', error);
|
|
|
|
|
showAlert('Failed to load logs', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Action functions
|
|
|
|
|
async function restartSystem() {
|
|
|
|
|
if (confirm('Are you sure you want to restart the system? This will temporarily interrupt service.')) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/restart', {
|
|
|
|
|
method: 'POST'
|
|
|
|
|
});
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
showAlert(`Restart initiated: ${result.message}`, 'success');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error restarting system:', error);
|
|
|
|
|
showAlert('Failed to restart system', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function createBackup() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/backup');
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
showAlert(`Backup initiated: ${result.message}`, 'success');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error creating backup:', error);
|
|
|
|
|
showAlert('Failed to create backup', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function runHealthCheck() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/health');
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
showAlert(`Health check: ${result.status}`, 'success');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error running health check:', error);
|
|
|
|
|
showAlert('Health check failed', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function viewMetrics() {
|
|
|
|
|
window.open('/metrics', '_blank');
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-01 11:12:12 +00:00
|
|
|
// SCADA/Hardware functions
|
|
|
|
|
function showSCADAAlert(message, type) {
|
|
|
|
|
const alertsDiv = document.getElementById('scada-alerts');
|
|
|
|
|
const alertDiv = document.createElement('div');
|
|
|
|
|
alertDiv.className = `alert ${type}`;
|
|
|
|
|
alertDiv.textContent = message;
|
|
|
|
|
alertsDiv.appendChild(alertDiv);
|
|
|
|
|
|
|
|
|
|
// Auto-remove after 5 seconds
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
alertDiv.remove();
|
|
|
|
|
}, 5000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadSCADAConfig() {
|
|
|
|
|
try {
|
|
|
|
|
// Load current SCADA configuration
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/scada-config');
|
|
|
|
|
const config = await response.json();
|
|
|
|
|
|
|
|
|
|
// Populate SCADA form fields
|
|
|
|
|
document.getElementById('modbus_enabled_scada').checked = config.modbus.enabled;
|
|
|
|
|
document.getElementById('modbus_port_scada').value = config.modbus.port;
|
|
|
|
|
document.getElementById('modbus_slave_id').value = config.modbus.slave_id;
|
|
|
|
|
document.getElementById('modbus_baud_rate').value = config.modbus.baud_rate;
|
|
|
|
|
|
|
|
|
|
document.getElementById('opcua_enabled_scada').checked = config.opcua.enabled;
|
|
|
|
|
document.getElementById('opcua_port_scada').value = config.opcua.port;
|
|
|
|
|
document.getElementById('opcua_security_mode').value = config.opcua.security_mode;
|
|
|
|
|
|
|
|
|
|
document.getElementById('device_mapping').value = config.device_mapping;
|
|
|
|
|
|
|
|
|
|
showSCADAAlert('SCADA configuration loaded successfully', 'success');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading SCADA configuration:', error);
|
|
|
|
|
showSCADAAlert('Failed to load SCADA configuration', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveSCADAConfig() {
|
|
|
|
|
try {
|
|
|
|
|
const config = {
|
|
|
|
|
modbus: {
|
|
|
|
|
enabled: document.getElementById('modbus_enabled_scada').checked,
|
|
|
|
|
port: parseInt(document.getElementById('modbus_port_scada').value),
|
|
|
|
|
slave_id: parseInt(document.getElementById('modbus_slave_id').value),
|
|
|
|
|
baud_rate: parseInt(document.getElementById('modbus_baud_rate').value)
|
|
|
|
|
},
|
|
|
|
|
opcua: {
|
|
|
|
|
enabled: document.getElementById('opcua_enabled_scada').checked,
|
|
|
|
|
port: parseInt(document.getElementById('opcua_port_scada').value),
|
|
|
|
|
security_mode: document.getElementById('opcua_security_mode').value
|
|
|
|
|
},
|
|
|
|
|
device_mapping: document.getElementById('device_mapping').value
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/scada-config', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(config)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
showSCADAAlert('SCADA configuration saved successfully', 'success');
|
|
|
|
|
} else {
|
|
|
|
|
showSCADAAlert('Failed to save SCADA configuration: ' + result.error, 'error');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error saving SCADA configuration:', error);
|
|
|
|
|
showSCADAAlert('Failed to save SCADA configuration', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function testSCADAConnection() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/test-scada');
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
showSCADAAlert('SCADA connection test successful', 'success');
|
|
|
|
|
} else {
|
|
|
|
|
showSCADAAlert('SCADA connection test failed: ' + result.error, 'error');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error testing SCADA connection:', error);
|
|
|
|
|
showSCADAAlert('Failed to test SCADA connection', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadSCADAStatus() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/scada-status');
|
|
|
|
|
const status = await response.json();
|
|
|
|
|
|
|
|
|
|
const statusGrid = document.getElementById('scada-status-grid');
|
|
|
|
|
statusGrid.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
const scadaStatus = [
|
|
|
|
|
{ name: 'Modbus Server', status: status.modbus_enabled ? 'running' : 'stopped' },
|
|
|
|
|
{ name: 'OPC UA Server', status: status.opcua_enabled ? 'running' : 'stopped' },
|
|
|
|
|
{ name: 'Device Connections', status: status.device_connections > 0 ? 'running' : 'stopped' },
|
|
|
|
|
{ name: 'Data Acquisition', status: status.data_acquisition ? 'running' : 'stopped' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
scadaStatus.forEach(item => {
|
|
|
|
|
const statusCard = document.createElement('div');
|
|
|
|
|
statusCard.className = `status-card ${item.status}`;
|
|
|
|
|
statusCard.innerHTML = `
|
|
|
|
|
<h3>${item.name}</h3>
|
|
|
|
|
<p>${item.status.toUpperCase()}</p>
|
|
|
|
|
`;
|
|
|
|
|
statusGrid.appendChild(statusCard);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add connection details
|
|
|
|
|
const detailsDiv = document.createElement('div');
|
|
|
|
|
detailsDiv.style.gridColumn = '1 / -1';
|
|
|
|
|
detailsDiv.innerHTML = `
|
|
|
|
|
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 4px;">
|
|
|
|
|
<h4>Connection Details:</h4>
|
|
|
|
|
<p><strong>Modbus Port:</strong> ${status.modbus_port || 'Not configured'}</p>
|
|
|
|
|
<p><strong>OPC UA Port:</strong> ${status.opcua_port || 'Not configured'}</p>
|
|
|
|
|
<p><strong>Connected Devices:</strong> ${status.device_connections || 0}</p>
|
|
|
|
|
<p><strong>Last Data Update:</strong> ${status.last_update || 'Never'}</p>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
statusGrid.appendChild(detailsDiv);
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading SCADA status:', error);
|
|
|
|
|
showSCADAAlert('Failed to load SCADA status', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-01 12:06:23 +00:00
|
|
|
// Signal Overview functions
|
|
|
|
|
async function loadSignals() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/signals');
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
// Update protocol statistics
|
|
|
|
|
const statsGrid = document.getElementById('protocol-stats-grid');
|
|
|
|
|
statsGrid.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
for (const [protocol, stats] of Object.entries(data.protocol_stats || {})) {
|
|
|
|
|
const card = document.createElement('div');
|
|
|
|
|
card.className = 'status-card running';
|
|
|
|
|
card.innerHTML = `
|
|
|
|
|
<h3>${protocol.toUpperCase()}</h3>
|
|
|
|
|
<p>Active Signals: ${stats.active_signals || 0}</p>
|
|
|
|
|
<p>Total Signals: ${stats.total_signals || 0}</p>
|
|
|
|
|
<p>Error Rate: ${stats.error_rate || '0%'}</p>
|
|
|
|
|
`;
|
|
|
|
|
statsGrid.appendChild(card);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update signals table
|
|
|
|
|
const signalsBody = document.getElementById('signals-body');
|
|
|
|
|
signalsBody.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
data.signals.forEach(signal => {
|
|
|
|
|
const row = document.createElement('tr');
|
|
|
|
|
row.innerHTML = `
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">${signal.name}</td>
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">${signal.protocol}</td>
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">${signal.address}</td>
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">${signal.data_type}</td>
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">${signal.current_value}</td>
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">
|
|
|
|
|
<span class="status-badge ${signal.quality === 'Good' ? 'running' : signal.quality === 'Bad' ? 'error' : 'warning'}">
|
|
|
|
|
${signal.quality}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td style="padding: 10px; border: 1px solid #ddd;">${signal.timestamp}</td>
|
|
|
|
|
`;
|
|
|
|
|
signalsBody.appendChild(row);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error loading signals:', error);
|
|
|
|
|
showAlert('Failed to load signal data', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function refreshSignals() {
|
|
|
|
|
await loadSignals();
|
|
|
|
|
showAlert('Signals refreshed', 'success');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function exportSignals() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('/api/v1/dashboard/signals/export');
|
|
|
|
|
const blob = await response.blob();
|
|
|
|
|
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');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error exporting signals:', error);
|
|
|
|
|
showAlert('Failed to export signals', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-30 07:22:00 +00:00
|
|
|
// Initialize dashboard on load
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
// Load initial status
|
|
|
|
|
loadStatus();
|
|
|
|
|
|
|
|
|
|
// Auto-refresh status every 30 seconds
|
|
|
|
|
setInterval(loadStatus, 30000);
|
|
|
|
|
});
|