fix: Improve dashboard UI and add SCADA/hardware configuration
- Fix tab visibility issue with white-on-white background - Add SCADA/Hardware configuration tab with comprehensive settings - Add Modbus TCP configuration with port, slave ID, and baud rate - Add OPC UA configuration with security mode options - Add device mapping interface for SCADA device configuration - Add SCADA status monitoring with connection details - Add Prometheus authentication configuration (basic auth) - Update JavaScript to handle new SCADA functionality
This commit is contained in:
parent
d68fab1aab
commit
da82ab5d9f
|
|
@ -53,10 +53,12 @@ services:
|
||||||
- "9091:9090"
|
- "9091:9090"
|
||||||
volumes:
|
volumes:
|
||||||
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
|
- ./monitoring/prometheus-web.yml:/etc/prometheus/web.yml
|
||||||
- ./monitoring/alert_rules.yml:/etc/prometheus/alert_rules.yml
|
- ./monitoring/alert_rules.yml:/etc/prometheus/alert_rules.yml
|
||||||
- prometheus_data:/prometheus
|
- prometheus_data:/prometheus
|
||||||
command:
|
command:
|
||||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
- '--web.config.file=/etc/prometheus/web.yml'
|
||||||
- '--storage.tsdb.path=/prometheus'
|
- '--storage.tsdb.path=/prometheus'
|
||||||
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
||||||
- '--web.console.templates=/etc/prometheus/consoles'
|
- '--web.console.templates=/etc/prometheus/consoles'
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -108,14 +108,24 @@ DASHBOARD_HTML = """
|
||||||
}
|
}
|
||||||
.tab-button {
|
.tab-button {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
background: none;
|
background: #f8f9fa;
|
||||||
border: none;
|
border: 1px solid #ddd;
|
||||||
|
border-bottom: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-bottom: 3px solid transparent;
|
border-bottom: 3px solid transparent;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 2px;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
}
|
}
|
||||||
.tab-button.active {
|
.tab-button.active {
|
||||||
border-bottom-color: #007acc;
|
border-bottom-color: #007acc;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
background: white;
|
||||||
|
border-color: #ddd;
|
||||||
|
border-bottom-color: white;
|
||||||
|
}
|
||||||
|
.tab-button:hover {
|
||||||
|
background: #e9ecef;
|
||||||
}
|
}
|
||||||
.tab-content {
|
.tab-content {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
@ -164,6 +174,7 @@ DASHBOARD_HTML = """
|
||||||
<div class="tab-buttons">
|
<div class="tab-buttons">
|
||||||
<button class="tab-button active" onclick="showTab('status')">Status</button>
|
<button class="tab-button active" onclick="showTab('status')">Status</button>
|
||||||
<button class="tab-button" onclick="showTab('config')">Configuration</button>
|
<button class="tab-button" onclick="showTab('config')">Configuration</button>
|
||||||
|
<button class="tab-button" onclick="showTab('scada')">SCADA/Hardware</button>
|
||||||
<button class="tab-button" onclick="showTab('logs')">Logs</button>
|
<button class="tab-button" onclick="showTab('logs')">Logs</button>
|
||||||
<button class="tab-button" onclick="showTab('actions')">Actions</button>
|
<button class="tab-button" onclick="showTab('actions')">Actions</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -269,6 +280,87 @@ DASHBOARD_HTML = """
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- SCADA/Hardware Tab -->
|
||||||
|
<div id="scada-tab" class="tab-content">
|
||||||
|
<h2>SCADA/Hardware Configuration</h2>
|
||||||
|
<div id="scada-alerts"></div>
|
||||||
|
|
||||||
|
<div class="config-section">
|
||||||
|
<h3>Modbus TCP Configuration</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="modbus_enabled_scada">
|
||||||
|
<input type="checkbox" id="modbus_enabled_scada" name="modbus_enabled_scada">
|
||||||
|
Enable Modbus TCP Server
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="modbus_port_scada">Modbus Port:</label>
|
||||||
|
<input type="number" id="modbus_port_scada" name="modbus_port_scada" min="1" max="65535" value="502">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="modbus_slave_id">Slave ID:</label>
|
||||||
|
<input type="number" id="modbus_slave_id" name="modbus_slave_id" min="1" max="247" value="1">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="modbus_baud_rate">Baud Rate:</label>
|
||||||
|
<select id="modbus_baud_rate" name="modbus_baud_rate">
|
||||||
|
<option value="9600">9600</option>
|
||||||
|
<option value="19200">19200</option>
|
||||||
|
<option value="38400">38400</option>
|
||||||
|
<option value="57600">57600</option>
|
||||||
|
<option value="115200" selected>115200</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-section">
|
||||||
|
<h3>OPC UA Configuration</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="opcua_enabled_scada">
|
||||||
|
<input type="checkbox" id="opcua_enabled_scada" name="opcua_enabled_scada">
|
||||||
|
Enable OPC UA Server
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="opcua_port_scada">OPC UA Port:</label>
|
||||||
|
<input type="number" id="opcua_port_scada" name="opcua_port_scada" min="1" max="65535" value="4840">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="opcua_security_mode">Security Mode:</label>
|
||||||
|
<select id="opcua_security_mode" name="opcua_security_mode">
|
||||||
|
<option value="None">None</option>
|
||||||
|
<option value="Sign">Sign</option>
|
||||||
|
<option value="SignAndEncrypt" selected>Sign and Encrypt</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-section">
|
||||||
|
<h3>Device Mapping</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="device_mapping">Device Address Mapping:</label>
|
||||||
|
<textarea id="device_mapping" name="device_mapping" rows="8" placeholder="Device ID,Address,Type,Description
|
||||||
|
1,40001,Holding Register,Temperature Sensor
|
||||||
|
2,40002,Holding Register,Pressure Sensor
|
||||||
|
3,10001,Coil,Relay Output
|
||||||
|
4,10002,Coil,Valve Control"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="config-section">
|
||||||
|
<h3>Current SCADA Status</h3>
|
||||||
|
<div class="status-grid" id="scada-status-grid">
|
||||||
|
<!-- SCADA status will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button type="button" onclick="loadSCADAConfig()">Load Current</button>
|
||||||
|
<button type="button" onclick="saveSCADAConfig()">Save Configuration</button>
|
||||||
|
<button type="button" onclick="testSCADAConnection()">Test Connection</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Logs Tab -->
|
<!-- Logs Tab -->
|
||||||
<div id="logs-tab" class="tab-content">
|
<div id="logs-tab" class="tab-content">
|
||||||
<h2>System Logs</h2>
|
<h2>System Logs</h2>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ function showTab(tabName) {
|
||||||
// Load data for the tab
|
// Load data for the tab
|
||||||
if (tabName === 'status') {
|
if (tabName === 'status') {
|
||||||
loadStatus();
|
loadStatus();
|
||||||
|
} else if (tabName === 'scada') {
|
||||||
|
loadSCADAStatus();
|
||||||
} else if (tabName === 'logs') {
|
} else if (tabName === 'logs') {
|
||||||
loadLogs();
|
loadLogs();
|
||||||
}
|
}
|
||||||
|
|
@ -284,6 +286,144 @@ function viewMetrics() {
|
||||||
window.open('/metrics', '_blank');
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize dashboard on load
|
// Initialize dashboard on load
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Load initial status
|
// Load initial status
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue