/**
* @file datacenter.js
* @brief RTU_ALL_AI 数据中心 — 搜索过滤 + 复选框 + 批量添加到配置页
*/
Router.register('#datacenter', function(c) { DcPage.render(c); });
var DcPage = (function() {
var allData = {};
var activeTab = 'out';
var searchText = '';
var selectedSet = {};
var MAX_SIGNALS = 500;
function getVisibleData() {
var data = allData[activeTab] || [];
if (!searchText) return data;
var st = searchText.toLowerCase();
return data.filter(function(x) {
return (x.saddr && x.saddr.toLowerCase().indexOf(st) !== -1)
|| (x.desc && x.desc.toLowerCase().indexOf(st) !== -1);
});
}
function getSelectedCount() {
var c = 0;
for (var k in selectedSet) { if (selectedSet.hasOwnProperty(k)) c++; }
return c;
}
function updateSelBar() {
var b = document.getElementById('dc-sel-bar');
var l = document.getElementById('dc-sel-label');
if (!b || !l) return;
var n = getSelectedCount();
b.style.display = n > 0 ? 'flex' : 'none';
l.textContent = I18n.t('dc.sel_count').replace('{n}', n);
}
function renderTabs() {
var types = ['out', 'in', 'yk', 'ao', 'param'];
var labels = { out: '注册 (out)', in: '链接注册 (in)', yk: '遥控 (yk)', ao: '参数 (ao)', param: '定值 (param)' };
var el = document.getElementById('dc-tab-bar');
if (!el) return;
var h = '';
types.forEach(function(t) {
h += '';
});
el.innerHTML = h;
}
function doAddSelected() {
var visible = getVisibleData(), items = [];
visible.forEach(function(x) { if (selectedSet[x.saddr]) items.push(x); });
if (items.length === 0) { toast(I18n.t('dc.no_selected'), 'error'); return; }
var existing = SignalStore.get(activeTab);
if (existing.length + items.length > MAX_SIGNALS) {
toast('MAX ' + MAX_SIGNALS + ' exceeded', 'error'); return;
}
if (!confirm(I18n.t('dc.confirm_add').replace('{n}', items.length) + ' -> ' + activeTab.toUpperCase())) return;
var ok = 0;
items.forEach(function(x) {
WsClient.send({ saddr: x.saddr, signal_type: activeTab, curd: 'add', setting_zone: '0', signal_data: '' });
ok++;
});
toast('Added ' + ok + ' to ' + activeTab.toUpperCase(), 'success');
selectedSet = {};
DcPage._renderTable();
updateSelBar();
}
function renderTable() {
var data = getVisibleData();
var tbody = document.getElementById('dc-tbody');
var cntEl = document.getElementById('dc-count');
if (!tbody) return;
if (cntEl) cntEl.textContent = data.length + ' / ' + (allData[activeTab] || []).length;
var allChk = data.length > 0 && data.every(function(x) { return !!selectedSet[x.saddr]; });
var thEl = document.getElementById('dc-thead');
if (thEl) thEl.innerHTML = '
| '
+ 'id | saddr | desc | val | type | ctrl | link | ';
if (data.length === 0) {
tbody.innerHTML = '| ' + I18n.t('dc.no_signal') + ' |
';
return;
}
var h = '';
data.forEach(function(x) {
var chk = !!selectedSet[x.saddr];
h += ' | '
+ '' + fmtVal(x.id) + ' | '
+ '' + esc(x.saddr) + ' | '
+ '' + esc(x.desc || '') + ' | '
+ '' + fmtVal(x.val) + ' | '
+ '' + esc(x.type || '') + ' | '
+ '' + (x.ctrl_type == 1 ? 'SBO' : 'Direct') + ' | '
+ '' + esc(x.link_saddrs || '') + ' |
';
});
tbody.innerHTML = h;
}
function parseDcOutput(text) {
if (!text || text.length < 10) return;
var types = ['out', 'in', 'yk', 'ao', 'param'], currentType = '';
var lines = text.split('\n');
lines.forEach(function(line) {
types.forEach(function(t) {
if (line.indexOf(t + ' signals:') >= 0) currentType = t;
});
if (!currentType) return;
if (line.indexOf('---') >= 0 || line.indexOf('saddr') >= 0 || line.indexOf(' id ') >= 0) return;
var trimmed = line.trim();
if (!trimmed) return;
var parts = trimmed.split(/\s+/);
if (parts.length < 4) return;
var item = {};
if (currentType === 'param') {
item = { id: parseInt(parts[0]), saddr: parts[1], desc: parts[2], type: parts[3] || '', ctrl_type: parseInt(parts[4]) || 0 };
} else if (currentType === 'in') {
item = { id: parseInt(parts[0]), saddr: parts[1], desc: parts[2], link_saddrs: parts[3] || '', type: parts[4] || '', val: parts[5] || '' };
} else {
item = { id: parseInt(parts[0]), saddr: parts[1], desc: parts[2], type: parts[3] || '', val: parts[4] || '' };
}
allData[currentType] = allData[currentType] || [];
allData[currentType].push(item);
});
DcPage._renderTable();
toast(I18n.t('dc.refreshed'), 'success');
}
function refreshAll() {
allData = { out: [], in: [], yk: [], ao: [], param: [] };
toast(I18n.t('dc.querying'), 'info');
var buf = '', timer = null, done = false;
var handler = function(data, binary) {
if (binary) {
buf += new TextDecoder().decode(binary);
clearTimeout(timer);
timer = setTimeout(function() {
if (done) return; done = true;
WsClient.msgCbs = WsClient.msgCbs.filter(function(cb) { return cb !== handler; });
parseDcOutput(buf);
}, 600);
}
};
WsClient.msgCbs.unshift(handler);
WsClient.send({ type: 'cmd', data: 'datacenter all' });
/* 兜底超时, 但只在 600ms 定时器未触发时生效 */
setTimeout(function() {
if (buf && !done) {
done = true;
WsClient.msgCbs = WsClient.msgCbs.filter(function(cb) { return cb !== handler; });
parseDcOutput(buf);
}
}, 6000);
}
return {
render: function(container) {
allData = {}; activeTab = 'out'; searchText = ''; selectedSet = {};
container.innerHTML = ''
+ ''
+ '
'
+ '
'
+ ''
+ ''
+ '
'
+ '
';
renderTabs();
renderTable();
},
refreshAll: refreshAll,
switchTab: function(t) {
activeTab = t; searchText = ''; selectedSet = {};
var inp = document.getElementById('dc-search'); if (inp) inp.value = '';
renderTabs(); DcPage._renderTable(); updateSelBar();
},
_renderTable: renderTable,
_search: function() {
var inp = document.getElementById('dc-search');
searchText = inp ? inp.value : '';
renderTable();
},
_toggleAll: function() {
var cb = document.getElementById('dc-cb-all');
var allChk = cb ? cb.checked : false;
var v = getVisibleData();
if (allChk) { v.forEach(function(x) { selectedSet[x.saddr] = true; }); }
else { v.forEach(function(x) { delete selectedSet[x.saddr]; }); }
renderTable(); updateSelBar();
},
_toggleRow: function(saddr) {
if (selectedSet[saddr]) { delete selectedSet[saddr]; }
else { selectedSet[saddr] = true; }
renderTable(); updateSelBar();
},
_addSelected: doAddSelected
};
})();