383 lines
15 KiB
JavaScript
383 lines
15 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}
|
|
var signalChecked = {}; // {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-signal-col">';
|
|
html += '<h3>📋 Out 信号</h3>';
|
|
html += '<div class="plc-arrow-row">';
|
|
html += '<button class="plc-arrow-btn" id="plc-arrow-left" title="选中信号→添加到指示灯">←</button>';
|
|
html += '<button class="plc-arrow-btn" id="plc-arrow-right" title="选中信号→添加到按键">→</button>';
|
|
html += '</div>';
|
|
html += '<div class="plc-sig-list">';
|
|
if (outSignals.length === 0) {
|
|
html += '<div style="color:#999;text-align:center;padding:20px">暂无 out 信号</div>';
|
|
}
|
|
else
|
|
{
|
|
for (var s = 0; s < outSignals.length; s++) {
|
|
var sig = outSignals[s];
|
|
var sigChk = signalChecked[s] ? ' checked' : '';
|
|
html += '<div class="plc-sig-check-item">'
|
|
+ '<input type="checkbox" class="plc-sig-cb" data-sig-idx="' + s + '"' + sigChk + '>'
|
|
+ '<span class="plc-sig-saddr" title="' + fmtVal(sig.saddr) + (sig.desc ? ' - ' + fmtVal(sig.desc) : '') + '">' + fmtVal(sig.saddr) + '</span>'
|
|
+ (sig.desc ? '<span style="color:#999;font-size:11px;max-width:60px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + fmtVal(sig.desc) + '</span>' : '')
|
|
+ '<span class="plc-sig-val">' + fmtVal(sig.val) + '</span>'
|
|
+ '</div>';
|
|
}
|
|
}
|
|
html += '</div></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();
|
|
};
|
|
}
|
|
|
|
// 信号选择列勾选框
|
|
var sigCbs = document.querySelectorAll('.plc-sig-cb');
|
|
for (var p = 0; p < sigCbs.length; p++) {
|
|
sigCbs[p].onclick = function(e) {
|
|
e.stopPropagation();
|
|
var sigIdx = parseInt(this.getAttribute('data-sig-idx'));
|
|
if (!isNaN(sigIdx)) {
|
|
if (this.checked) signalChecked[sigIdx] = true; else delete signalChecked[sigIdx];
|
|
}
|
|
};
|
|
}
|
|
|
|
// 信号选择列整行点击切换勾选
|
|
var sigRows = document.querySelectorAll('.plc-sig-check-item');
|
|
for (var q = 0; q < sigRows.length; q++) {
|
|
sigRows[q].onclick = function(e) {
|
|
if (e.target.tagName === 'INPUT') return;
|
|
var cb = this.querySelector('.plc-sig-cb');
|
|
if (cb) {
|
|
cb.checked = !cb.checked;
|
|
var sigIdx = parseInt(cb.getAttribute('data-sig-idx'));
|
|
if (!isNaN(sigIdx)) {
|
|
if (cb.checked) signalChecked[sigIdx] = true; else delete signalChecked[sigIdx];
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// 左箭头 → 添加到指示灯
|
|
var arrowLeft = document.getElementById('plc-arrow-left');
|
|
if (arrowLeft) {
|
|
arrowLeft.onclick = function(e) {
|
|
e.stopPropagation();
|
|
var added = 0;
|
|
for (var k in signalChecked) {
|
|
if (signalChecked.hasOwnProperty(k)) {
|
|
var idx = parseInt(k);
|
|
if (idx >= 0 && idx < outSignals.length) {
|
|
PlcDebugLeds.push({ saddr: outSignals[idx].saddr, desc: outSignals[idx].desc || '', val: outSignals[idx].val });
|
|
added++;
|
|
}
|
|
}
|
|
}
|
|
if (added > 0) {
|
|
showToast('✅ 已添加 ' + added + ' 个信号到指示灯');
|
|
signalChecked = {};
|
|
renderAll();
|
|
}
|
|
else {
|
|
showToast('⚠️ 请先勾选信号');
|
|
}
|
|
};
|
|
}
|
|
|
|
// 右箭头 → 添加到按键
|
|
var arrowRight = document.getElementById('plc-arrow-right');
|
|
if (arrowRight) {
|
|
arrowRight.onclick = function(e) {
|
|
e.stopPropagation();
|
|
var added = 0;
|
|
for (var k in signalChecked) {
|
|
if (signalChecked.hasOwnProperty(k)) {
|
|
var idx = parseInt(k);
|
|
if (idx >= 0 && idx < outSignals.length) {
|
|
PlcDebugButtons.push({ saddr: outSignals[idx].saddr, desc: outSignals[idx].desc || '', val: outSignals[idx].val });
|
|
added++;
|
|
}
|
|
}
|
|
}
|
|
if (added > 0) {
|
|
showToast('✅ 已添加 ' + added + ' 个信号到按键');
|
|
signalChecked = {};
|
|
renderAll();
|
|
}
|
|
else {
|
|
showToast('⚠️ 请先勾选信号');
|
|
}
|
|
};
|
|
}
|
|
|
|
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 = {}; signalChecked = {};
|
|
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();
|
|
}
|
|
};
|
|
})();
|