/** * @file signal-store.js * @brief 信号数据缓存 — 纯内存, WS 推送驱动, 不持久化 + 增量 DOM 刷新 */ var SignalStore = { data: { out: [], yk: [], ao: [], param: [] }, /** * @brief 合并 WS 推送数据 + 增量更新可见 DOM (不重刷整页) */ update: function(push) { var self = this; ['out', 'yk', 'ao', 'param'].forEach(function(type) { if (!push[type]) return; var map = {}; self.data[type].forEach(function(s) { map[s.saddr] = s; }); push[type].forEach(function(s) { map[s.saddr] = s; }); self.data[type] = Object.keys(map).map(function(k) { return map[k]; }); /* 增量更新可见页面 DOM (仅更新变化的单元格,不重绘整页) */ var tbody = document.getElementById('tbody-' + type); if (!tbody) return; push[type].forEach(function(s) { var row = tbody.querySelector('tr[data-saddr="' + s.saddr.replace(/"/g, '\\"') + '"]'); if (!row) return; /* 更新值列 (class=cell-val) */ var valCell = row.querySelector('.cell-val'); if (valCell && s.val !== undefined) { var nv = (s.val === null || s.val === '') ? '—' : String(s.val); if (valCell.textContent.trim() !== nv) { valCell.textContent = nv; valCell.classList.add('changed'); setTimeout(function() { valCell.classList.remove('changed'); }, 600); } } /* 更新 ctrl_type 列 (YK/AO) */ if (s.ctrl_type !== undefined) { var ctrlCell = row.querySelector('.cell-ctrl'); if (ctrlCell) { var ct = (s.ctrl_type == 1) ? (I18n.t('signal.ctrl_sbo') || 'SBO') : (I18n.t('signal.ctrl_direct') || 'Direct'); if (ctrlCell.textContent.trim() !== ct) ctrlCell.textContent = ct; } } /* Param: 更新定值区值 */ if (type === 'param' && s.setting_zone_list) { s.setting_zone_list.forEach(function(z) { var zoneInput = row.querySelector('.param-zone-val[data-zone="' + z.id + '"]'); if (zoneInput) { var zv = z.val || ''; if (zoneInput.textContent !== zv) zoneInput.textContent = zv; } }); } }); }); /* PLC Debug 页增量刷新 */ if (typeof PlcDebugPage !== 'undefined' && PlcDebugPage.incrRefresh) { PlcDebugPage.incrRefresh(); } }, get: function(type) { return this.data[type] || []; }, /** @brief 删除信号 (内存 + 立即移除 DOM,不等后端确认) */ remove: function(type, saddr) { this.data[type] = this.data[type].filter(function(s) { return s.saddr !== saddr; }); /* 立即从页面移除对应行 */ var row = document.querySelector('#tbody-' + type + ' tr[data-saddr="' + saddr.replace(/"/g, '\\"') + '"]'); if (row) { row.style.transition = 'opacity 0.2s'; row.style.opacity = '0'; setTimeout(function() { if (row.parentNode) row.parentNode.removeChild(row); }, 200); } }, /** @brief 清空所有缓存 (WebSocket 断连时调用) */ clear: function() { this.data = { out: [], yk: [], ao: [], param: [] }; } };