/**
* @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 = '' + I18n.t('plc_debug.bind_title') + '
'
+ '
' + rows + '
'
+ '
';
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 = '';
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 = '';
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,
};
})();