6 border-bottom-left-radius: 0;
7 border-bottom-right-radius: 0;
8 border-right: 1px solid white;
12 border-right: 1px solid white;
21 text-align-last:center;
24 background-color: transparent;
29 border-left: 2px solid #808080;
30 border-right: 2px solid #808080;
31 border-top: 2px solid #808080;
32 border-radius: 5px 5px 0 0;
36let estyle = document.createElement('style');
37estyle.textContent = eqtable_style;
38document.head.appendChild(estyle);
40<!-- Select file dialog -->
42<div class="dlgTitlebar">Select file</div>
45 <div style="margin: auto;width: 70%">
46 <input type="file" id="fileSelector" accept=".json">
49 <button class="dlgButton" onclick="importFileFromSelector(this.parentNode.parentNode);dlgHide('dlgFileSelect');">Load</button>
50 <button class="dlgButton" onclick="dlgHide('dlgFileSelect')">Cancel</button>
64function check_frontend() {
66 let fename = equipment.frontend_name;
67 mjsonrpc_call("cm_exist", {"name": fename}).then( function(rpc) {
68 if (rpc.result.status === 1)
69 document.getElementById("fewarning").style.display = "none";
71 document.getElementById("fewarning").style.display = "";
72 document.getElementById("fewarning").firstChild.innerHTML =
73 "<a href='?cmd=Programs'>" + fename + "</a> not running";
75 window.setTimeout(check_frontend, 5000);
76 }).catch(function(error){mjsonrpc_error_alert(error);});
80function valueChanged(t) {
82 let td = t.parentElement;
84 // don't make extra display yellow
85 if (td.parentElement &&
86 td.parentElement.parentElement &&
87 td.parentElement.parentElement.parentElement &&
88 td.parentElement.parentElement.parentElement.parentElement &&
89 td.parentElement.parentElement.parentElement.parentElement.id === "displayExtra")
92 // check if value really changed
93 if (t.oldValue === undefined) {
94 t.oldValue = t.innerText;
98 if (t.innerText === t.oldValue)
100 t.oldValue = t.innerText;
102 td.style.backgroundColor = 'var(--myellow)';
103 td.style.setProperty("-webkit-transition", "", "");
104 td.style.setProperty("transition", "", "");
106 window.setTimeout(() => {
107 td.style.setProperty("-webkit-transition", "background-color 1s", "");
108 td.style.setProperty("transition", "background-color 1s", "");
109 td.style.backgroundColor = "";
113function redirectEq(div, eqName) {
114 // update URL manually
115 let url = window.location.href;
116 if (url.search("&eq=") !== -1) {
117 url = url.slice(0, url.search("&eq="));
118 url += "&eq=" + eqName;
119 if (url !== window.location.href)
120 window.history.replaceState({}, "Equipment", url);
123 // delete lower part of old table
124 let table = document.getElementById(equipment.tid);
135 eqtable_init(div, eqName, true, true);
140function validateData(value, elem) {
141 // avoid double call of validateData
142 // - once by direct call
143 // - once by ODBFinishInlineEdit if focus is lost
144 if (confirmFlag === 1)
149 equipment.table.forEach( e => {
157 dlgConfirm("You have selected " + nSel + " values. Do you want to change all of them to " + value + " ?",
158 setAll, { "value" : value, "elem": elem });
164function setAll(flag, param) {
167 let tr = param.elem.parentNode.parentNode;
169 for (let i=0 ; i<tr.childNodes.length ; i++)
170 if (tr.childNodes[i] === param.elem.parentNode) {
174 equipment.table.forEach(e => {
175 if (index && e.selected) {
176 let m = e.tr.childNodes[index].firstChild;
177 m.setValue(param.value);
184 window.location.href = "?cmd=odb&odb_path=/Equipment/" + equipment.name;
187function selectGroup(t) {
188 for (let g of equipment.group) {
189 let trGroup = document.getElementsByName(g);
191 for (let tr of trGroup)
192 tr.style.display = (g === t.value || t.value === "All") ? "table-row" : "none";
196function enable_row(row, enabled, index) {
197 let disabled = false;
198 if (enabled !== undefined) {
199 if (Array.isArray(enabled)) {
202 } else if (enabled === false)
206 row.disabled = disabled;
209 row.style.backgroundColor = "#B0B0B0";
211 row.style.backgroundColor = "";
213 // go through row elements and disable input and select elements
214 for (let child of row.children) {
215 if (child.childNodes[0] && child.childNodes[0].dataset && child.childNodes[0].dataset.odbEditable !== undefined)
216 child.childNodes[0].dataset.odbEditable = disabled ? "0" : "1";
217 if (child.childNodes[0] && child.childNodes[0].tagName === "SELECT")
218 child.childNodes[0].disabled = disabled;
222function pageSel(page) {
224 let ps = document.getElementsByClassName('pageSelector');
228 // change color of tabs
230 for (let i= 0 ; i<ps.length ; i++) {
231 if (ps[i].children[0].innerText === page) {
233 ps[i].style.backgroundColor = "#8cbdff";
234 ps[i].children[0].style.color = "#f8f8f8";
236 ps[i].style.backgroundColor = "#dddddd";
237 ps[i].children[0].style.color = "#0000ff";
241 // hide/unhide table rows
242 if (page === "All") {
243 for (let i=0 ; i<equipment.table.length ; i++)
244 equipment.table[i].tr.style.display = "";
246 for (let i=0 ; i<equipment.table.length ; i++) {
247 if (Math.floor(i / equipment.channelsPerDevice) === pageIndex)
248 equipment.table[i].tr.style.display = "";
250 equipment.table[i].tr.style.display = "none";
254 // modify extra values
255 let extra = document.getElementById('displayExtra');
257 extra = extra.children[0].children[0].children[0];
258 for (let i=0 ; i<extra.children.length ; i++) {
259 let d = extra.children[i].children[0];
260 if (d.className === "modbselect" || d.className ==="modbvalue") {
262 let p = d.dataset.odbPath;
264 if (page === "All") {
266 p = p.replace(/\[.*?\]/g, "[*]");
271 p = p.replace(/\[.*?\]/g, "[" + pageIndex + "]");
273 p = p + "[" + pageIndex + "]";
276 d.dataset.odbPath = p;
281 equipment.selectedPage = pageIndex;
286function hist(title, str) {
287 mhistory_dialog(title, str);
290function eqtable_init(div, eqName, bButtons, bEqSelect) {
292 if (eqName === undefined)
293 equipment.name = new URLSearchParams(window.location.search).get('eq');
295 equipment.name = eqName;
297 if (equipment.name === undefined || equipment.name === null) {
298 dlgAlert("Please specify equipment name in URL with \"eq=<name>\"");
302 // add equipment table to <div>
303 if (div === undefined) {
304 dlgAlert("Please specify <div> element when calling eqtable_init()");
308 equipment.tid = 'eqTable' + equipment.name.replace(/\s+/g, ''); // remove all spaces from eq
310 let html = '<table id="' + equipment.tid + '" class="mtable" style="visibility: hidden">';
315 ' <td id="tableHeader" class="mtableheader">\n' +
316 ' <select class="mthselect" id="eqSelect" onchange="redirectEq(document.getElementById(\'' + div.id + '\'), this.value)">\n' +
321 html += '<tr><td id="fewarning" width="100%" style="text-align: center; display: none">' +
322 '<span style="font-size: 2em; padding: 5px; display: block; background-color: var(--mred);">' +
323 '<a href="?cmd=Programs">Frontend</a> not running</span>' +
329 <td id="menuButtons" style="border-radius: 12px;">
330 <button class="dlgButton" id="btnSave" title="Save current setting" onclick="saveFilePicker();">Save</button>
331 <button class="dlgButton" id="btnLoad" title="Load setting from file" style="display: none" onclick="loadFilePicker();">Load</button>
332 <button class="dlgButton" id="btnExport" title="Export current setting" onclick="exportFileQuery();">Export</button>
333 <button class="dlgButton" id="btnImport" title="Import current setting" style="display: none" onclick="importFile();">Import</button>
334 <button class="dlgButton" id="btnODB" title="Go to ODB" style="float: right" onclick="goToOdb();">ODB</button>
340 div.innerHTML = html;
342 mjsonrpc_db_get_value('/Equipment/' + equipment.name).then(
344 let eq = rpc.result.data[0];
346 dlgAlert('Equipment \"' + equipment.name + '\" does not exist in ODB',
347 () => window.location.href = ".");
351 if (eq.variables === null) {
352 dlgAlert('No valid \"/Equipment/' + equipment.name + '/Variables\" in ODB',
353 () => window.location.href = ".");
357 let table = document.getElementById(equipment.tid);
360 let gridDisplay = true;
362 equipment.frontend_name = eq.common["frontend name"];
364 if (document.getElementById('eqSelect'))
365 document.getElementById('eqSelect').innerHTML = equipment.name;
366 if (document.getElementById('tableHeader'))
367 document.getElementById('tableHeader').style.width = "300px";
369 // obtain lengths of elements under /Variables
371 for (const e in eq.variables) {
376 let v = eq.variables[e];
377 let len = v.length ? v.length : 1;
389 // overwrite gridDisplay flag from ODB if present
390 if (eq.settings && eq.settings["grid display"] === undefined) {
391 eq.settings["grid display"] = gridDisplay;
394 gridDisplay = eq.settings["grid display"];
401 cpd = eq.settings["channels per device"];
402 if (cpd && cpd > 0) {
404 let tr = table.insertRow();
405 let td = tr.insertCell();
406 td.id = "pageSelector";
407 td.style.padding = "0";
408 td.style.borderBottom = "2px solid #808080";
410 let pageSel = "<table><tr>";
411 pageSel += "<td class=\"pageSelector\"><a href=\"#\" onclick=\"pageSel('All')\">All</a></td>";
412 eq.devNames = eq.settings["device names"];
413 if (eq.devNames !== undefined && !Array.isArray(eq.devNames))
414 eq.devNames = new Array(eq.devNames);
416 if (eq.devNames === undefined) {
418 for (let i = 0; i < nRows / cpd; i++)
419 eq.devNames[i] = String(i);
422 for (let i = 0; i < nRows / cpd; i++) {
423 pageSel += "<td class=\"pageSelector\"><a href=\"#\" onclick=\"pageSel('" + eq.devNames[i] +
424 "')\">" + eq.devNames[i] + "</a></td>";
426 pageSel += "</tr></table>";
427 td.innerHTML = pageSel;
428 equipment.channelsPerDevice = cpd;
431 // extra display at the top
432 if (eq.settings && eq.settings["display extra"]) {
433 let tr = table.insertRow();
434 let td = tr.insertCell();
435 td.id = "displayExtra";
436 td.style.padding = "0";
438 let extraTable = "<table><tr>";
439 let displayExtra = eq.settings["display extra"].split(',').map(item => item.trim());
440 for (const vName of displayExtra) {
441 td.style.height = "22px";
443 let subdir = "Variables";
444 if (eq.variables[vName.toLowerCase()] === undefined)
446 let editable = (eq.settings &&
447 eq.settings.editable && eq.settings.editable.includes(vName));
450 if (eq.variables[vName.toLowerCase()] === undefined) {
451 if (Array.isArray(eq.settings[vName.toLowerCase()]))
452 bool = (typeof eq.settings[vName.toLowerCase()][0] === 'boolean');
454 bool = (typeof eq.settings[vName.toLowerCase()] === 'boolean');
456 if (Array.isArray(eq.variables[vName.toLowerCase()]))
457 bool = (typeof eq.variables[vName.toLowerCase()][0] === 'boolean');
459 bool = (typeof eq.variables[vName.toLowerCase()] === 'boolean');
466 format = eq.settings['format ' + vName.toLowerCase()];
470 unit = eq.settings['unit ' + vName.toLowerCase()];
476 vDiv = "<select class=\"modbselect\" " +
477 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
478 "><option value='false'>No</option>" +
479 "<option value='true'>Yes</option>" +
480 "<option value='...'>...</option>" +
481 "onchange='valueChanged(this);' " +
484 vDiv = "<span class=\"modbvalue\" style=\"border-right: 1px black;\" " +
485 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
486 "data-odb-editable='1' " +
488 "data-format=\"" + format + "\" " +
489 "onchange='valueChanged(this);' " +
492 vDiv = "<span class=\"modbvalue\" " +
493 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
494 "data-odb-editable='1' " +
496 "onchange='valueChanged(this);' " +
501 vDiv = "<span class=\"modbvalue\" " +
502 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
503 "data-format=\"" + format + "\" " +
504 "onchange='valueChanged(this);' " +
507 vDiv = "<span class=\"modbvalue\" " +
508 "data-odb-path=\"/Equipment/" + equipment.name + "/" + subdir + "/" + vName + "\" " +
509 "onchange='valueChanged(this);' " +
516 extraTable += "<td>";
517 extraTable += vName + ": " + vDiv + " ";
518 extraTable += "</td>";
521 extraTable += "</tr></table>";
522 td.innerHTML = extraTable;
525 if (eq.settings && eq.settings.names !== undefined && !Array.isArray(eq.settings.names))
526 eq.settings.names = [eq.settings.names];
529 equipment.enabled = eq.settings.enabled;
531 equipment.enabled = true;
533 let groupDisplay = false;
535 if (eq.settings && eq.settings.names !== undefined)
536 groupDisplay = eq.settings.names.every(e => e.includes('%'));
539 let tr = table.insertRow();
540 let td = tr.insertCell();
541 td.id = "groupSelector";
542 let h = "Group: ";
544 eq.settings.names.forEach(n => {
545 let g = n.substring(0, n.indexOf('%'));
546 if (!groupList.includes(g))
550 h += "<select onchange='selectGroup(this)'>";
551 groupList.forEach(g => {
552 h += "<option value=\"" + g + "\">" + g + "</option>";
554 h += "<option value=\"All\">- All -</option>";
558 equipment.group = groupList;
563 // assemble display list
566 if (eq.settings && eq.settings.display) {
567 display = eq.settings.display.split(',').map(item => item.trim());
570 display.push("Names");
571 let v = Object.entries(eq.variables)
572 .filter(([key, value]) => key.includes('/name'))
573 .map(([key, value]) => value);
574 display = display.concat(v);
577 let tr = table.insertRow();
579 for (const title of display) {
580 let th = document.createElement('th');
581 th.innerHTML = (title === "Names" ? "Name" : title);
582 th.className = "mtableheader mvarheader";
585 if (eq.variables[title.toLowerCase()] !== undefined)
586 subdir = "/Variables/";
587 else if (eq.settings && eq.settings[title.toLowerCase()] !== undefined)
588 subdir = "/Settings/";
590 if (eq.settings && eq.settings['unit ' + title.toLowerCase()]) {
598 let editable = (eq.settings &&
599 eq.settings.editable &&
600 eq.settings.editable.toLowerCase().includes(title.toLowerCase()));
602 // enable load/import if we have editable variables
603 if (editable && bButtons) {
604 document.getElementById('btnLoad').style.display = "inline";
605 document.getElementById('btnImport').style.display = "inline";
608 equipment.vars.push({
610 'editable': editable,
615 if (eq.settings["history buttons"]) {
616 let th = document.createElement('th');
617 th.innerHTML = "HST";
618 th.className = "mtableheader mvarheader";
623 for (let i = 0; i < nRows; i++) {
624 if (equipment.table.length <= i)
625 equipment.table.push({});
627 equipment.table[i].selected = false;
628 equipment.table[i].lastSelected = false;
630 // create new row, install listener and set style
631 let tr = table.insertRow();
635 tr.classList.add("p" + Math.floor(i / cpd));
637 tr.addEventListener('mousedown', mouseEvent);
638 tr.addEventListener('mousemove', mouseEvent);
639 tr.addEventListener('mouseout', mouseEvent);
641 // catch all mouseup events to stop dragging
642 document.addEventListener('mouseup', mouseEvent);
644 tr.style.userSelect = 'none';
647 let group = eq.settings.names[i];
648 group = group.substring(0, group.indexOf('%'));
649 tr.setAttribute('name', group);
650 if (groupList.length > 0 && group !== groupList[0])
651 tr.style.display = "none";
654 equipment.table[i].tr = tr;
656 // iterate over all variables in equipment
657 for (let v of equipment.vars) {
659 let td = tr.insertCell();
660 td.style.textAlign = "right";
662 if (eq.settings && eq.settings.group)
663 if (eq.settings.group[i])
664 td.style.borderTop = "2px solid #808080";
666 if (v.name === "#") {
667 td.innerHTML = i.toString();
668 td.style.width = "10px";
669 } else if (v.name === "Names") {
670 td.style.textAlign = "center";
671 if (eq.settings && eq.settings.names) {
673 td.innerHTML = eq.settings.names[i].substring(eq.settings.names[i].indexOf('%') + 1);
675 td.innerHTML = eq.settings.names[i];
678 let format = eq.settings && eq.settings['format ' + v.name.toLowerCase()];
679 if (format === undefined || format === null)
680 format = "%f2"; // default format
681 else if (Array.isArray(format))
688 if (v.subdir.includes("Variables")) {
689 boolean = typeof eq.variables[v.name.toLowerCase()] === 'boolean' ||
690 (typeof eq.variables[v.name.toLowerCase()] === 'object' &&
691 typeof eq.variables[v.name.toLowerCase()][0] === 'boolean');
692 } else if (v.subdir.includes("Settings")) {
693 boolean = typeof eq.settings[v.name.toLowerCase()] === 'boolean' ||
694 (typeof eq.settings[v.name.toLowerCase()] === 'object' &&
695 typeof eq.settings[v.name.toLowerCase()][0] === 'boolean');
697 if (eq.settings["options " + v.name.toLowerCase()]) {
698 options = eq.settings["options " + v.name.toLowerCase()];
702 td.innerHTML = "<select class=\"modbselect\" " +
703 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
704 "data-validate='validateData'>" +
705 "<option value='false'>No</option>" +
706 "<option value='true'>Yes</option>" +
708 } else if (options.length > 0) {
709 let html = "<select class=\"modbselect\" " +
710 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
711 "data-validate='validateData'>";
713 options.forEach(opt => {
714 html += `<option value="${opt}">${opt}</option>`;
721 td.style.width = "90px";
722 td.style.height = "22px";
723 td.innerHTML = "<div class=\"modbvalue\" " +
724 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
725 "data-odb-editable='1' " +
727 "data-format=\"" + format + "\" " +
728 "data-validate='validateData' " +
729 "onchange='valueChanged(this);' " +
733 td.innerHTML = "<div class=\"modbvalue\" " +
734 "data-odb-path=\"/Equipment/" + equipment.name + v.subdir + v.name + "[" + i + "]\" " +
735 "data-format=\"" + format + "\" " +
736 "onchange='valueChanged(this);' " +
740 if (eq.settings && eq.settings['unit ' + v.name.toLowerCase()]) {
742 let unit = eq.settings['unit ' + v.name.toLowerCase()];
743 if (Array.isArray(unit))
746 td = tr.insertCell();
747 td.style.width = "10px";
749 if (eq.settings && eq.settings.group)
750 if (eq.settings.group[i])
751 td.style.borderTop = "2px solid #808080";
756 td.style.textAlign = 'center';
760 if (eq.settings['history buttons']) {
762 td = tr.insertCell();
763 td.style.width = "10px";
764 td.style.textAlign = 'center';
766 if (eq.settings && eq.settings.group)
767 if (eq.settings.group[i])
768 td.style.borderTop = "2px solid #808080";
771 for (let v of equipment.vars) {
772 let vn = eq.variables[v.name.toLowerCase()];
773 if (vn && (typeof vn === "number" || Array.isArray(vn) && typeof vn[0] === "number")) {
774 if (eq.settings.names && Array.isArray(eq.settings.names))
775 str += equipment.name + ":" + eq.settings.names[i] + " " + v.name + ",";
777 str += equipment.name + ":" + v.name + "[" + i + "],";
781 str = str.slice(0, -1);
783 let title = Array.isArray(eq.settings.names) ? eq.settings.names[i] : v.name;
784 td.innerHTML = "<button style='padding: 0'" +
785 " onclick=\"hist(\'" + title + "\',\'" + str + "\');\">" +
786 " <img src='icons/activity.svg' title='Show graph'>" +
790 document.getElementById(equipment.tid).style.visibility = '';
792 if (cpd && cpd > 0) {
793 enable_row(tr, eq.settings.enabled, Math.floor(i / cpd));
795 if (eq.settings === undefined)
796 enable_row(tr, true, i);
798 if (Array.isArray(eq.settings.enabled))
799 enable_row(tr, eq.settings.enabled[i], i);
801 enable_row(tr, eq.settings.enabled, i);
807 pageSel(eq.devNames[0]);
809 } else { // ------------ non-grid display
813 for (const e in eq.variables) {
814 if (!e.includes('/name'))
817 let vName = eq.variables[e];
818 let vArr = eq.variables[vName.toLowerCase()];
820 if (eq.settings && eq.settings.editable) {
821 if (Array.isArray(eq.settings.editable))
822 editable = eq.settings.editable;
824 editable = eq.settings.editable.toLowerCase().includes(vName.toLowerCase());
827 // skip subdirectories
828 if (typeof vArr === 'object' && !Array.isArray(vArr))
832 if (Array.isArray(editable))
833 flag = editable.includes(true);
835 equipment.vars.push({
840 // enable load/import if we have editable variables
841 if (flag && bButtons) {
842 document.getElementById('btnLoad').style.display = "inline";
843 document.getElementById('btnImport').style.display = "inline";
846 let tr = table.insertRow();
849 let th = document.createElement('th');
850 th.className = "mtableheader mvarheader";
856 if (eq.settings && eq.settings['names ' + vName.toLowerCase()] !== undefined) {
857 let th = document.createElement('th');
858 th.className = "mtableheader mvarheader";
859 th.innerHTML = "Name";
864 th = document.createElement('th');
865 th.innerHTML = vName;
866 th.className = "mtableheader mvarheader";
871 if (eq.settings && eq.settings["history buttons"]) {
872 let th = document.createElement('th');
873 th.innerHTML = "HST";
874 th.className = "mtableheader mvarheader";
880 if (!Array.isArray(vArr))
882 for (let i = 0; i < vArr.length; i++) {
884 equipment.table.push({});
886 equipment.table[n].selected = false;
887 equipment.table[n].lastSelected = false;
889 // create new row, install listener and set style
890 let tr = table.insertRow();
893 tr.addEventListener('mousedown', mouseEvent);
894 tr.addEventListener('mousemove', mouseEvent);
895 tr.addEventListener('mouseout', mouseEvent);
897 // catch all mouseup events to stop dragging
898 document.addEventListener('mouseup', mouseEvent);
900 tr.style.userSelect = 'none';
903 let group = eq.settings.names[i];
904 group = group.substring(0, group.indexOf('%'));
905 tr.setAttribute('name', group);
906 if (groupList.length > 0 && group !== groupList[0])
907 tr.style.display = "none";
910 equipment.table[n].tr = tr;
913 if (eq.settings && eq.settings['names ' + vName.toLowerCase()] !== undefined) {
914 td = tr.insertCell();
915 if (eq.settings && eq.settings.group)
916 if (eq.settings.group[i])
917 td.style.borderTop = "2px solid #808080";
921 td = tr.insertCell();
922 if (eq.settings && eq.settings.group)
923 if (eq.settings.group[i])
924 td.style.borderTop = "2px solid #808080";
926 if (Array.isArray(eq.settings['names ' + vName.toLowerCase()])) {
927 td.innerHTML = eq.settings['names ' + vName.toLowerCase()][i]
929 td.innerHTML = eq.settings['names ' + vName.toLowerCase()];
932 td = tr.insertCell();
934 if (eq.settings && eq.settings.group)
935 if (eq.settings.group[i])
936 td.style.borderTop = "2px solid #808080";
938 td.innerHTML = vName + '[' + i + ']';
943 if (eq.settings && Array.isArray(eq.settings['unit ' + vName.toLowerCase()]))
944 unit = eq.settings['unit ' + vName.toLowerCase()];
945 else if (eq.settings && eq.settings['unit ' + vName.toLowerCase()])
946 unit = new Array(vArr.length).fill(eq.settings['unit ' + vName.toLowerCase()]);
949 td = tr.insertCell();
951 if (eq.settings && eq.settings.group)
952 if (eq.settings.group[i])
953 td.style.borderTop = "2px solid #808080";
957 format = eq.settings['format ' + vName.toLowerCase()];
958 if (format === undefined || format === null)
959 format = new Array(vArr.length).fill("%f2"); // default format
962 if (Array.isArray(flag))
966 td.style.width = "90px";
967 td.style.height = "22px";
968 td.style.textAlign = "right";
969 td.innerHTML = "<div class=\"modbvalue\" " +
970 "data-odb-path=\"/Equipment/" + equipment.name + "/Variables/" + vName + "[" + i + "]\" " +
971 "data-odb-editable='1' " +
973 "data-format=\"" + format[i] + "\" " +
974 "onchange='valueChanged(this);' " +
977 td.style.textAlign = "right";
978 td.innerHTML = "<div class=\"modbvalue\" " +
979 "data-odb-path=\"/Equipment/" + equipment.name + "/Variables/" + vName + "[" + i + "]\" " +
980 "data-format=\"" + format[i] + "\" " +
981 "onchange='valueChanged(this);' " +
985 td = tr.insertCell();
986 td.style.width = "10px";
988 if (eq.settings && eq.settings.group)
989 if (eq.settings.group[i])
990 td.style.borderTop = "2px solid #808080";
993 td.innerHTML = unit[i];
997 if (eq.settings && eq.settings['history buttons']) {
999 td = tr.insertCell();
1000 td.style.width = "10px";
1001 td.style.textAlign = 'center';
1003 if (eq.settings && eq.settings.group)
1004 if (eq.settings.group[i])
1005 td.style.borderTop = "2px solid #808080";
1008 if (eq.settings && eq.settings['names'] !== undefined) {
1009 str = eq.settings['names'][i];
1010 } else if (eq.settings && eq.settings['names ' + vName.toLowerCase()] !== undefined) {
1011 if (Array.isArray(eq.settings['names ' + vName.toLowerCase()])) {
1012 str = eq.settings['names ' + vName.toLowerCase()][i]
1014 str = eq.settings['names ' + vName.toLowerCase()];
1017 str = vName + '[' + i + ']';
1020 td.innerHTML = "<button style='padding: 0'" +
1021 " onclick=\"hist(\'" + equipment.name + ":" + str + "\');\">" +
1022 " <img src='icons/activity.svg' title='Show graph'>" +
1031 document.getElementById(equipment.tid).style.visibility = '';
1034 if (document.getElementById('fewarning'))
1035 document.getElementById('fewarning').colSpan = columns;
1036 if (document.getElementById('tableHeader'))
1037 document.getElementById('tableHeader').colSpan = columns;
1038 if (document.getElementById('menuButtons'))
1039 document.getElementById('menuButtons').colSpan = columns;
1041 document.getElementById('groupSelector').colSpan = columns;
1042 if (document.getElementById('displayExtra'))
1043 document.getElementById('displayExtra').colSpan = columns;
1044 if (document.getElementById('pageSelector'))
1045 document.getElementById('pageSelector').colSpan = columns;
1047 // populate equipment table
1049 mjsonrpc_db_ls(["/Equipment"]).then(
1051 let eqList = rpc.result.data[0];
1052 let sel = document.getElementById('eqSelect');
1053 for (eq in eqList) {
1054 let o = document.createElement('option');
1058 sel.value = equipment.name;
1062 let d = document.getElementById(equipment.tid);
1064 d = d.parentNode.parentNode.parentNode;
1065 if (d.tagName === 'DIV')
1071 document.addEventListener('mousedown', removeSelection);
1076let allowIncDec = false;
1078function saveFilePicker() {
1079 file_picker("equipment", "*.json", saveFile, true, equipment.name, true);
1082function saveFile(filename) {
1083 mjsonrpc_db_copy(['/Equipment/' + equipment.name + '/Variables']).then(rpc => {
1085 if (filename.indexOf('.json') === -1)
1086 filename += '.json';
1089 "/MIDAS version": "2.1",
1090 "/filename": filename,
1091 "/ODB path": "/Equipment/" + equipment.name + "/Variables"
1093 header = JSON.stringify(header, null, ' ');
1094 header = header.substring(0, header.length-2) + ','; // strip trailing '}'
1096 let odbJson = JSON.stringify(rpc.result.data[0], null, ' ');
1097 if (odbJson.indexOf('{') === 0)
1098 odbJson = odbJson.substring(1); // strip leading '{'
1100 odbJson = header + odbJson;
1102 file_save_ascii(filename, odbJson, "Equipment \"" + equipment.name +
1103 "\" saved to file \"" + filename + "\"");
1105 }).catch(error => mjsonrpc_error_alert(error));
1108function exportFileQuery() {
1109 dlgQuery("Enter filename: ", "default.json", exportFile);
1112function exportFile(filename) {
1113 if (filename === false)
1115 mjsonrpc_db_copy(['/Equipment/' + equipment.name + '/Variables']).then(rpc => {
1117 if (filename.indexOf('.json') === -1)
1118 filename += '.json';
1121 "/MIDAS version": "2.1",
1122 "/filename": filename,
1123 "/ODB path": "/Equipment/" + equipment.name + "/Variables"
1125 header = JSON.stringify(header, null, ' ');
1126 header = header.substring(0, header.length-2) + ','; // strip trailing '}'
1128 let odbJson = JSON.stringify(rpc.result.data[0], null, ' ');
1129 if (odbJson.indexOf('{') === 0)
1130 odbJson = odbJson.substring(1); // strip leading '{'
1132 odbJson = header + odbJson;
1134 // use trick from FileSaver.js
1135 let a = document.getElementById('downloadHook');
1137 a = document.createElement("a");
1138 a.style.display = "none";
1139 a.id = "downloadHook";
1140 document.body.appendChild(a);
1143 let blob = new Blob([odbJson], {type: "text/json"});
1144 let url = window.URL.createObjectURL(blob);
1147 a.download = filename;
1149 window.URL.revokeObjectURL(url);
1150 dlgAlert("Equipment \"" + equipment.name +
1151 "\" downloaded to file \"" + filename + "\"");
1153 }).catch(error => mjsonrpc_error_alert(error));
1156function loadFilePicker() {
1157 file_picker("equipment", "*.json", loadFile);
1160function loadFile(filename) {
1161 file_load_ascii(filename, (text) => fileInterprete(filename, text) );
1164async function importFile() {
1165 // Chrome has file picker, others might have not
1168 [fileHandle] = await window.showOpenFilePicker();
1169 const file = await fileHandle.getFile();
1170 const text = await file.text();
1171 fileInterprete(file.name, text);
1173 if (error.name !== 'AbortError') {
1174 // fall-back to old method
1175 if (document.getElementById('dlgFileSelect') === null) {
1176 let fsDiv = document.createElement('div');
1177 fsDiv.id = "dlgFileSelect";
1178 fsDiv.className = "dlgFrame";
1179 fsDiv.innerHTML = dlgFileSelect;
1180 document.body.appendChild(fsDiv);
1182 dlgShow('dlgFileSelect', true);
1187function importFileFromSelector() {
1189 let input = document.getElementById('fileSelector');
1190 let file = input.files[0];
1191 if (file !== undefined) {
1192 let reader = new FileReader();
1193 reader.readAsText(file);
1195 reader.onerror = function () {
1196 dlgAlert('File read error: ' + reader.error);
1199 reader.onload = function () {
1200 fileInterprete(file.name, reader.result);
1205function fileInterprete(filename, text) {
1207 let j = JSON.parse(text);
1209 // check for correct equipment
1210 let path = j["/ODB path"].split('/');
1211 if (path[2] !== equipment.name) {
1212 dlgAlert("File contains settings for equipment \"" + path[2] + "\"<br />"+
1213 "and therefore cannot be loaded into equipment \"" + equipment.name +"\"");
1217 mhttpd_refresh_pause(true);
1219 for (let v of equipment.vars)
1221 modbset("/Equipment/" + equipment.name + "/Variables/" + v.name, j[v.name]);
1223 mhttpd_refresh_pause(false);
1225 window.setTimeout(mhttpd_refresh, 10);
1227 mjsonrpc_cm_msg("Values loaded from file \"" + filename + "\"");
1229 dlgAlert("File \"" + filename + "\" is not a valid JSON file:<br /><br />" + error);
1233function renderSelection() {
1234 // change color according to selection
1235 for (let i=0 ; i<equipment.table.length ; i++) {
1236 if (equipment.table[i].selected) {
1237 equipment.table[i].tr.style.backgroundColor = '#004CBD';
1238 equipment.table[i].tr.style.color = '#FFFFFF';
1241 if (equipment.table[i].tr.getElementsByTagName('a').length > 0)
1242 equipment.table[i].tr.getElementsByTagName('a')[0].style.color = '#FFFFFF';
1247 if (equipment.channelsPerDevice)
1248 index = Math.floor(i / equipment.channelsPerDevice);
1249 enable_row(equipment.table[i].tr, equipment.enabled, index);
1251 equipment.table[i].tr.style.color = ''; // text is black again
1254 let a = equipment.table[i].tr.getElementsByTagName('a');
1263let mouseDragged = false;
1265// function gets called from document
1266function removeSelection(e) {
1267 // don't de-select if we click on "->" or on drop-down box
1268 if (e.target.tagName === 'BUTTON' || e.target.tagName === 'SELECT')
1271 for (let i = 0; i < equipment.table.length; i++) {
1272 equipment.table[i].selected = false;
1273 equipment.table[i].lastSelected = false;
1278function getSelectedElements() {
1280 for (let i = 0; i < equipment.table.length; i++)
1281 if (equipment.table[i].selected)
1287function mouseEvent(e) {
1289 if (e.type === 'mouseup')
1290 mouseDragged = false;
1292 // keep off secondary mouse buttons
1296 // keep off drop down boxes
1297 if (e.target.tagName === 'SELECT')
1301 e.stopPropagation();
1305 for (index = 0; index < equipment.table.length; index++)
1306 if (equipment.table[index].tr === e.target ||
1307 equipment.table[index].tr.contains(e.target))
1309 if (index === equipment.table.length)
1312 // ignore clicks to <button>'a and <a>'s
1313 if (e.target.tagName === 'A' || e.target.tagName === 'BUTTON' || e.target.tagName === 'IMG')
1316 if (e.type === 'mousedown') {
1317 mouseDragged = true;
1321 // search last selected element
1323 for (i1 = 0 ; i1 < equipment.table.length ; i1++)
1324 if (equipment.table[i1].lastSelected)
1326 if (i1 === equipment.table.length)
1327 i1 = 0; // none selected, so use first one
1330 [i1, index] = [index, i1];
1332 for (let i=i1 ; i<= index ; i++)
1333 if (!equipment.table[i].tr.disabled)
1334 equipment.table[i].selected = true;
1336 } else if (e.metaKey || e.ctrlKey) {
1338 console.log("index = " + index);
1340 // just toggle current selection
1341 if (!equipment.table[index].tr.disabled)
1342 equipment.table[index].selected = !equipment.table[index].selected;
1344 // remember which row was last selected
1345 for (let i=0 ; i<equipment.table.length ; i++)
1346 equipment.table[i].lastSelected = false;
1347 if (equipment.table[index].selected)
1348 equipment.table[index].lastSelected = true;
1351 // no key pressed -> un-select all but current
1352 for (let i = 0; i < equipment.table.length; i++) {
1353 equipment.table[i].selected = false;
1354 equipment.table[i].lastSelected = false;
1357 if (!equipment.table[index].tr.disabled) {
1358 equipment.table[index].selected = true;
1359 equipment.table[index].lastSelected = true;
1366 if (e.type === 'mousemove' && mouseDragged) {
1368 // don't do dragging if shift or ctrl key pressed
1369 if (!e.shiftKey && !e.metaKey && !e.ctrlKey) {
1371 for (let i = 0; i < equipment.table.length; i++)
1372 equipment.table[i].selected = false;
1376 for (i2 = 0; i2 < equipment.table.length; i2++)
1377 if (equipment.table[i2].tr === e.target ||
1378 equipment.table[i2].tr.contains(e.target))
1381 if (i2 < equipment.table.length) {
1382 // search last selected element
1384 for (i1 = 0; i1 < equipment.table.length; i1++)
1385 if (equipment.table[i1].lastSelected)
1387 if (i1 === equipment.table.length)
1388 i1 = 0; // none selected, so use first one
1391 [i1, i2] = [i2, i1];
1393 for (let i = i1; i <= i2; i++)
1394 if (!equipment.table[i].tr.disabled)
1395 equipment.table[i].selected = true;
1403function dlgEquipment(eqName, bButtons = false, bEqSelect = false) {
1405 // check if equipment exists in ODB
1406 mjsonrpc_db_ls(['/Equipment/' + eqName]).then(
1408 let eq = rpc.result.data[0];
1410 dlgAlert('dlgEquipment: Equipment \"' + eqName + '\" does not exist in ODB');
1414 if (eq.variables === null) {
1415 dlgAlert('dlgEquipment: No valid \"/Equipment/' + eqName + '/Variables\" in ODB');
1419 let d = document.getElementById('dlgEqTitle');
1421 // If dialog is already there, reuse it.
1423 d = document.createElement("div");
1424 d.className = "dlgFrame";
1425 d.style.zIndex = "30";
1426 d.shouldDestroy = true;
1428 d.innerHTML = "<div class=\"dlgTitlebar\" id=\"dlgEqTitle\">" + eqName + "</div>" +
1429 "<div class=\"dlgPanel\" style=\"padding: 4px;\">" + "<div id=\"dlgEquipment\">" + "</div></div>";
1430 document.body.appendChild(d);
1434 eqtable_init(document.getElementById('dlgEquipment'), eqName, bButtons, bEqSelect);
1437 let eqSelect = document.getElementById('eqSelect');
1438 eqSelect.onchange = (event) => {
1439 redirectEq(document.getElementById('dlgEquipment'), event.target.value);