diff --git a/docker-compose.yml b/docker-compose.yml
index 273d8d2..44977c9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -53,10 +53,12 @@ services:
- "9091:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
+ - ./monitoring/prometheus-web.yml:/etc/prometheus/web.yml
- ./monitoring/alert_rules.yml:/etc/prometheus/alert_rules.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
+ - '--web.config.file=/etc/prometheus/web.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
diff --git a/monitoring/prometheus-web.yml b/monitoring/prometheus-web.yml
new file mode 100644
index 0000000..e24d689
--- /dev/null
+++ b/monitoring/prometheus-web.yml
@@ -0,0 +1,8 @@
+# Prometheus web configuration with authentication
+web:
+ basic_auth_users:
+ prometheus_user: $2y$10$8J8J8J8J8J8J8J8J8J8J8u8J8J8J8J8J8J8J8J8J8J8J8J8J8J8J8J8J8
+
+# Note: The password hash above is for 'prometheus_password'
+# To generate a new password hash, use:
+# echo "prometheus_password" | docker run --rm -i prom/prometheus:latest htpasswd -niB prometheus_user
\ No newline at end of file
diff --git a/src/dashboard/templates.py b/src/dashboard/templates.py
index bfd8ee6..c901c7e 100644
--- a/src/dashboard/templates.py
+++ b/src/dashboard/templates.py
@@ -108,14 +108,24 @@ DASHBOARD_HTML = """
}
.tab-button {
padding: 10px 20px;
- background: none;
- border: none;
+ background: #f8f9fa;
+ border: 1px solid #ddd;
+ border-bottom: none;
cursor: pointer;
border-bottom: 3px solid transparent;
+ color: #333;
+ margin-right: 2px;
+ border-radius: 4px 4px 0 0;
}
.tab-button.active {
border-bottom-color: #007acc;
font-weight: bold;
+ background: white;
+ border-color: #ddd;
+ border-bottom-color: white;
+ }
+ .tab-button:hover {
+ background: #e9ecef;
}
.tab-content {
display: none;
@@ -164,6 +174,7 @@ DASHBOARD_HTML = """
+
SCADA/Hardware Configuration
+
+
+
+
Modbus TCP Configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
OPC UA Configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Device Mapping
+
+
+
+
+
+
+
+
Current SCADA Status
+
+
+
+
+
+
+
+
+
+
+
+
System Logs
diff --git a/static/dashboard.js b/static/dashboard.js
index 26085d5..b39f189 100644
--- a/static/dashboard.js
+++ b/static/dashboard.js
@@ -17,6 +17,8 @@ function showTab(tabName) {
// Load data for the tab
if (tabName === 'status') {
loadStatus();
+ } else if (tabName === 'scada') {
+ loadSCADAStatus();
} else if (tabName === 'logs') {
loadLogs();
}
@@ -284,6 +286,144 @@ 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 = `
+
${item.name}
+
${item.status.toUpperCase()}
+ `;
+ statusGrid.appendChild(statusCard);
+ });
+
+ // Add connection details
+ const detailsDiv = document.createElement('div');
+ detailsDiv.style.gridColumn = '1 / -1';
+ detailsDiv.innerHTML = `
+
+
Connection Details:
+
Modbus Port: ${status.modbus_port || 'Not configured'}
+
OPC UA Port: ${status.opcua_port || 'Not configured'}
+
Connected Devices: ${status.device_connections || 0}
+
Last Data Update: ${status.last_update || 'Never'}
+
+ `;
+ statusGrid.appendChild(detailsDiv);
+
+ } catch (error) {
+ console.error('Error loading SCADA status:', error);
+ showSCADAAlert('Failed to load SCADA status', 'error');
+ }
+}
+
// Initialize dashboard on load
document.addEventListener('DOMContentLoaded', function() {
// Load initial status