294 lines
12 KiB
HTML
294 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>XTU WEB DEBUG - Signal Monitor</title>
|
||
<style>
|
||
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
|
||
.container { max-width: 1400px; margin: 0 auto; }
|
||
h1 { color: #333; text-align: center; }
|
||
.card { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||
input, select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
|
||
.saddr-input { width: 280px; }
|
||
select { width: 140px; }
|
||
button { padding: 8px 16px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
||
button.secondary { background: #2196F3; }
|
||
#status { padding: 6px 12px; border-radius: 20px; font-weight: bold; }
|
||
.status-connected { background: #4CAF50; color: white; }
|
||
.status-disconnected { background: #f44336; color: white; }
|
||
#log { background: #f8f9fa; height: 150px; padding: 10px; overflow-y: auto; font-family: monospace; font-size: 12px; }
|
||
.log-entry { margin: 4px 0; }
|
||
.log-receive { color: #2196F3; }
|
||
.log-send { color: #4CAF50; }
|
||
.log-error { color: #f44336; }
|
||
|
||
.table-container { margin-bottom: 20px; }
|
||
.table-title { font-size: 16px; font-weight: bold; margin-bottom: 8px; color: #2c3e50; }
|
||
.table-wrapper {
|
||
max-height: 320px;
|
||
overflow-y: auto;
|
||
border: 1px solid #eee;
|
||
border-radius: 4px;
|
||
}
|
||
.signalTable { width: 100%; border-collapse: collapse; background: white; }
|
||
.signalTable th {
|
||
position: sticky;
|
||
top: 0;
|
||
background: #2c3e50;
|
||
color: white;
|
||
padding: 12px;
|
||
text-align: left;
|
||
z-index: 10;
|
||
}
|
||
.signalTable td { padding: 10px; border-bottom: 1px solid #eee; }
|
||
.signalTable tr:hover { background: #f5f5f5; }
|
||
|
||
.form-row { display: flex; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; align-items: flex-end; }
|
||
.form-group { display: flex; flex-direction: column; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>XTU WEB DEBUG - Signal Monitor</h1>
|
||
|
||
<div class="card">
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>saddr *</label>
|
||
<input id="saddr" class="saddr-input" placeholder="iec.run_cnt">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>signal_type *</label>
|
||
<select id="signal_type">
|
||
<option value="out">out</option>
|
||
<option value="in">in</option>
|
||
<option value="yk">yk</option>
|
||
<option value="ao">ao</option>
|
||
<option value="param">param</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>curd *</label>
|
||
<select id="curd">
|
||
<option value="add">add</option>
|
||
<option value="set">set</option>
|
||
<option value="del">del</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>setting_zone</label>
|
||
<input id="setting_zone" value="0" placeholder="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>signal_data</label>
|
||
<input id="signal_data" placeholder="(can empty)">
|
||
</div>
|
||
</div>
|
||
<button onclick="sendSignal()">Send</button>
|
||
<button onclick="clearForm()" class="secondary">Clear Form</button>
|
||
<button onclick="clearAllTables()" class="secondary">Clear All Tables</button>
|
||
</div>
|
||
|
||
<div class="card">
|
||
WebSocket Status: <span id="status" class="status-disconnected">Disconnected</span>
|
||
<button onclick="connectWS()" class="secondary">Connect</button>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="table-container">
|
||
<div class="table-title">OUT 信号</div>
|
||
<div class="table-wrapper">
|
||
<table class="signalTable">
|
||
<thead><tr><th>#</th><th>saddr</th><th>desc</th><th>数据类型</th><th>val</th></tr></thead>
|
||
<tbody id="outBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<div class="table-title">IN 信号</div>
|
||
<div class="table-wrapper">
|
||
<table class="signalTable">
|
||
<thead><tr><th>#</th><th>saddr</th><th>desc</th><th>数据类型</th><th>val</th></tr></thead>
|
||
<tbody id="inBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<div class="table-title">YK 信号</div>
|
||
<div class="table-wrapper">
|
||
<table class="signalTable">
|
||
<thead><tr><th>#</th><th>saddr</th><th>desc</th><th>数据类型</th><th>val</th><th>ctrl_type</th></tr></thead>
|
||
<tbody id="ykBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<div class="table-title">AO 信号</div>
|
||
<div class="table-wrapper">
|
||
<table class="signalTable">
|
||
<thead><tr><th>#</th><th>saddr</th><th>desc</th><th>数据类型</th><th>val</th><th>ctrl_type</th><th>min</th><th>max</th><th>step</th><th>unit</th><th>default</th></tr></thead>
|
||
<tbody id="aoBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<div class="table-title">PARAM 信号(当前定值区:<span id="currentZone">0</span>)</div>
|
||
<div class="table-wrapper">
|
||
<table class="signalTable">
|
||
<thead><tr><th>#</th><th>saddr</th><th>desc</th><th>数据类型</th><th>val</th><th>ctrl_type</th><th>min</th><th>max</th><th>step</th><th>unit</th><th>default_val</th></tr></thead>
|
||
<tbody id="paramBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div id="log"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let ws = null;
|
||
const tableBodies = {
|
||
out: document.getElementById('outBody'),
|
||
in: document.getElementById('inBody'),
|
||
yk: document.getElementById('ykBody'),
|
||
ao: document.getElementById('aoBody'),
|
||
param: document.getElementById('paramBody')
|
||
};
|
||
|
||
const ctrlTypeMap = { 0: "只读", 1: "直控", 2: "选控" };
|
||
const log = document.getElementById('log');
|
||
const status = document.getElementById('status');
|
||
const currentZoneSpan = document.getElementById('currentZone');
|
||
|
||
function addLog(msg, type = 'info') {
|
||
const t = new Date().toLocaleTimeString();
|
||
log.innerHTML += `<div class="log-entry"><span>[${t}]</span> <span class="log-${type}">${msg}</span></div>`;
|
||
log.scrollTop = log.scrollHeight;
|
||
}
|
||
|
||
function connectWS() {
|
||
if (ws) return;
|
||
const url = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/ws`;
|
||
ws = new WebSocket(url);
|
||
ws.onopen = () => { status.textContent = 'Connected'; status.className = 'status-connected'; addLog('connected', 'info'); };
|
||
ws.onclose = () => { status.textContent = 'Disconnected'; status.className = 'status-disconnected'; ws = null; setTimeout(connectWS, 3000); };
|
||
ws.onerror = () => addLog('error', 'error');
|
||
ws.onmessage = (e) => {
|
||
try {
|
||
const data = JSON.parse(e.data);
|
||
addLog("received data", "receive");
|
||
parseData(data);
|
||
} catch (e) {
|
||
addLog("parse data error: " + e.message, "error");
|
||
}
|
||
};
|
||
}
|
||
|
||
function parseData(root) {
|
||
const setting_zone = document.getElementById('setting_zone').value.trim();
|
||
currentZoneSpan.textContent = setting_zone;
|
||
|
||
const types = ['out', 'in', 'yk', 'ao', 'param'];
|
||
types.forEach(type => {
|
||
const arr = root[type];
|
||
const body = tableBodies[type];
|
||
body.innerHTML = '';
|
||
if (!Array.isArray(arr)) return;
|
||
|
||
arr.forEach((item, index) => {
|
||
if (!item.saddr) return;
|
||
|
||
if (type === 'param') {
|
||
const zoneList = item.setting_zone_list || [];
|
||
const match = zoneList.find(z => z.id === setting_zone);
|
||
if (!match) return;
|
||
|
||
const tr = body.insertRow();
|
||
tr.innerHTML = `
|
||
<td>${index+1}</td>
|
||
<td>${item.saddr}</td>
|
||
<td>${item.desc || '-'}</td>
|
||
<td>${item.type || '-'}</td>
|
||
<td>${match.val || '-'}</td>
|
||
<td>${ctrlTypeMap[item.ctrl_type] || item.ctrl_type}</td>
|
||
<td>${item.min || '-'}</td>
|
||
<td>${item.max || '-'}</td>
|
||
<td>${item.step || '-'}</td>
|
||
<td>${item.unit || '-'}</td>
|
||
<td>${match.default_val || '-'}</td>
|
||
`;
|
||
} else {
|
||
addNormalRow(type, item, index+1);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function addNormalRow(type, item, index) {
|
||
const body = tableBodies[type];
|
||
const tr = body.insertRow();
|
||
let html = `
|
||
<td>${index}</td>
|
||
<td>${item.saddr}</td>
|
||
<td>${item.desc || '-'}</td>
|
||
<td>${item.type || '-'}</td>
|
||
<td>${item.val || '-'}</td>
|
||
`;
|
||
if (type === 'yk') html += `<td>${ctrlTypeMap[item.ctrl_type] || item.ctrl_type}</td>`;
|
||
if (type === 'ao') {
|
||
html += `
|
||
<td>${ctrlTypeMap[item.ctrl_type] || item.ctrl_type}</td>
|
||
<td>${item.min || '-'}</td>
|
||
<td>${item.max || '-'}</td>
|
||
<td>${item.step || '-'}</td>
|
||
<td>${item.unit || '-'}</td>
|
||
<td>${item.default || '-'}</td>
|
||
`;
|
||
}
|
||
tr.innerHTML = html;
|
||
}
|
||
|
||
function sendSignal() {
|
||
const saddr = document.getElementById('saddr').value.trim();
|
||
const type = document.getElementById('signal_type').value;
|
||
const curd = document.getElementById('curd').value;
|
||
const setting_zone = document.getElementById('setting_zone').value.trim();
|
||
const data = document.getElementById('signal_data').value.trim();
|
||
|
||
if (!saddr) return addLog('saddr required', 'error');
|
||
if (!ws || ws.readyState !== 1) return addLog('not connected', 'error');
|
||
|
||
const msg = {
|
||
saddr,
|
||
signal_type: type,
|
||
curd,
|
||
setting_zone: setting_zone,
|
||
signal_data: data
|
||
};
|
||
|
||
ws.send(JSON.stringify(msg));
|
||
addLog(`sent: ${JSON.stringify(msg)}`, 'send');
|
||
parseData(JSON.parse(ws._lastData || '{}'));
|
||
}
|
||
|
||
function clearForm() {
|
||
document.getElementById('saddr').value = '';
|
||
document.getElementById('signal_data').value = '';
|
||
}
|
||
|
||
function clearAllTables() {
|
||
Object.values(tableBodies).forEach(body => body.innerHTML = '');
|
||
addLog('all tables cleared', 'info');
|
||
}
|
||
|
||
connectWS();
|
||
</script>
|
||
</body>
|
||
</html> |