/** * @file plc_debug.js * @brief PLC 调试页 — 指示灯/按键绑定 out 信号 + 增量刷新 */ var PlcDebugLeds = []; var PlcDebugButtons = []; Router.register('#plc_debug', function(c) { PlcDebugPage.render(c); }); var PlcDebugPage = (function() { var outSignals = []; var ledChecked = {}, btnChecked = {}, sigChecked = {}; function getOut() { var out = SignalStore.get('out'); return (Array.isArray(out) && out.length) ? out : outSignals; } function showPicker(cb) { var sigs = getOut(); if (!sigs.length) { toast(I18n.t('plc_debug.no_sig'), 'error'); return; } var rows = ''; sigs.forEach(function(s) { rows += '
' + '' + esc(s.saddr) + '' + (s.desc ? ' ' + esc(s.desc) + '' : '') + '' + fmtVal(s.val) + '
'; }); var ov = document.createElement('div'); ov.className = 'modal-overlay'; ov.innerHTML = ''; document.body.appendChild(ov); document.getElementById('plc-pick-cancel').onclick = function() { ov.remove(); }; ov.addEventListener('click', function(e) { if (e.target === ov) ov.remove(); }); ov.querySelectorAll('.plc-pick-row').forEach(function(r) { r.onclick = function() { var sa = this.getAttribute('data-saddr'), de = this.getAttribute('data-desc'); ov.remove(); if (confirm(I18n.t('plc_debug.confirm_bind') + ' ' + sa + ' ?')) cb(sa, de); }; }); } function findSig(sa) { for (var i = 0; i < outSignals.length; i++) if (outSignals[i].saddr === sa) return outSignals[i]; return null; } function refreshData() { var o = getOut(); if (Array.isArray(o)) outSignals = o; PlcDebugLeds.forEach(function(l) { var f = findSig(l.saddr); l.val = f ? f.val : 0; }); PlcDebugButtons.forEach(function(b) { var f = findSig(b.saddr); b.val = f ? f.val : 0; }); } function incrRefresh() { refreshData(); PlcDebugLeds.forEach(function(l, i) { var on = parseFloat(l.val) > 0; var ve = document.getElementById('led-val-' + i), ie = document.getElementById('led-icon-' + i); if (ve) { ve.textContent = '(' + l.val + ')'; ve.style.color = on ? 'var(--accent)' : 'var(--text-muted)'; } if (ie) ie.className = 'plc-led' + (on ? ' plc-led-on' : ''); }); PlcDebugButtons.forEach(function(b, j) { var ve = document.getElementById('btn-val-' + j); if (ve) { ve.textContent = '(' + b.val + ')'; ve.style.color = parseFloat(b.val) > 0 ? 'var(--accent)' : 'var(--text-muted)'; } }); } function countChecked(obj) { var n = 0; for (var k in obj) if (obj.hasOwnProperty(k)) n++; return n; } function delChecked() { var todel = []; for (var k in ledChecked) if (ledChecked.hasOwnProperty(k)) todel.push({ type: 'led', idx: parseInt(k) }); for (var k in btnChecked) if (btnChecked.hasOwnProperty(k)) todel.push({ type: 'btn', idx: parseInt(k) }); if (!todel.length) { toast(I18n.t('toast.no_selected'), 'error'); return; } if (!confirm(I18n.t('plc_debug.confirm_del') + ' ' + todel.length + ' ?')) return; todel.sort(function(a, b) { return b.idx - a.idx; }).forEach(function(t) { if (t.type === 'led') PlcDebugLeds.splice(t.idx, 1); else PlcDebugButtons.splice(t.idx, 1); }); ledChecked = {}; btnChecked = {}; renderAll(); } function renderAll() { var c = document.getElementById('plc-content'); if (!c) return; refreshData(); /* 指示灯列 */ var ledHtml = ''; if (!PlcDebugLeds.length) { ledHtml = '
' + I18n.t('plc_debug.no_led') + '
'; } else { PlcDebugLeds.forEach(function(l, i) { var on = parseFloat(l.val) > 0, chk = !!ledChecked[i]; ledHtml += '
' + '
' + '
' + (l.saddr ? '' + esc(l.saddr) + ' (' + fmtVal(l.val) + ')' + (l.desc ? '' + esc(l.desc) + '' : '') : '' + I18n.t('plc_debug.unbound') + '') + '
' + '' + '
'; }); } /* 按键列 */ var btnHtml = ''; if (!PlcDebugButtons.length) { btnHtml = '
' + I18n.t('plc_debug.no_btn') + '
'; } else { PlcDebugButtons.forEach(function(b, j) { var chk = !!btnChecked[j]; btnHtml += '
' + '' + '
' + (b.saddr ? '' + esc(b.saddr) + ' (' + fmtVal(b.val) + ')' + (b.desc ? '' + esc(b.desc) + '' : '') : '' + I18n.t('plc_debug.unbound') + '') + '
' + '' + '
'; }); } /* 信号列 */ var o = getOut(); var sigHtml = ''; if (!o.length) { sigHtml = '
' + I18n.t('plc_debug.no_sig') + '
'; } else { o.forEach(function(s, i) { var chk = !!sigChecked[i]; sigHtml += '
' + '' + '' + esc(s.saddr) + '' + '' + esc(s.desc || '') + '' + '' + fmtVal(s.val) + '' + '
'; }); } c.innerHTML = '
' + '
💡 ' + I18n.t('plc_debug.led_col') + '' + '
' + '' + '
' + ledHtml + '
' + '
📋 ' + I18n.t('plc_debug.sig_col') + '' + '
' + '' + '' + '
' + sigHtml + '
' + '
🔘 ' + I18n.t('plc_debug.btn_col') + '' + '
' + '' + '
' + btnHtml + '
' + '
'; updateDelBtn(); } function updateDelBtn() { var el = document.getElementById('plc-del-btn'); if (!el) return; var n = countChecked(ledChecked) + countChecked(btnChecked); el.textContent = I18n.t('plc_debug.del_sel') + (n ? ' (' + n + ')' : ''); el.disabled = !n; } return { render: function(c) { ledChecked = {}; btnChecked = {}; sigChecked = {}; refreshData(); c.innerHTML = '
' + '
' + I18n.t('plc_debug.title') + '' + '
' + '' + '' + '
' + '
'; renderAll(); }, _addLed: function() { PlcDebugLeds.push({ saddr: '', desc: '', val: 0 }); renderAll(); }, _addBtn: function() { PlcDebugButtons.push({ saddr: '', desc: '', val: 0 }); renderAll(); }, _bindLed: function(i) { showPicker(function(sa, de) { PlcDebugLeds[i].saddr = sa; PlcDebugLeds[i].desc = de; var f = findSig(sa); if (f) PlcDebugLeds[i].val = f.val; renderAll(); }); }, _toggleBtn: function(j) { var b = PlcDebugButtons[j]; if (!b.saddr) { showPicker(function(sa, de) { b.saddr = sa; b.desc = de; var f = findSig(sa); if (f) b.val = f.val; renderAll(); }); return; } var nv = parseFloat(b.val) > 0 ? 0 : 1; WsClient.send({ curd: 'set', signal_type: 'out', saddr: b.saddr, signal_data: String(nv), setting_zone: '0' }); b.val = nv; renderAll(); }, _toggleLed: function(i, v) { ledChecked[i] = v; updateDelBtn(); }, _toggleBtnChk: function(j, v) { btnChecked[j] = v; updateDelBtn(); }, _toggleSig: function(i, v) { sigChecked[i] = v; }, _moveSel: function(target) { var picked = []; for (var k in sigChecked) if (sigChecked.hasOwnProperty(k)) picked.push(parseInt(k)); if (!picked.length) { toast(I18n.t('toast.no_selected'), 'error'); return; } picked.sort(function(a, b) { return a - b; }).forEach(function(i) { var s = outSignals[i]; if (!s) return; if (target === 'led') PlcDebugLeds.push({ saddr: s.saddr, desc: s.desc || '', val: s.val || 0 }); else PlcDebugButtons.push({ saddr: s.saddr, desc: s.desc || '', val: s.val || 0 }); }); sigChecked = {}; renderAll(); }, _refresh: function() { refreshData(); incrRefresh(); }, _delChecked: delChecked, }; })();