CalejoControl/static/dashboard.js

510 lines
19 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();
} else if (tabName === 'protocol-mapping') {
loadProtocolMappings();
}
}
// 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');
}
// 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');
}
}
// 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');
}
}
// Initialize dashboard on load
document.addEventListener('DOMContentLoaded', function() {
// Load initial status
loadStatus();