RTU/test/web_root/js/plc_debug.js

276 lines
11 KiB
JavaScript

/* ========================================================================
* PLC 调试页面 — 指示灯/按键 + out 绑定 + 勾选删除 + 增量刷新
* ======================================================================== */
var PlcDebugLeds = []; // [{saddr:'', desc:'', val:0}]
var PlcDebugButtons = []; // [{saddr:'', desc:'', val:0}]
var PlcDebugPage = (function() {
var outSignals = [];
var ledChecked = {}; // {idx: true}
var btnChecked = {}; // {idx: true}
function getOutSignals() {
if (typeof App !== 'undefined' && App.getSignalData) {
var out = App.getSignalData('out');
if (Array.isArray(out) && out.length > 0) return out;
}
return outSignals;
}
function showSignalPicker(callback) {
var sigs = getOutSignals();
if (sigs.length === 0) { alert('暂无 out 已注册信号,请先在注册(out)页面添加信号'); return; }
var html = '<div class="modal-overlay" id="plc-modal"><div class="modal-box" style="max-width:550px">'
+ '<h3>绑定 out 信号</h3>'
+ '<div style="max-height:300px;overflow-y:auto;margin:12px 0">';
for (var i = 0; i < sigs.length; i++) {
var s = sigs[i];
html += '<div class="plc-sig-row" data-saddr="' + fmtVal(s.saddr) + '" data-desc="' + fmtVal(s.desc || '') + '" style="padding:8px;cursor:pointer;border-bottom:1px solid #eee">'
+ '<strong>' + fmtVal(s.saddr) + '</strong>'
+ (s.desc ? ' <span style="color:#999">' + fmtVal(s.desc) + '</span>' : '')
+ '<span style="float:right;color:#2196F3;font-weight:700">' + (s.val !== undefined ? fmtVal(s.val) : '-') + '</span>'
+ '</div>';
}
html += '</div><button class="btn btn-outline" id="plc-modal-cancel">取消</button></div></div>';
var div = document.createElement('div');
div.innerHTML = html;
document.body.appendChild(div.firstElementChild);
document.getElementById('plc-modal-cancel').onclick = closeModal;
var rows = document.querySelectorAll('.plc-sig-row');
for (var j = 0; j < rows.length; j++) {
rows[j].onclick = function() {
var saddr = this.getAttribute('data-saddr');
var desc = this.getAttribute('data-desc') || '';
closeModal();
if (confirm('确定绑定信号 ' + saddr + ' 吗?')) { callback(saddr, desc); }
};
}
}
function closeModal() { var m = document.getElementById('plc-modal'); if (m) m.remove(); }
function refreshOutData() {
var sigs = getOutSignals();
if (Array.isArray(sigs)) outSignals = sigs;
for (var i = 0; i < PlcDebugLeds.length; i++) {
var found = findSignal(PlcDebugLeds[i].saddr);
PlcDebugLeds[i].val = found ? found.val : 0;
}
for (var j = 0; j < PlcDebugButtons.length; j++) {
var found2 = findSignal(PlcDebugButtons[j].saddr);
PlcDebugButtons[j].val = found2 ? found2.val : 0;
}
}
function findSignal(saddr) {
for (var i = 0; i < outSignals.length; i++) {
if (outSignals[i].saddr === saddr) return outSignals[i];
}
return null;
}
function countChecked(obj) { var n = 0; for (var k in obj) { if (obj.hasOwnProperty(k)) n++; } return n; }
function updateDelBtn() {
var ledCnt = countChecked(ledChecked);
var btnCnt = countChecked(btnChecked);
var total = ledCnt + btnCnt;
var el = document.getElementById('plc-del-btn');
if (el) {
el.textContent = '🗑 删除选中' + (total > 0 ? '(' + total + ')' : '');
el.disabled = (total === 0);
}
}
/* ---- 增量刷新: 只更新 val 文本, 不重建 DOM (add/del 时才全重建) ---- */
function incrRefresh() {
refreshOutData();
// 指示灯: 逐个更新 val 文本和样式
for (var i = 0; i < PlcDebugLeds.length; i++) {
var valEl = document.getElementById('led-val-' + i);
var iconEl = document.getElementById('led-icon-' + i);
var on = (parseFloat(PlcDebugLeds[i].val) > 0);
if (valEl) valEl.textContent = '(' + PlcDebugLeds[i].val + ')';
if (valEl) valEl.style.color = on ? '#4CAF50' : '#999';
if (iconEl) { iconEl.className = 'plc-led' + (on ? ' plc-led-on' : ''); }
}
// 按键: 逐个更新 val 文本
for (var j = 0; j < PlcDebugButtons.length; j++) {
var bValEl = document.getElementById('btn-val-' + j);
if (bValEl) bValEl.textContent = '(' + PlcDebugButtons[j].val + ')';
}
}
function renderAll() {
refreshOutData();
var html = '';
html += '<div class="plc-toolbar">'
+ '<button class="btn btn-warn" id="plc-manual-set">⚙ 手动置数</button>'
+ '<button class="btn btn-outline" id="plc-refresh">🔄 刷新</button>'
+ '<span style="flex:1"></span>'
+ '<button class="btn btn-danger" id="plc-del-btn" disabled>🗑 删除选中</button>'
+ '<button class="btn btn-outline" id="plc-exit-set">🚪 退出置数</button>'
+ '</div>';
html += '<div class="plc-actions">'
+ '<button class="btn btn-primary" id="plc-add-led">💡 增加指示灯</button>'
+ '<span style="flex:1"></span>'
+ '<button class="btn btn-info" id="plc-add-btn">🔘 增加按键</button>'
+ '</div>';
html += '<div class="plc-grid">';
// 指示灯
html += '<div class="plc-col"><h3>💡 指示灯</h3>';
for (var i = 0; i < PlcDebugLeds.length; i++) {
var led = PlcDebugLeds[i];
var on = (parseFloat(led.val) > 0);
var chk = ledChecked[i] ? ' checked' : '';
html += '<div class="plc-item">'
+ '<input type="checkbox" class="plc-cb" data-led-idx="' + i + '"' + chk + '>'
+ '<span class="plc-led ' + (on ? 'plc-led-on' : '') + '" id="led-icon-' + i + '" data-led-idx="' + i + '">💡</span>'
+ '<span class="plc-info">'
+ (led.saddr ? '<span class="plc-saddr">' + fmtVal(led.saddr) + ' <span id="led-val-' + i + '" style="color:' + (on ? '#4CAF50' : '#999') + '">(' + fmtVal(led.val) + ')</span>'
+ (led.desc ? ' <span style="color:#888;font-size:12px">' + fmtVal(led.desc) + '</span>' : '')
: '<span style="color:#999">未绑定</span>')
+ '</span></div>';
}
html += '</div>';
// 按键
html += '<div class="plc-col"><h3>🔘 按键</h3>';
for (var j = 0; j < PlcDebugButtons.length; j++) {
var btn = PlcDebugButtons[j];
var chk2 = btnChecked[j] ? ' checked' : '';
html += '<div class="plc-item">'
+ '<input type="checkbox" class="plc-cb" data-btn-idx="' + j + '"' + chk2 + '>'
+ '<span class="plc-btn-icon" id="btn-icon-' + j + '" data-btn-idx="' + j + '">🔘</span>'
+ '<span class="plc-info">'
+ (btn.saddr ? '<span class="plc-saddr">' + fmtVal(btn.saddr) + ' <span id="btn-val-' + j + '" style="color:#2196F3">(' + fmtVal(btn.val) + ')</span>'
+ (btn.desc ? ' <span style="color:#888;font-size:12px">' + fmtVal(btn.desc) + '</span>' : '')
: '<span style="color:#999">未绑定</span>')
+ '</span></div>';
}
html += '</div></div>';
document.getElementById('plc-content').innerHTML = html;
// ---- 事件绑定 ----
document.getElementById('plc-add-led').onclick = function() {
PlcDebugLeds.push({ saddr: '', desc: '', val: 0 });
renderAll();
};
document.getElementById('plc-add-btn').onclick = function() {
PlcDebugButtons.push({ saddr: '', desc: '', val: 0 });
renderAll();
};
// 删除选中
document.getElementById('plc-del-btn').onclick = function() {
var delLeds = [], delBtns = [];
for (var k in ledChecked) { if (ledChecked.hasOwnProperty(k)) delLeds.push(parseInt(k)); }
for (var k in btnChecked) { if (btnChecked.hasOwnProperty(k)) delBtns.push(parseInt(k)); }
var total = delLeds.length + delBtns.length;
if (total === 0) return;
if (!confirm('确定删除 ' + total + ' 个控件?')) return;
delLeds.sort(function(a,b){return b-a;});
delBtns.sort(function(a,b){return b-a;});
for (var a = 0; a < delLeds.length; a++) PlcDebugLeds.splice(delLeds[a], 1);
for (var b = 0; b < delBtns.length; b++) PlcDebugButtons.splice(delBtns[b], 1);
ledChecked = {}; btnChecked = {};
renderAll();
};
// LED 点击
var ledIcons = document.querySelectorAll('.plc-led');
for (var k = 0; k < ledIcons.length; k++) {
ledIcons[k].onclick = function() {
var idx = parseInt(this.getAttribute('data-led-idx'));
if (PlcDebugLeds[idx].saddr) {
if (!confirm('当前绑定: ' + PlcDebugLeds[idx].saddr + ', 是否修改绑定?')) return;
}
showSignalPicker(function(saddr, desc) {
PlcDebugLeds[idx].saddr = saddr;
PlcDebugLeds[idx].desc = desc || '';
renderAll();
});
};
}
// 按钮点击
var btnIcons = document.querySelectorAll('.plc-btn-icon');
for (var m = 0; m < btnIcons.length; m++) {
btnIcons[m].onclick = function() {
var idx = parseInt(this.getAttribute('data-btn-idx'));
if (!PlcDebugButtons[idx].saddr) {
showSignalPicker(function(saddr, desc) {
PlcDebugButtons[idx].saddr = saddr;
PlcDebugButtons[idx].desc = desc || '';
PlcDebugButtons[idx].val = 0;
renderAll();
});
return;
}
var newVal = (PlcDebugButtons[idx].val == 1) ? 0 : 1;
PlcDebugButtons[idx].val = newVal;
WsClient.send({ curd:'set', signal_type:'out', saddr:PlcDebugButtons[idx].saddr, signal_data:String(newVal), setting_zone:'0' });
showToast('✅ 已发送翻转: ' + PlcDebugButtons[idx].saddr + ' → ' + newVal);
incrRefresh();
};
}
// 勾选框
var allCbs = document.querySelectorAll('.plc-cb');
for (var n = 0; n < allCbs.length; n++) {
allCbs[n].onclick = function(e) {
e.stopPropagation();
var ledIdx = this.getAttribute('data-led-idx');
var btnIdx = this.getAttribute('data-btn-idx');
if (ledIdx !== null) {
if (this.checked) ledChecked[ledIdx] = true; else delete ledChecked[ledIdx];
} else if (btnIdx !== null) {
if (this.checked) btnChecked[btnIdx] = true; else delete btnChecked[btnIdx];
}
updateDelBtn();
};
}
document.getElementById('plc-refresh').onclick = function() {
refreshOutData();
renderAll();
showToast('🔄 已刷新');
};
var manualBtn = document.getElementById('plc-manual-set');
var exitBtn = document.getElementById('plc-exit-set');
if (manualBtn) manualBtn.onclick = function() { alert('手动置数功能待实现'); };
if (exitBtn) exitBtn.onclick = function() { alert('退出置数功能待实现'); };
}
function showToast(msg) {
var t = document.getElementById('plc-toast');
if (!t) {
t = document.createElement('div');
t.id = 'plc-toast';
t.style.cssText = 'position:fixed;top:80px;left:50%;transform:translateX(-50%);background:#333;color:#fff;padding:10px 24px;border-radius:6px;z-index:9999;font-size:14px;box-shadow:0 4px 12px rgba(0,0,0,0.3);transition:opacity 0.3s;pointer-events:none';
document.body.appendChild(t);
}
t.textContent = msg; t.style.opacity = '1';
clearTimeout(t._timer);
t._timer = setTimeout(function() { t.style.opacity = '0'; }, 1200);
}
return {
render: function(container) {
ledChecked = {}; btnChecked = {};
container.innerHTML = ''
+ '<div class="page-header"><h2>PLC 调试</h2><p>指示灯绑定 out 已注册信号(>0 亮/≤0 灭),按键绑定 out 已注册信号(点击翻转 0↔1)</p></div>'
+ '<div id="plc-content"></div>';
renderAll();
},
onEnter: function() { refreshOutData(); renderAll(); },
onLeave: function() {},
onData: function(data) {
if (Array.isArray(data)) { outSignals = data; }
incrRefresh();
}
};
})();