RTU_ALL_AI/config/web/js/pages/plc_debug.js

223 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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 += '<div class="plc-pick-row" data-saddr="' + esc(s.saddr) + '" data-desc="' + esc(s.desc || '') + '">'
+ '<strong>' + esc(s.saddr) + '</strong>'
+ (s.desc ? ' <span style="color:var(--text-muted)">' + esc(s.desc) + '</span>' : '')
+ '<span style="float:right;color:var(--accent);font-weight:700">' + fmtVal(s.val) + '</span></div>';
});
var ov = document.createElement('div');
ov.className = 'modal-overlay';
ov.innerHTML = '<div class="modal-box" style="max-width:550px"><h3>' + I18n.t('plc_debug.bind_title') + '</h3>'
+ '<div style="max-height:300px;overflow-y:auto;margin:12px 0">' + rows + '</div>'
+ '<button class="btn btn-sm" id="plc-pick-cancel">' + I18n.t('toast.cancel') + '</button></div>';
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 = '<div style="color:var(--text-muted);padding:20px;text-align:center">' + I18n.t('plc_debug.no_led') + '</div>';
} else {
PlcDebugLeds.forEach(function(l, i) {
var on = parseFloat(l.val) > 0, chk = !!ledChecked[i];
ledHtml += '<div class="plc-item">'
+ '<div class="plc-led' + (on ? ' plc-led-on' : '') + '" id="led-icon-' + i + '" onclick="PlcDebugPage._bindLed(' + i + ')" title="' + I18n.t('plc_debug.click_to_bind') + '"></div>'
+ '<div class="plc-info">'
+ (l.saddr ? '<span class="plc-saddr">' + esc(l.saddr) + ' <span id="led-val-' + i + '" style="color:' + (on ? 'var(--accent)' : 'var(--text-muted)') + '">(' + fmtVal(l.val) + ')</span></span>'
+ (l.desc ? '<span style="color:var(--text-muted);font-size:11px">' + esc(l.desc) + '</span>' : '')
: '<span style="color:var(--text-muted)">' + I18n.t('plc_debug.unbound') + '</span>')
+ '</div>'
+ '<input type="checkbox" ' + (chk ? 'checked' : '') + ' onchange="PlcDebugPage._toggleLed(' + i + ',this.checked)">'
+ '</div>';
});
}
/* 按键列 */
var btnHtml = '';
if (!PlcDebugButtons.length) {
btnHtml = '<div style="color:var(--text-muted);padding:20px;text-align:center">' + I18n.t('plc_debug.no_btn') + '</div>';
} else {
PlcDebugButtons.forEach(function(b, j) {
var chk = !!btnChecked[j];
btnHtml += '<div class="plc-item">'
+ '<button class="plc-btn-icon" id="btn-icon-' + j + '" onclick="PlcDebugPage._toggleBtn(' + j + ')" title="' + I18n.t('plc_debug.toggle') + '">🔘</button>'
+ '<div class="plc-info">'
+ (b.saddr ? '<span class="plc-saddr">' + esc(b.saddr) + ' <span id="btn-val-' + j + '" style="color:var(--text-muted)">(' + fmtVal(b.val) + ')</span></span>'
+ (b.desc ? '<span style="color:var(--text-muted);font-size:11px">' + esc(b.desc) + '</span>' : '')
: '<span style="color:var(--text-muted)">' + I18n.t('plc_debug.unbound') + '</span>')
+ '</div>'
+ '<input type="checkbox" ' + (chk ? 'checked' : '') + ' onchange="PlcDebugPage._toggleBtnChk(' + j + ',this.checked)">'
+ '</div>';
});
}
/* 信号列 */
var o = getOut();
var sigHtml = '';
if (!o.length) {
sigHtml = '<div style="color:var(--text-muted);padding:20px;text-align:center">' + I18n.t('plc_debug.no_sig') + '</div>';
} else {
o.forEach(function(s, i) {
var chk = !!sigChecked[i];
sigHtml += '<div class="plc-item">'
+ '<input type="checkbox" ' + (chk ? 'checked' : '') + ' onchange="PlcDebugPage._toggleSig(' + i + ',this.checked)">'
+ '<span class="plc-saddr">' + esc(s.saddr) + '</span>'
+ '<span style="color:var(--text-muted);font-size:11px">' + esc(s.desc || '') + '</span>'
+ '<span style="margin-left:auto;color:var(--accent);font-weight:700;font-family:var(--font-mono)">' + fmtVal(s.val) + '</span>'
+ '</div>';
});
}
c.innerHTML = '<div class="plc-debug-grid">'
+ '<div class="plc-col"><div class="card-header" style="margin-bottom:8px"><span>💡 ' + I18n.t('plc_debug.led_col') + '</span>'
+ '<div class="btn-group">'
+ '<button class="btn btn-sm" onclick="PlcDebugPage._addLed()">' + I18n.t('plc_debug.add_led') + '</button>'
+ '</div></div><div class="plc-sig-list">' + ledHtml + '</div></div>'
+ '<div class="plc-col plc-sig-col"><div class="card-header" style="margin-bottom:8px"><span>📋 ' + I18n.t('plc_debug.sig_col') + '</span>'
+ '<div class="plc-arrow-row">'
+ '<button class="btn btn-sm" onclick="PlcDebugPage._moveSel(\'led\')" title="' + I18n.t('plc_debug.to_led') + '">← LED</button>'
+ '<button class="btn btn-sm" onclick="PlcDebugPage._moveSel(\'btn\')" title="' + I18n.t('plc_debug.to_btn') + '">→ BTN</button>'
+ '</div></div><div class="plc-sig-list">' + sigHtml + '</div></div>'
+ '<div class="plc-col"><div class="card-header" style="margin-bottom:8px"><span>🔘 ' + I18n.t('plc_debug.btn_col') + '</span>'
+ '<div class="btn-group">'
+ '<button class="btn btn-sm" onclick="PlcDebugPage._addBtn()">' + I18n.t('plc_debug.add_btn') + '</button>'
+ '</div></div><div class="plc-sig-list">' + btnHtml + '</div></div>'
+ '</div>';
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 = '<div class="card">'
+ '<div class="card-header"><span>' + I18n.t('plc_debug.title') + '</span>'
+ '<div class="btn-group">'
+ '<button class="btn btn-sm" onclick="PlcDebugPage._refresh()">🔄 ' + I18n.t('plc_debug.refresh_btn') + '</button>'
+ '<button class="btn btn-danger btn-sm" id="plc-del-btn" disabled onclick="PlcDebugPage._delChecked()">🗑 ' + I18n.t('plc_debug.del_sel') + '</button>'
+ '</div></div>'
+ '<div id="plc-content"></div></div>';
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;
/* 同步写入 SignalStore确保 refreshData() 不覆盖新值 */
var outs = SignalStore.get('out');
for (var i = 0; i < outs.length; i++) {
if (outs[i].saddr === b.saddr) { outs[i].val = String(nv); break; }
}
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(); },
incrRefresh: function() { refreshData(); incrRefresh(); },
_delChecked: delChecked,
};
})();