907 lines
39 KiB
Python
907 lines
39 KiB
Python
"""
|
|
Dashboard HTML templates for Calejo Control Adapter
|
|
"""
|
|
|
|
DASHBOARD_HTML = """
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Calejo Control Adapter - Dashboard</title>
|
|
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background-color: #f5f5f5;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
border-bottom: 2px solid #007acc;
|
|
padding-bottom: 10px;
|
|
}
|
|
.status-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin-bottom: 30px;
|
|
}
|
|
.status-card {
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
text-align: center;
|
|
background: #f8f9fa;
|
|
border-left: 4px solid #007acc;
|
|
}
|
|
.status-card.running { border-left-color: #28a745; }
|
|
.status-card.error { border-left-color: #dc3545; }
|
|
.status-card.warning { border-left-color: #ffc107; }
|
|
.config-section {
|
|
margin-bottom: 30px;
|
|
padding: 20px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
}
|
|
.form-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
}
|
|
input, select {
|
|
width: 100%;
|
|
padding: 8px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
box-sizing: border-box;
|
|
}
|
|
button {
|
|
background: #007acc;
|
|
color: white;
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin-right: 10px;
|
|
font-weight: bold;
|
|
}
|
|
button:hover {
|
|
background: #005a9e;
|
|
}
|
|
.alert {
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.alert.error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
.alert.warning {
|
|
background: #fff3cd;
|
|
color: #856404;
|
|
border: 1px solid #ffeaa7;
|
|
}
|
|
.alert.success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
.tab-container {
|
|
margin-bottom: 20px;
|
|
}
|
|
.tab-buttons {
|
|
display: flex;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
.tab-button {
|
|
padding: 10px 20px;
|
|
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;
|
|
font-weight: bold;
|
|
}
|
|
.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;
|
|
padding: 20px 0;
|
|
}
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
.action-buttons {
|
|
margin-top: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Protocol Selector Styles */
|
|
.protocol-selector {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.protocol-btn {
|
|
padding: 8px 16px;
|
|
background: #f8f9fa;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.protocol-btn.active {
|
|
background: #007acc;
|
|
color: white;
|
|
border-color: #007acc;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.protocol-btn:hover {
|
|
background: #e9ecef;
|
|
}
|
|
|
|
.protocol-btn.active:hover {
|
|
background: #005a9e;
|
|
}
|
|
|
|
/* Modal Styles */
|
|
.modal {
|
|
position: fixed;
|
|
z-index: 1000;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(0,0,0,0.5);
|
|
}
|
|
|
|
.modal-content {
|
|
background-color: white;
|
|
margin: 10% auto;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
width: 80%;
|
|
max-width: 600px;
|
|
position: relative;
|
|
}
|
|
|
|
.close {
|
|
color: #aaa;
|
|
float: right;
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.close:hover {
|
|
color: black;
|
|
}
|
|
.logs-container {
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 10px;
|
|
background: #f8f9fa;
|
|
}
|
|
.log-entry {
|
|
padding: 5px;
|
|
border-bottom: 1px solid #eee;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
.log-entry.error {
|
|
color: #dc3545;
|
|
}
|
|
.log-entry.warning {
|
|
color: #856404;
|
|
}
|
|
.log-entry.info {
|
|
color: #007acc;
|
|
}
|
|
|
|
/* Discovery Results Styling */
|
|
.discovery-result-card {
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
padding: 15px;
|
|
margin-bottom: 10px;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.discovery-result-card .signal-info {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.discovery-result-card .signal-tags {
|
|
margin: 5px 0;
|
|
}
|
|
|
|
.discovery-result-card .signal-details {
|
|
display: flex;
|
|
gap: 15px;
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
|
|
.use-signal-btn {
|
|
background: #007acc;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.use-signal-btn:hover {
|
|
background: #005a9e;
|
|
}
|
|
|
|
.apply-all-btn {
|
|
background: #28a745;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: bold;
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.apply-all-btn:hover {
|
|
background: #218838;
|
|
}
|
|
|
|
.discovery-notification {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
z-index: 10000;
|
|
max-width: 300px;
|
|
}
|
|
|
|
.discovery-notification.success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
|
|
.discovery-notification.error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
|
|
.discovery-notification.warning {
|
|
background: #fff3cd;
|
|
color: #856404;
|
|
border: 1px solid #ffeaa7;
|
|
}
|
|
|
|
/* Table Layout Fixes for Protocol Mappings */
|
|
.protocol-mappings-table-container {
|
|
overflow-x: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
#protocol-mappings-table {
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
min-width: 800px;
|
|
}
|
|
|
|
#protocol-mappings-table th,
|
|
#protocol-mappings-table td {
|
|
padding: 8px 10px;
|
|
border: 1px solid #ddd;
|
|
text-align: left;
|
|
word-wrap: break-word;
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
#protocol-mappings-table th:nth-child(1) { width: 10%; min-width: 80px; } /* ID */
|
|
#protocol-mappings-table th:nth-child(2) { width: 8%; min-width: 80px; } /* Protocol */
|
|
#protocol-mappings-table th:nth-child(3) { width: 15%; min-width: 120px; } /* Station */
|
|
#protocol-mappings-table th:nth-child(4) { width: 15%; min-width: 120px; } /* Equipment */
|
|
#protocol-mappings-table th:nth-child(5) { width: 15%; min-width: 120px; } /* Data Type */
|
|
#protocol-mappings-table th:nth-child(6) { width: 12%; min-width: 100px; } /* Protocol Address */
|
|
#protocol-mappings-table th:nth-child(7) { width: 15%; min-width: 120px; } /* Database Source */
|
|
#protocol-mappings-table th:nth-child(8) { width: 10%; min-width: 100px; } /* Actions */
|
|
|
|
/* Protocol Signals Table */
|
|
.protocol-signals-table-container {
|
|
overflow-x: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
#protocol-signals-table {
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
min-width: 700px;
|
|
}
|
|
|
|
#protocol-signals-table th,
|
|
#protocol-signals-table td {
|
|
padding: 8px 10px;
|
|
border: 1px solid #ddd;
|
|
text-align: left;
|
|
word-wrap: break-word;
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
#protocol-signals-table th:nth-child(1) { width: 20%; min-width: 120px; } /* Signal Name */
|
|
#protocol-signals-table th:nth-child(2) { width: 12%; min-width: 100px; } /* Protocol Type */
|
|
#protocol-signals-table th:nth-child(3) { width: 20%; min-width: 150px; } /* Tags */
|
|
#protocol-signals-table th:nth-child(4) { width: 15%; min-width: 100px; } /* Protocol Address */
|
|
#protocol-signals-table th:nth-child(5) { width: 18%; min-width: 120px; } /* Database Source */
|
|
#protocol-signals-table th:nth-child(6) { width: 8%; min-width: 80px; } /* Status */
|
|
#protocol-signals-table th:nth-child(7) { width: 7%; min-width: 100px; } /* Actions */
|
|
|
|
/* Mobile responsiveness */
|
|
@media (max-width: 768px) {
|
|
.protocol-mappings-table-container,
|
|
.protocol-signals-table-container {
|
|
font-size: 14px;
|
|
}
|
|
|
|
#protocol-mappings-table th,
|
|
#protocol-mappings-table td,
|
|
#protocol-signals-table th,
|
|
#protocol-signals-table td {
|
|
padding: 6px 8px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Calejo Control Adapter Dashboard</h1>
|
|
<p>Configuration and Monitoring Interface</p>
|
|
</div>
|
|
|
|
<div class="tab-container">
|
|
<div class="tab-buttons">
|
|
<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('scada')">SCADA/Hardware</button>
|
|
<button class="tab-button" onclick="showTab('signals')">Signal Overview</button>
|
|
<button class="tab-button" onclick="showTab('protocol-mapping')">Protocol Mapping</button>
|
|
<button class="tab-button" onclick="showTab('logs')">Logs</button>
|
|
<button class="tab-button" onclick="showTab('actions')">Actions</button>
|
|
</div>
|
|
|
|
<!-- Status Tab -->
|
|
<div id="status-tab" class="tab-content active">
|
|
<h2>System Status</h2>
|
|
<div class="status-grid" id="status-grid">
|
|
<!-- Status cards will be populated by JavaScript -->
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<button onclick="refreshStatus()">Refresh Status</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Configuration Tab -->
|
|
<div id="config-tab" class="tab-content">
|
|
<h2>Configuration</h2>
|
|
<div id="alerts"></div>
|
|
|
|
<form id="config-form">
|
|
<div class="config-section">
|
|
<h3>Database Configuration</h3>
|
|
<div class="form-group">
|
|
<label for="db_host">Host:</label>
|
|
<input type="text" id="db_host" name="db_host" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="db_port">Port:</label>
|
|
<input type="number" id="db_port" name="db_port" min="1" max="65535" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="db_name">Database Name:</label>
|
|
<input type="text" id="db_name" name="db_name" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="db_user">Username:</label>
|
|
<input type="text" id="db_user" name="db_user" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="db_password">Password:</label>
|
|
<input type="password" id="db_password" name="db_password">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Protocol Configuration</h3>
|
|
<div class="form-group">
|
|
<label>
|
|
<input type="checkbox" id="opcua_enabled" name="opcua_enabled">
|
|
Enable OPC UA Server
|
|
</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="opcua_port">OPC UA Port:</label>
|
|
<input type="number" id="opcua_port" name="opcua_port" min="1" max="65535">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>
|
|
<input type="checkbox" id="modbus_enabled" name="modbus_enabled">
|
|
Enable Modbus Server
|
|
</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="modbus_port">Modbus Port:</label>
|
|
<input type="number" id="modbus_port" name="modbus_port" min="1" max="65535">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>REST API Configuration</h3>
|
|
<div class="form-group">
|
|
<label for="rest_api_host">Host:</label>
|
|
<input type="text" id="rest_api_host" name="rest_api_host">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="rest_api_port">Port:</label>
|
|
<input type="number" id="rest_api_port" name="rest_api_port" min="1" max="65535">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>
|
|
<input type="checkbox" id="rest_api_cors_enabled" name="rest_api_cors_enabled">
|
|
Enable CORS
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Monitoring Configuration</h3>
|
|
<div class="form-group">
|
|
<label for="health_monitor_port">Health Monitor Port:</label>
|
|
<input type="number" id="health_monitor_port" name="health_monitor_port" min="1" max="65535">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<button type="button" onclick="loadConfiguration()">Load Current</button>
|
|
<button type="button" onclick="saveConfiguration()">Save Configuration</button>
|
|
<button type="button" onclick="validateConfiguration()">Validate</button>
|
|
</div>
|
|
</form>
|
|
</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>
|
|
|
|
<!-- Signal Overview Tab -->
|
|
<div id="signals-tab" class="tab-content">
|
|
<h2>Signal Overview</h2>
|
|
<div id="signals-alerts"></div>
|
|
|
|
<div class="config-section">
|
|
<h3>Active Signals</h3>
|
|
<div class="action-buttons">
|
|
<button onclick="refreshSignals()">Refresh Signals</button>
|
|
<button onclick="exportSignals()">Export to CSV</button>
|
|
</div>
|
|
|
|
<div style="margin-top: 20px;">
|
|
<table style="width: 100%; border-collapse: collapse;" id="signals-table">
|
|
<thead>
|
|
<tr style="background: #f8f9fa;">
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Signal Name</th>
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Protocol</th>
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Address/Node</th>
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Data Type</th>
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Current Value</th>
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Quality</th>
|
|
<th style="padding: 10px; border: 1px solid #ddd; text-align: left;">Timestamp</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="signals-body">
|
|
<!-- Signals will be populated by JavaScript -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Protocol Statistics</h3>
|
|
<div class="status-grid" id="protocol-stats-grid">
|
|
<!-- Protocol statistics will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Signal Configuration</h3>
|
|
<div class="form-group">
|
|
<label for="signal_filter">Filter Signals:</label>
|
|
<input type="text" id="signal_filter" placeholder="Filter by name, protocol, or address..." style="width: 300px;">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="protocol_filter">Protocol:</label>
|
|
<select id="protocol_filter">
|
|
<option value="">All Protocols</option>
|
|
<option value="modbus">Modbus</option>
|
|
<option value="opcua">OPC UA</option>
|
|
<option value="profinet">Profinet</option>
|
|
<option value="rest">REST API</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs Tab -->
|
|
<div id="logs-tab" class="tab-content">
|
|
<h2>System Logs</h2>
|
|
<div class="logs-container" id="logs-container">
|
|
<!-- Logs will be populated by JavaScript -->
|
|
</div>
|
|
<div class="action-buttons">
|
|
<button onclick="loadLogs()">Refresh Logs</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Protocol Mapping Tab -->
|
|
<div id="protocol-mapping-tab" class="tab-content">
|
|
<h2>Protocol Mapping Configuration</h2>
|
|
<div id="protocol-mapping-alerts"></div>
|
|
|
|
<!-- Protocol Selector -->
|
|
<div class="config-section">
|
|
<h3>Protocol Selection</h3>
|
|
<div class="protocol-selector">
|
|
<button class="protocol-btn active" onclick="selectProtocol('all')">All Protocols</button>
|
|
<button class="protocol-btn" onclick="selectProtocol('modbus_tcp')">Modbus TCP</button>
|
|
<button class="protocol-btn" onclick="selectProtocol('opcua')">OPC UA</button>
|
|
<button class="protocol-btn" onclick="selectProtocol('modbus_rtu')">Modbus RTU</button>
|
|
<button class="protocol-btn" onclick="selectProtocol('rest_api')">REST API</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Protocol Discovery -->
|
|
<div class="config-section">
|
|
<h3>Protocol Discovery</h3>
|
|
<div id="discovery-notifications"></div>
|
|
|
|
<div class="discovery-controls">
|
|
<div class="action-buttons">
|
|
<button id="start-discovery-scan" class="btn-primary">
|
|
<i class="fas fa-search"></i> Start Discovery Scan
|
|
</button>
|
|
<button id="stop-discovery-scan" class="btn-secondary" disabled>
|
|
<i class="fas fa-stop"></i> Stop Scan
|
|
</button>
|
|
<button id="refresh-discovery-status" class="btn-outline">
|
|
<i class="fas fa-sync"></i> Refresh Status
|
|
</button>
|
|
</div>
|
|
|
|
<div id="discovery-status" style="margin-top: 15px;">
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle"></i>
|
|
Discovery service ready
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="discovery-results" style="margin-top: 20px;">
|
|
<!-- Discovery results will be populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mapping Grid -->
|
|
<div class="config-section">
|
|
<h3>Protocol Mappings</h3>
|
|
<div class="action-buttons">
|
|
<button onclick="loadAllSignals()">Refresh Mappings</button>
|
|
<button onclick="showAddSignalModal()" style="background: #28a745;">Add New Mapping</button>
|
|
<button onclick="exportProtocolMappings()">Export to CSV</button>
|
|
</div>
|
|
|
|
<div class="protocol-mappings-table-container">
|
|
<table id="protocol-mappings-table">
|
|
<thead>
|
|
<tr style="background: #f8f9fa;">
|
|
<th>ID</th>
|
|
<th>Protocol</th>
|
|
<th>Station (Name & ID)</th>
|
|
<th>Equipment (Name & ID)</th>
|
|
<th>Data Type (Name & ID)</th>
|
|
<th>Protocol Address</th>
|
|
<th>Database Source</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="protocol-mappings-body">
|
|
<!-- Protocol mappings will be populated by JavaScript -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Protocol Signals Table (for discovery integration) -->
|
|
<div class="config-section">
|
|
<h3>Protocol Signals</h3>
|
|
<p>Signals discovered through protocol discovery will appear here</p>
|
|
|
|
<div class="protocol-signals-table-container">
|
|
<table id="protocol-signals-table">
|
|
<thead>
|
|
<tr style="background: #f8f9fa;">
|
|
<th>Signal Name</th>
|
|
<th>Protocol Type</th>
|
|
<th>Tags</th>
|
|
<th>Protocol Address</th>
|
|
<th>Database Source</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="protocol-signals-body">
|
|
<!-- Protocol signals will be populated by JavaScript -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add/Edit Mapping Modal -->
|
|
<div id="mapping-modal" class="modal" style="display: none;">
|
|
<div class="modal-content">
|
|
<span class="close" onclick="closeMappingModal()">×</span>
|
|
<h3 id="modal-title">Add Protocol Mapping</h3>
|
|
<form id="mapping-form">
|
|
<div class="form-group">
|
|
<label for="mapping_id">Mapping ID:</label>
|
|
<input type="text" id="mapping_id" name="mapping_id" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="mapping_protocol_type">Protocol Type:</label>
|
|
<select id="mapping_protocol_type" name="protocol_type" required onchange="updateProtocolFields()">
|
|
<option value="">Select Protocol</option>
|
|
<option value="modbus_tcp">Modbus TCP</option>
|
|
<option value="opcua">OPC UA</option>
|
|
<option value="modbus_rtu">Modbus RTU</option>
|
|
<option value="rest_api">REST API</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="station_id">Station:</label>
|
|
<select id="station_id" name="station_id" required>
|
|
<option value="">Select Station</option>
|
|
</select>
|
|
<small style="color: #666;">Stations will be loaded from tag metadata system</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="equipment_id">Equipment:</label>
|
|
<select id="equipment_id" name="equipment_id" required>
|
|
<option value="">Select Equipment</option>
|
|
</select>
|
|
<small style="color: #666;">Equipment will be loaded based on selected station</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="data_type_id">Data Type:</label>
|
|
<select id="data_type_id" name="data_type_id" required>
|
|
<option value="">Select Data Type</option>
|
|
</select>
|
|
<small style="color: #666;">Data types will be loaded from tag metadata system</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="protocol_address">Protocol Address:</label>
|
|
<input type="text" id="protocol_address" name="protocol_address" required>
|
|
<small id="protocol_address_help" style="color: #666;"></small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="db_source">Database Source:</label>
|
|
<input type="text" id="db_source" name="db_source" required placeholder="table.column">
|
|
</div>
|
|
<div class="action-buttons">
|
|
<button type="button" onclick="validateMapping()">Validate</button>
|
|
<button type="submit" style="background: #28a745;">Save Mapping</button>
|
|
<button type="button" onclick="closeMappingModal()" style="background: #dc3545;">Cancel</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Simplified Signal Modal (for discovery integration) -->
|
|
<div id="signal-modal" class="modal" style="display: none;">
|
|
<div class="modal-content">
|
|
<span class="close" onclick="closeSignalModal()">×</span>
|
|
<h3 id="modal-title">Add Protocol Signal</h3>
|
|
<form id="signal-form">
|
|
<div class="form-group">
|
|
<label for="signal_name">Signal Name *</label>
|
|
<input type="text" id="signal_name" name="signal_name" required>
|
|
<small style="color: #666;">Human-readable name for this signal (e.g., "Main Pump Speed")</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="tags">Tags</label>
|
|
<input type="text" id="tags" name="tags" placeholder="equipment:pump, protocol:modbus_tcp, data_point:speed">
|
|
<small style="color: #666;">Comma-separated tags for categorization and filtering</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="protocol_type">Protocol Type *</label>
|
|
<select id="protocol_type" name="protocol_type" required onchange="updateProtocolFields()">
|
|
<option value="">Select Protocol Type</option>
|
|
<option value="modbus_tcp">Modbus TCP</option>
|
|
<option value="modbus_rtu">Modbus RTU</option>
|
|
<option value="opcua">OPC UA</option>
|
|
<option value="rest_api">REST API</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="protocol_address">Protocol Address *</label>
|
|
<input type="text" id="protocol_address" name="protocol_address" required>
|
|
<small id="protocol-address-help" style="color: #666;"></small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="db_source">Database Source *</label>
|
|
<input type="text" id="db_source" name="db_source" required>
|
|
<small style="color: #666;">Database table and column name (e.g., measurements.pump_speed)</small>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>
|
|
<input type="checkbox" id="preprocessing_enabled" name="preprocessing_enabled">
|
|
Enable Signal Preprocessing
|
|
</label>
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<button type="button" onclick="validateSignal()">Validate</button>
|
|
<button type="submit" style="background: #28a745;">Save Signal</button>
|
|
<button type="button" onclick="closeSignalModal()" style="background: #dc3545;">Cancel</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions Tab -->
|
|
<div id="actions-tab" class="tab-content">
|
|
<h2>System Actions</h2>
|
|
<div class="config-section">
|
|
<h3>System Operations</h3>
|
|
<div class="action-buttons">
|
|
<button onclick="restartSystem()" style="background: #dc3545;">Restart System</button>
|
|
<button onclick="createBackup()" style="background: #28a745;">Create Backup</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Health Checks</h3>
|
|
<div class="action-buttons">
|
|
<button onclick="runHealthCheck()">Run Health Check</button>
|
|
<button onclick="viewMetrics()">View Metrics</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/static/dashboard.js"></script>
|
|
<script src="/static/protocol_mapping.js"></script>
|
|
<script src="/static/simplified_protocol_mapping.js"></script>
|
|
<script src="/static/discovery.js"></script>
|
|
<script src="/static/simplified_discovery.js"></script>
|
|
</body>
|
|
</html>
|
|
""" |