From 880edfc6043c625eb1d033789f17378f29cc741e Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 9 Sep 2024 17:50:17 +0200 Subject: [PATCH 01/25] Nok status after 6 hours of no communication --- config | 6 +- databases/nodes.table | 33 +- flow/cmd_manager.js | 841 +++++++++++++++------------------ flow/helper/DataToTbHandler.js | 7 +- 4 files changed, 381 insertions(+), 506 deletions(-) diff --git a/config b/config index b10cd71..f3a7be7 100644 --- a/config +++ b/config @@ -6,7 +6,7 @@ package#flow (Object) : { url: '/' } table.relays : line:number|tbname:string|contactor:number|profile:string -table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean -table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number -table.pins : pin:string|type:string|line:number +table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number +table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number +table.pins : pin:number|type:string|line:number table.notifications : key:string|weight:string|sk:string|en:string diff --git a/databases/nodes.table b/databases/nodes.table index 57331db..0efaed3 100644 --- a/databases/nodes.table +++ b/databases/nodes.table @@ -1,31 +1,2 @@ -node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean -+|3522|RO8rjaBDy21qPQJzW7oD96ApK3xmNleVZg9Ed4Gw|1||1|1|........... -+|4018|3JjOWdylwgNLzxVab7NEznkZ2vG64rq8PEB5QmDo|1||1|1|........... -+|4019|Z5KyJe9nEg1QNbWlX0wWRB0oDjBLdqzR83VGv624|1||1|1|........... -+|4154|1JMYvnx2RzKEo4aWQ7DmN5AL8yZV3m9NBePXbrdj|1||1|0|........... -+|3907|PjLblDgRBO6WQqnxmkJ59r0Jv3ewZN4p5a89yKdY|1||1|1|........... -+|4148|dz4ojlpP85JMgDLZWkQOoGAaKYqQexEr62GXRV1y|1||1|1|........... -+|4153|d5xjWYMwEJon6rLlK7yBYmAqgV4DaOeNB9ZX3Gzb|1||1|1|........... -+|3938|gRoJEyXVx4qD9er287LP1v7wBzGldaPjLWQKm3Mv|1||1|1|........... -+|3802|K94XLav1glVRnyQ6r01BNzkme3YJwBxM5oOzdP2j|1||1|1|........... -+|4015|d9x2V5LGYBzXp4mMRAOBDj7PloaqJwnQj6DgrNe3|1||1|0|........... -+|3929|B5EoxeMVp4zwr8nqW0GjDpARjvD1PNamOGbLg63Z|1||1|1|........... -+|3946|aw4eELG2DlPMdn1JW0B1DnAqQXOZRN3xB5yp8VKr|1||1|1|........... -+|4014|ZmRwd93QL4gaezxEbAxW5971prn2XjlPvGyqJ6BO|1||1|1|........... -+|4155|eod9aRWLVl34Gx1Dn7VoaaA2rz6qjgmpEXwQJN5Z|1||1|1|........... -+|4149|3a5oqJN1bgnx4Ol9dk86NBAByE6jQ8mKDWMpGrLV|1||1|1|........... -+|3642|EjgWGnXaLy9opPOz20n694086BlYM3w1deVQvbKr|1||1|1|........... -+|3636|wvKJdZML6mXP4DzWBAXWNW7jxNloa5g23Ve9Y1ry|1||1|1|........... -+|3991|Nzp2OoJlqn6r1ZgvdA3GWdAabBwP5G4eE3RQmyxD|1||1|1|........... -+|3994|PLBJzmK1r3Gynd6OW0gGdM0e5wV4vx9bDEqNgYR8|1||1|1|........... -+|3990|52dD6ZlV1QaOpRBmbAqKZgkKnGzWMLj4eJq38Pgo|1||1|1|........... -+|3967|rDbQ84xzwgdqEoPm3kbJw3k9anOZY1RXyBv2LVM6|1||1|1|........... -+|3977|E6Kg9oDnLWyzPRMva7vrwa7Jxp4VG58qO2w1lZYe|1||1|1|........... -+|3757|roKgWqY95V3mXMRzyAjm8D7bLjexpJPvaGDBw826|1||1|1|........... -+|3633|nJL5lPMwBx23YpqRe0rlKV7damXvWVbOrD4gNzy8|1||1|1|........... -+|3744|ZmRwd93QL4gaezxEbAxW5O71prn2XjlPvGyqJ6BO|1||1|1|........... -+|4023|eod9aRWLVl34Gx1Dn7VoaMA2rz6qjgmpEXwQJN5Z|1||1|1|........... -+|3720|3a5oqJN1bgnx4Ol9dk86NZAByE6jQ8mKDWMpGrLV|1||1|1|........... -+|3734|EjgWGnXaLy9opPOz20n69V086BlYM3w1deVQvbKr|1||1|1|........... -+|3741|wvKJdZML6mXP4DzWBAXWN17jxNloa5g23Ve9Y1ry|1||1|1|........... -+|3721|Nzp2OoJlqn6r1ZgvdA3GWKAabBwP5G4eE3RQmyxD|1||0|0|........... +node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number ++|638|rDbQ84xzwgdqEoPm3kbJQWk9anOZY1RXyBv2LVM6|3|{"intervals":[{"cct":3000,"value":20,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"05:30","start_time":"20:00"},{"cct":3000,"value":20,"end_time":"13:00","start_time":"05:30"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":-20,"dusk_astro_clock_offset":20,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|0|1725885127396|............................................................................................................................................................................................................................................................|................. diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 48b4987..734bc3d 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -7,7 +7,7 @@ exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; //blue - send message to relays -exports.input = true; +exports.input = 2; exports.author = 'Daniel Segeš'; exports.icon = 'cloud-upload'; //exports.npm = ['serialport' , 'child_process']; @@ -84,6 +84,7 @@ const PRIORITY_TYPES = { let tasks = []; let interval = null;//timeout for procesing tasks +let customTasksInterval = null; let refFlowdata = null;//holds reference to httprequest flowdata let refFlowdataObj = {}; @@ -139,6 +140,8 @@ let nodesData = {};//key is node, value data from db let cmdCounter = {};//key is node, value is counter let cmdNOKNodeCounter = {};//key is node, value is counter +let testTbName = "deleteAfterTesting" //for status testing purposes; + //END OF VARIABLE SETTINGS //-------------------------------- @@ -196,7 +199,7 @@ function getParams(priority) params.rw = 0;//0: read, 1: write //other values - //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" "edge_date_time" "number_of_luminaires" + //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" //params.tbname = tbname; params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed @@ -230,7 +233,7 @@ async function loadSettings() FLOW.OMS_temperature_adress = responseSettings[0]["temperature_adress"]; FLOW.OMS_controller_type = responseSettings[0]["controller_type"]; FLOW.OMS_serial_port = responseSettings[0]["serial_port"]; - + FLOW.OMS_node_status_nok_time = responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000; // hour * minutes * seconds //logger.debug('settings', responseSettings[0]); initNotifications(); @@ -277,8 +280,8 @@ function processNodeProfile(node) logger.debug("processNodeProfile: start - set profile for ", node, profile); let nodeProfile; - try{ - nodeProfile = JSON.parse( profile ); + try { + nodeProfile = JSON.parse(profile); if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined"); } catch (error) { logger.debug("Error parsing node profile", error); @@ -289,10 +292,6 @@ function processNodeProfile(node) let timestamp = PRIORITY_TYPES.node_cmd; removeTask({type: "set_node_profile", address: node}); - cmdNOKNodeCounter[node] = 0; - - //co ked sa prave spracovava? - //if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false; if(nodeProfile === undefined) { @@ -325,10 +324,6 @@ function processNodeProfile(node) else { let tasksProfile = []; - //cmdCounter[node] = tasksProfile.length; - //tasks.push(tasksProfile); - - //let timestamp = PRIORITY_TYPES.node_cmd; //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu let params = getParams(PRIORITY_TYPES.node_cmd); @@ -565,8 +560,6 @@ function processNodeProfile(node) params.register = 8; params.recipient = 1; params.rw = 1;//write - - //Time schedule settings let bits = []; @@ -681,8 +674,6 @@ function removeTask(obj) } - - exports.install = function(instance) { let now = new Date(); @@ -708,7 +699,6 @@ exports.install = function(instance) { process.on('uncaughtException', function (err) { - //TODO send to service errLogger.error('uncaughtException:', err.message) @@ -780,24 +770,19 @@ exports.install = function(instance) { let params = {}; - let recipient = 2;//2 broadcast, address = 0 - let address = 0;//0 - if(recipient === 2) - { - address = 0xffffffff;//Broadcast - } - var d = new Date(); let hours = d.getHours(); let minutes = d.getMinutes(); let seconds = d.getSeconds(); - params.address = address;//broadcast + let time = d.getTime(); // time in ms + + params.address = 0xffffffff;//Broadcast params.byte1 = hours;//h params.byte2 = minutes;//m params.byte3 = seconds;//s params.byte4 = 0; - params.recipient = recipient; + params.recipient = 2;//2 broadcast, address = 0 params.register = 87;//Actual time params.rw = 1;//write @@ -823,6 +808,26 @@ exports.install = function(instance) { { let tbname = nodesData[k].tbname; let node = nodesData[k].node; + let status = "NOK"; + + // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. + if(nodesData[k].status) { + status = "OK"; + nodesData[k].time_of_last_communication = time; + } + + let dataToTb = { + [tbname]: [ + { + ts: time, + values: { + status: status + } + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); //prud, vykon - current, input power pre liniu pre vsetky nody @@ -890,7 +895,7 @@ exports.install = function(instance) { values["dimming"] = 0;//brightness values["power"] = 0;//výkon values["current"] = 0;//prúd - values["status"] = "OFFLINE";//prúd + values["status"] = "OFFLINE"; for (let k in nodesData) { @@ -903,23 +908,18 @@ exports.install = function(instance) { let dataToTb = { [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); + { + "ts": Date.now(), + "values": values + } + ] + } + + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); } } - //report OFFLINE for line - //relaysData[line].tbname; - - //values = {}; - //values["status"] = "OFFLINE";//prúd } @@ -992,7 +992,7 @@ exports.install = function(instance) { message = "NOK"; } - return {message: message, type: type, error: error}; + return {message, type, error}; } @@ -1267,6 +1267,7 @@ exports.install = function(instance) { params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "Broadcast-duskTime"; @@ -1301,6 +1302,7 @@ exports.install = function(instance) { params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "Broadcast-dawnTime"; @@ -1333,6 +1335,7 @@ exports.install = function(instance) { params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; @@ -1392,7 +1395,7 @@ exports.install = function(instance) { if(k == 698) params.debug = true; } */ - + tasks.push(params); } @@ -1400,7 +1403,6 @@ exports.install = function(instance) { } - //niektore ulohy sa vygeneruju iba 1x pri starte!!! if(!init) return; @@ -1411,12 +1413,14 @@ exports.install = function(instance) { //tak treba vyreportovať string "NOK". { let params = getParams(PRIORITY_TYPES.fw_detection); - params.type = "cmd"; + params.type = "cmd-master"; params.register = 4; params.address = 0; let timestampStart = PRIORITY_TYPES.fw_detection; params.timestamp = timestampStart; + + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = 5; params.tbname = FLOW.OMS_edgeName; params.info = "Master node FW verzia"; @@ -1441,36 +1445,6 @@ exports.install = function(instance) { tasks.push(params); } - { - //edge_date_time - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "edge_date_time"; - - let timestampStart = PRIORITY_TYPES.node_cmd; - params.timestamp = timestampStart; - params.addMinutesToTimestamp = 1; - params.tbname = FLOW.OMS_edgeName; - params.info = "reportovanie aktuálneho času na LM - EDGE-Date Time"; - //logger.debug("BUILD Master node FW verzia"); - tasks.push(params); - } - - { - //edge_date_time - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "number_of_luminaires"; - - let timestampStart = PRIORITY_TYPES.node_cmd + 1; - params.timestamp = timestampStart; - params.addMinutesToTimestamp = 1; - params.tbname = FLOW.OMS_edgeName; - params.info = "reportovanie number_of_luminaires"; - - tasks.push(params); - } - monitor.info("tasks created:", tasks.length); } @@ -1559,8 +1533,14 @@ exports.install = function(instance) { } - - async function upateNodeStatus(node, status) + /** + * function updates status and time_of_last_communication of node in the dbNodes + * it only updates if conditions are met + * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) + * if newStatus of node is always receiving false, and it is already for more than FLOW.OMS_node_status_nok_time value, we update status to "NOK" in tb + * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). + */ + function updateNodeStatus(node, newStatus) { //MASTER if(node == 0) return; @@ -1568,24 +1548,72 @@ exports.install = function(instance) { let nodeObj = nodesData[node]; if(nodeObj == undefined) return; - if(status) - { - cmdNOKNodeCounter[node] = 0; - } - else cmdNOKNodeCounter[node]++; + let nodeCurrentStatus = nodeObj.status; + const now = Date.now(); - if(nodeObj.status !== status) + if(newStatus == false && nodeCurrentStatus == false) { + if(node == 638 || node == 637) console.log("false, false, return", node, now) + return true; + } + + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - 600000){ + + if(node == 638 || node == 637) console.log("true true, return", node, now); + return; + } + + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - 600000) { - await dbNodes.modify({ status: status }).where("node", node).make(function(builder) { + dbNodes.modify({ time_of_last_communication: now}).where("node", node).make(function(builder) { builder.callback(function(err, response) { - if(err == null) nodesData[node].status = status; + if(err == null) { + nodeObj.time_of_last_communication = now; + + if(node == 638 || node == 637) console.log('zapisane do db => status true & true', node, now) + } }); }); + return; + } + + if(newStatus == false && nodeCurrentStatus == true) + { + if(nodeObj.time_of_last_communication + FLOW.OMS_node_status_nok_time > now) { + if(node == 638 || node == 637) console.log('false true, return', node, now); + return; + } + else { + dbNodes.modify({ status: newStatus}).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(err == null) { + nodeObj.status = newStatus; + + if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) + } + }); + }); + return true; + } + } + + if(newStatus == true && nodeCurrentStatus == false) + { + dbNodes.modify({ status: newStatus, time_of_last_communication: now}).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(err == null) { + nodeObj.status = newStatus; + nodeObj.time_of_last_communication = now; + + if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) + } + }); + }); + return; } } - async function runTasks() { + async function runTasks(){ clearInterval(interval); @@ -1676,12 +1704,12 @@ exports.install = function(instance) { let type = params.type; let tbname = params.tbname; - let nodeKey = params.address; + let nodeAddress = params.address; let line = null; //rpc related - if(nodesData[nodeKey] !== undefined) line = nodesData[nodeKey].line; + if(nodesData[nodeAddress] !== undefined) line = nodesData[nodeAddress].line; if(params.line !== undefined) line = params.line; let repeatTask = false; @@ -1689,7 +1717,7 @@ exports.install = function(instance) { if(repeatTask) { - if(type === "cmd") + if(type === "cmd" || type === "cmd-master") { //set next start time automatically tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; @@ -1700,58 +1728,6 @@ exports.install = function(instance) { tasks.shift(); } - //custom tasks - if(type == "number_of_luminaires") - { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - - //treba reportovat node status - { - //number_of_luminaires - //number_of_ok_luminaires - //number_of_nok_luminaires - - let keys = Object.keys(nodesData); - - let number_of_luminaires = keys.length; - let number_of_ok_luminaires = 0; - let number_of_nok_luminaires = 0; - - for(let i = 0; i < keys.length; i++) - { - let key = keys[i]; - let nodeObj = nodesData[key]; - if(nodeObj.tbname == undefined) continue; - - if(nodeObj.status) number_of_ok_luminaires++; - else number_of_nok_luminaires++; - } - - let values = { - number_of_luminaires: number_of_luminaires, - number_of_ok_luminaires: number_of_ok_luminaires, - number_of_nok_luminaires: number_of_nok_luminaires - }; - - let dataToTb = { - [FLOW.OMS_edgeName]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - - interval = setInterval(runTasks, SHORT_INTERVAL); - - return; - } - } - - //kontrola nespracovanych profilov nodov if(type == "process_profiles") { @@ -1791,27 +1767,6 @@ exports.install = function(instance) { return; } - if(type == "edge_date_time") - { - const ts = Date.now(); - - let values = {"edge_date_time": ts}; - - let dataToTb = { - [tbname]: [ - { - "ts": ts, - "values": values - } - ] - } - - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - - instance.send(SEND_TO.tb, dataToTb); - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } //relay if(type == "relay") @@ -1912,6 +1867,8 @@ exports.install = function(instance) { disconnectedReport[tbname] = false; + const register = params.register; + //high_priority if(!FLOW.OMS_masterNodeIsResponding) { @@ -1919,11 +1876,9 @@ exports.install = function(instance) { errorHandler.sendMessageToService("Master node is not responding"); let stop = true; - if(params.type == "cmd-terminal") stop = false; //fw version - register == 4 - if(params.type == "cmd" && params.register == 4 && params.address == 0) stop = false; - + if(type == "cmd-terminal" || register == 4) stop = false; if(stop) { interval = setInterval(runTasks, LONG_INTERVAL); @@ -1938,7 +1893,7 @@ exports.install = function(instance) { } if(line == 0) relayStatus = 0; - if(params.type == "cmd-terminal") relayStatus = 1; + if(type == "cmd-terminal") relayStatus = 1; //check if rotary_switch_state == "Off" @@ -1971,7 +1926,7 @@ exports.install = function(instance) { //RE-CALCULATE VALUES //set actual time for broadcast - if(params.register == 87 && params.recipient === 2) + if(register == 87 && params.recipient === 2) { var d = new Date(); let hours = d.getHours(); @@ -1986,10 +1941,10 @@ exports.install = function(instance) { //SET DUSK/DAWN FOR BROADCAST //Time of dusk - if(params.register == 6 && params.recipient === 2) + if(register == 6 && params.recipient === 2) { - if(params.type != "cmd-terminal") + if(type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); let dusk_hours = sunCalcResult["dusk_hours"]; @@ -2007,9 +1962,9 @@ exports.install = function(instance) { } //Time of dawn - if(params.register == 7 && params.recipient === 2) + if(register == 7 && params.recipient === 2) { - if(params.type != "cmd-terminal") + if(type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); let dawn_hours = sunCalcResult["dawn_hours"]; @@ -2028,17 +1983,20 @@ exports.install = function(instance) { } //----------------------- - - let register = params.register; - instance.send(SEND_TO.debug, "address: " + params.address + " register:" + params.register + "type: " + params.type); + instance.send(SEND_TO.debug, "address: " + nodeAddress + " register:" + register + "type: " + type); var startTime, endTime; startTime = new Date(); - let resp = com_generic(params.address, params.recipient, params.rw, params.register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); + let saveToTb = true; + if(tbname == null || tbname == undefined || tbname == "") saveToTb = false; + let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) + + let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); let readBytes = 11; let timeout = 5000; + // await keyword is important, otherwise incorrect data is returned! await writeData(rsPort, resp, readBytes, timeout).then(function (data) { endTime = new Date(); @@ -2051,57 +2009,59 @@ exports.install = function(instance) { let result = detectIfResponseIsValid(bytes); - let message = result.message; - let type = result.type; + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let message = result.message; // OK, NOK + let message_type = result.type; let error = result.error; - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - + //testing purpose node 638, tbname rDbQ84xzwgdqEoPm3kbJQWk9anOZY1RXyBv2LVM6 + if(tbname == testTbName) { + message = "NOK"; + message_type = "ERROR"; + error = "type is: ERROR"; + } + if(params.debug != "generated cmd") { - //debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug, params); + //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug, params); } - if(params.hasOwnProperty("debug")) - { - if(params.debug) - { - console.log("detected response:", result); + // if(params.hasOwnProperty("debug")) + // { + // if(params.debug) + // { + // console.log("detected response:", result); - logger.debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug, params, result); - } - } + // logger.debug("writeData: done " + message_typetype + " duration: " + timeDiff + " type: " + params.debug, params, result); + // } + // } - //debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug); - //debug("writeData done", type, "duration", timeDiff, "type", params.debug, result); + //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug); + //debug("writeData done", message_type, "duration", timeDiff, "message_type", params.debug, result); - let tbname = params.tbname; - - let saveToTb = true; - if(tbname == null || tbname == undefined || tbname == "") saveToTb = false; - //-- + let values = {}; //CMD FINISHED if(message == "OK") { - upateNodeStatus(params.address, true); + updateNodeStatus(nodeAddress, true); //write - if(params.type == "set_node_profile") + if(type == "set_node_profile") { - let result = cmdCounterResolve(params.address); + let result = cmdCounterResolve(nodeAddress); if(result == 0) { - dbNodes.modify({ processed: true }).where("node", params.address).make(function(builder) { + dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { builder.callback(function(err, response) { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: params.address}, "", SEND_TO.tb, instance ); + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); - logger.debug( "--> profil úspešne odoslaný na node č. " + params.address); - nodesData[params.address].processed = true; + logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); + nodesData[nodeAddress].processed = true; }); }); @@ -2109,22 +2069,25 @@ exports.install = function(instance) { } //parse read response - let values = {}; - if(params.rw == 0) { - values = processResponse(register, dataBytes);//read - } - if(params.rw == 1) - { //write command - //set command dimming - if(params.register == 1) values = {"comm_status": message}; + if(params.rw == 0) + { + values = processResponse(register, dataBytes); //read } - if(params.register == 0) values["status"] = message; + if(itIsNodeCommand) + { + values.comm_status = "OK"; + values.status = "OK"; + } - //fw version - register == 4 - if(params.register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version; + //master node + if(nodeAddress == 0 && register == 4) + { + values.status = "OK"; + values["edge_fw_version"] = FLOW.OMS_edge_fw_version; + } - if(params.address == 0) + if(nodeAddress == 0) { //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", SEND_TO.tb, instance, "rvo_status" ); //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); @@ -2133,7 +2096,7 @@ exports.install = function(instance) { } //odoslanie príkazu z terminálu - dáta - if(params.type == "cmd-terminal") + if(type == "cmd-terminal") { //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.DEBUG, "odoslanie príkazu z terminálu", params, SEND_TO.tb, instance, null ); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); @@ -2141,7 +2104,7 @@ exports.install = function(instance) { if(params.debug) { - logger.debug("saveToTb", saveToTb, tbname, values); + //logger.debug("saveToTb", saveToTb, tbname, values); } if(saveToTb) @@ -2157,199 +2120,40 @@ exports.install = function(instance) { //instance.send(SEND_TO.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); - } else { - - if(params.type == "cmd-terminal") + if(type == "cmd-terminal") { - if(params.refFlowdataKey != undefined) - { - - logger.debug("cmd-terminal SUCCESS"); - logger.debug(currentTask); - - //make http response - let responseObj = {}; - responseObj["type"] = "SUCESS"; - responseObj["bytes"] = data; - - //params.refFlowdata.data = responseObj; - //instance.send(SEND_TO.http_response, params.refFlowdata); - - let refFlowdata = refFlowdataObj[ params.refFlowdataKey ]; - refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, refFlowdata); - - } - else - { - console.log("params.refFlowdataKey is undefined", params); - } + terminalCommandResponse(params, "SUCCESS", data); } - } } else { - upateNodeStatus(params.address, false); - - if(params.refFlowdataKey != undefined) - { - - logger.debug("cmd-terminal FAILED"); - logger.debug(currentTask); - - //make http response - let responseObj = {}; - responseObj["type"] = "ERROR"; - responseObj["bytes"] = data; - - //params.refFlowdata.data = responseObj; - //instance.send(SEND_TO.http_response, params.refFlowdata); - - let refFlowdata = refFlowdataObj[ params.refFlowdataKey ]; - if(refFlowdata !== undefined) - { - refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, refFlowdata); - } - - - } - - /* - if(params.type == "cmd-terminal") - { - if(params.refFlowdata != undefined) - { - - logger.debug("cmd-terminal FAILED"); - logger.debug(currentTask); - - //make http response - let responseObj = {}; - responseObj["type"] = "ERROR"; - responseObj["bytes"] = data; - - params.refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, params.refFlowdata); + terminalCommandResponse(params, "ERROR", data) + handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); - } - } - */ - - if(params.address == 0) - { - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", SEND_TO.tb, instance, "rvo_status"); - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); - logger.debug("master_node_is_not_responding", params); - FLOW.OMS_masterNodeIsResponding = false; - } - - if(params.type == "set_node_profile") - { - delete cmdCounter[params.address]; - let tbname = nodesData[ params.address ].tbname; - - logger.debug( "profil nebol úspešne odoslaný na node č. ", params, result, resp); - - //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "profil nebol úspešne odoslaný na node č. " + params.address, "", SEND_TO.tb, instance, null ); - sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", SEND_TO.tb, instance ); - } - - //is it node? - if(nodesData.hasOwnProperty(params.address)) - { - if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false; - } - - //Master node version - //if(params.register == 4 && saveToTb) - if(saveToTb) - { - let values = { - "status": "NOK" - }; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - } - - //instance.send(SEND_TO.debug, result); - if(params.hasOwnProperty("debug")) { if(params.debug) { - logger.debug("writeData err: ", error, result, params); + //logger.debug("writeData err: ", error, result, params); + logger.debug("writeData err: ", tbname, nodeAddress, register, values); } } //logger.debug(error, result, params); } - - }).catch(function (reason) { + }).catch(function(reason) { console.log("writeData catch exception", reason); - logger.debug(currentTask); + instance.send(SEND_TO.debug, reason); - if(params.refFlowdataKey != undefined) - { - - logger.debug("catch: cmd-terminal FAILED"); - logger.debug(currentTask); - - //make http response - let responseObj = {}; - responseObj["type"] = "ERROR";// - responseObj["message"] = "ERROR WRITE FAILED: " + reason;// - - //params.refFlowdata.data = responseObj; - //instance.send(SEND_TO.http_response, params.refFlowdata); - - let refFlowdata = refFlowdataObj[ params.refFlowdataKey ]; - if(refFlowdata !== undefined) - { - refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, refFlowdata); - } - - - } - /* - if(params.type == "cmd-terminal") - { - if(params.refFlowdata != undefined) - { - - logger.debug("cmd-terminal FAILED"); - logger.debug(currentTask); - - //make http response - let responseObj = {}; - responseObj["type"] = "ERROR WRITE FAILED: " + reason; - //responseObj["bytes"] = data; - - params.refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, params.refFlowdata); - - //refFlowdata = undefined; - } - } - */ + terminalCommandResponse(params, "FAILURE", null, reason); + handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); if(params.hasOwnProperty("debug")) { @@ -2359,93 +2163,16 @@ exports.install = function(instance) { } } - upateNodeStatus(params.address, false); - - let tbname = params.tbname; - - let saveToTb = true; - if(tbname == null || tbname == undefined || tbname == "") saveToTb = false; - - if(params.address == 0) - { - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", SEND_TO.tb, instance, "rvo_status"); - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); - logger.debug("master_node_is_not_responding", params); - - FLOW.OMS_masterNodeIsResponding = false; - } - - if(params.type == "set_node_profile") - { - delete cmdCounter[params.address]; - let tbname = nodesData[ params.address ].tbname; - - logger.debug( "profil nebol úspešne odoslaný na node č. ", params, resp); - - //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "odosielanie profilu na node č. " + params.address + " zlyhalo", "", SEND_TO.tb, instance, null ); - sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", SEND_TO.tb, instance ); - } - - //is it node? - if(nodesData.hasOwnProperty(params.address)) - { - if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false; - } - - //Master node version - if(params.register == 4 && saveToTb) - { - let values = { - "status": "NOK", - "master_node_version": "NOK" - }; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - - FLOW.OMS_masterNodeIsResponding = false; - } - //treba? - /* - else if(saveToTb) - { - let values = { - "comm_status": "no_comm" - }; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - instance.send(SEND_TO.tb, dataToTb); - } - */ - - instance.send(SEND_TO.debug, reason); }); } else { - if(currentTask.debug) - { - //currentTask.timestamp <= currentTimestamp - logger.debug("currentTask is not processed - task is in the future", currentTask); - } + // if(currentTask.debug) + // { + // //currentTask.timestamp <= currentTimestamp + // logger.debug("currentTask is not processed - task is in the future", currentTask); + // } interval = setInterval(runTasks, LONG_INTERVAL); return; @@ -2454,7 +2181,178 @@ exports.install = function(instance) { //console.log("----->runTasks - setInterval", new Date()); interval = setInterval(runTasks, SHORT_INTERVAL); } + + + function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb){ + + let node = params.address; + let register = params.register; + let type = params.type; + let tbName = params.tbname; + tbName && (tbName = nodesData[node].tbname); + + let values = {}; + + console.log(message); + let updateStatus = updateNodeStatus(node, false); + + if(node == 0) + { + //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", SEND_TO.tb, instance, "rvo_status"); + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); + logger.debug("master_node_is_not_responding", params); + FLOW.OMS_masterNodeIsResponding = false; + } + + if(type == "set_node_profile") + { + delete cmdCounter[node]; + logger.debug( "profil nebol úspešne odoslaný na node č. ", params, resp); + + //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "odosielanie profilu na node č. " + node + " zlyhalo", "", SEND_TO.tb, instance, null ); + sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", {node: node}, "", SEND_TO.tb, instance ); + } + + if(itIsNodeCommand) + { + values.comm_status = "NOK"; + } + + if(updateStatus) + { + values.status = "NOK"; + } + + //master node + if(node == 0 && register == 4) + { + values.status = "NOK"; + values["master_node_version"] = "NOK"; + } + + console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); + if(saveToTb && Object.keys(values).length > 0 && tbName) + { + + let dataToTb = { + [tbName]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); + } + + } + /** + * function handles requests from terminal + * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. + * FAILURE means, that we got into catch block of writeData function. + */ + function terminalCommandResponse(params, responseType, data=null, reason="") { //success, error, failure + + if(params.refFlowdataKey == undefined) + { + //console.log("params.refFlowdataKey is undefined", params); + return; + } + else + { + console.log("params.refFlowdataKey: ", params); + } + + let message = null; + let type = null; + + switch (responseType) { + case "SUCCESS": + message = "cmd-terminal SUCCESS"; + type = "SUCCESS"; + break; + case "ERROR": + message = "cmd-terminal FAILED"; + type = "ERROR"; + break; + case "FAILURE": + message = "ERROR WRITE FAILED: " + reason; + type = "ERROR"; + break; + default: + type = undefined; + } + + logger.debug(message); + logger.debug(params); + + //make http response + let responseObj = {} + responseObj["type"] = type; + + if(responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; + else responseObj["bytes"] = data; + + let refFlowdata = refFlowdataObj[params.refFlowdataKey]; + if(refFlowdata !== undefined) + { + refFlowdata.data = responseObj; + instance.send(SEND_TO.http_response, refFlowdata); + } + } + + + /** + * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function + */ + function reportEdgeDateTimeAndNumberOfLuminaires(){ + + if(!FLOW.OMS_edgeName) return; + + const ts = Date.now(); + const keys = Object.keys(nodesData); + + const number_of_luminaires = keys.length; + let number_of_ok_luminaires = 0; + let number_of_nok_luminaires = 0; + + for(let i = 0; i < keys.length; i++) + { + let key = keys[i]; + let nodeObj = nodesData[key]; + if(nodeObj.tbname == undefined) continue; + + if(nodeObj.status) number_of_ok_luminaires++; + else number_of_nok_luminaires++; + } + + const values = { + "number_of_luminaires": number_of_luminaires, + "number_of_ok_luminaires": number_of_ok_luminaires, + "number_of_nok_luminaires": number_of_nok_luminaires, + "edge_date_time": ts + }; + + let dataToTb = { + [FLOW.OMS_edgeName]: [ + { + "ts": ts, + "values": values + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); + //instance.send(SEND_TO.tb, dataToTb); + } + + //to ensure, edgeDateTime will be send to tb at full minute + customTasksInterval = setInterval(function() { + if(new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); + }, 1000); + //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM @@ -2509,9 +2407,16 @@ exports.install = function(instance) { instance.on("close", () => { clearInterval(interval); + clearInterval(customTasksInterval); rsPort.close(); }); + instance.on("1", function(flowdata){ + if(flowdata.data.hasOwnProperty("testTbName")) + { + testTbName = flowdata.data.testTbName; + } + }) instance.on("data", async function(flowdata) { diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 8fff312..8010581 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -92,15 +92,14 @@ class DataToTbHandler } //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); - if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); + //if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); instance.send(this.index, dataToTbModified); } getDiffTimestamp(key) { - let seconds = 60*60;//1h - //seconds = 1;//for testing + let seconds = 30*60;//30 minutes //TODO set different value for given key!!! //if(key == "status") seconds = 2*60*60;//2h @@ -160,4 +159,4 @@ class DataToTbHandler } } -module.exports = DataToTbHandler; \ No newline at end of file +module.exports = DataToTbHandler; From 1b4b9ca973c3391b52cfd8b2e0fa7622f25f4c34 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Tue, 17 Sep 2024 08:54:32 +0200 Subject: [PATCH 02/25] PR request changes && rvo status added for all devices - em, twilight, thermometer --- config | 3 +- databases/status.table | 2 + flow/cmd_manager.js | 66 +++---- flow/dido_controller.js | 322 +++++++++++++++++++-------------- flow/helper/DataToTbHandler.js | 12 +- flow/modbus_reader.js | 1 - flow/thermometer.js | 6 +- flow/wsmqttpublish.js | 13 +- 8 files changed, 230 insertions(+), 195 deletions(-) create mode 100644 databases/status.table diff --git a/config b/config index f3a7be7..287769b 100644 --- a/config +++ b/config @@ -8,5 +8,6 @@ package#flow (Object) : { url: '/' } table.relays : line:number|tbname:string|contactor:number|profile:string table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number -table.pins : pin:number|type:string|line:number +table.pins : pin:string|type:string|line:number table.notifications : key:string|weight:string|sk:string|en:string +table.status : thermometer:string|em:string|twilight_sensor:string diff --git a/databases/status.table b/databases/status.table new file mode 100644 index 0000000..6d4f686 --- /dev/null +++ b/databases/status.table @@ -0,0 +1,2 @@ +thermometer:string|em:string|twilight_sensor:string ++|OK|OK|OK|............. diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 734bc3d..0fdfea1 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -80,6 +80,8 @@ const PRIORITY_TYPES = { node_cmd: 6 } +const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes + //list of command calls to process. Processing in runTasks function let tasks = []; @@ -141,7 +143,6 @@ let cmdCounter = {};//key is node, value is counter let cmdNOKNodeCounter = {};//key is node, value is counter let testTbName = "deleteAfterTesting" //for status testing purposes; - //END OF VARIABLE SETTINGS //-------------------------------- @@ -786,12 +787,9 @@ exports.install = function(instance) { params.register = 87;//Actual time params.rw = 1;//write - let timestampStart = PRIORITY_TYPES.node_broadcast; - //other values params.type = "cmd"; - //params.tbname = tbname; - params.timestamp = timestampStart; + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; @@ -1261,18 +1259,13 @@ exports.install = function(instance) { params.register = 6;//Time of dusk - Reg 6 params.rw = 1;//write - let timestampStart = PRIORITY_TYPES.node_broadcast; - //other values params.type = "cmd"; - //params.tbname = tbname; - params.timestamp = timestampStart; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "Broadcast-duskTime"; tasks.push(params); - } { @@ -1296,12 +1289,8 @@ exports.install = function(instance) { params.register = 7;//Time of dawn - Reg 6 params.rw = 1;//write - let timestampStart = PRIORITY_TYPES.node_broadcast; - //other values params.type = "cmd"; - //params.tbname = tbname; - params.timestamp = timestampStart; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "Broadcast-dawnTime"; @@ -1329,12 +1318,8 @@ exports.install = function(instance) { params.register = 87;//Actual time params.rw = 1;//write - let timestampStart = PRIORITY_TYPES.node_broadcast; - //other values params.type = "cmd"; - //params.tbname = tbname; - params.timestamp = timestampStart; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; @@ -1416,10 +1401,6 @@ exports.install = function(instance) { params.type = "cmd-master"; params.register = 4; params.address = 0; - - let timestampStart = PRIORITY_TYPES.fw_detection; - params.timestamp = timestampStart; - params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = 5; params.tbname = FLOW.OMS_edgeName; @@ -1435,9 +1416,7 @@ exports.install = function(instance) { { let params = getParams(PRIORITY_TYPES.fw_detection); params.type = "process_profiles"; - - let timestampStart = PRIORITY_TYPES.relay_profile; - params.timestamp = timestampStart; + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = 60;//60 = every hour params.info = "detekcia nespracovaných profilov linie a nodov"; //params.debug = true; @@ -1556,17 +1535,17 @@ exports.install = function(instance) { return true; } - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - 600000){ + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION){ if(node == 638 || node == 637) console.log("true true, return", node, now); return; } - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - 600000) + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { dbNodes.modify({ time_of_last_communication: now}).where("node", node).make(function(builder) { builder.callback(function(err, response) { - if(err == null) { + if(!err) { nodeObj.time_of_last_communication = now; if(node == 638 || node == 637) console.log('zapisane do db => status true & true', node, now) @@ -1585,7 +1564,7 @@ exports.install = function(instance) { else { dbNodes.modify({ status: newStatus}).where("node", node).make(function(builder) { builder.callback(function(err, response) { - if(err == null) { + if(!err) { nodeObj.status = newStatus; if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) @@ -1600,7 +1579,7 @@ exports.install = function(instance) { { dbNodes.modify({ status: newStatus, time_of_last_communication: now}).where("node", node).make(function(builder) { builder.callback(function(err, response) { - if(err == null) { + if(!err) { nodeObj.status = newStatus; nodeObj.time_of_last_communication = now; @@ -1989,7 +1968,7 @@ exports.install = function(instance) { startTime = new Date(); let saveToTb = true; - if(tbname == null || tbname == undefined || tbname == "") saveToTb = false; + if(!tbname) saveToTb = false; let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); @@ -2053,18 +2032,15 @@ exports.install = function(instance) { let result = cmdCounterResolve(nodeAddress); if(result == 0) { - dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { - builder.callback(function(err, response) { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); - - logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); - nodesData[nodeAddress].processed = true; - - }); + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); + + logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); + nodesData[nodeAddress].processed = true; }); + }); } } @@ -2296,7 +2272,7 @@ exports.install = function(instance) { else responseObj["bytes"] = data; let refFlowdata = refFlowdataObj[params.refFlowdataKey]; - if(refFlowdata !== undefined) + if(refFlowdata) { refFlowdata.data = responseObj; instance.send(SEND_TO.http_response, refFlowdata); @@ -2311,17 +2287,21 @@ exports.install = function(instance) { if(!FLOW.OMS_edgeName) return; + //Number of ok and nok nodes on platform does not equals to total number of nodes. + //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + const ts = Date.now(); - const keys = Object.keys(nodesData); + const keys = Object.keys(nodesData_clone); const number_of_luminaires = keys.length; let number_of_ok_luminaires = 0; let number_of_nok_luminaires = 0; - + for(let i = 0; i < keys.length; i++) { let key = keys[i]; - let nodeObj = nodesData[key]; + let nodeObj = nodesData_clone[key]; if(nodeObj.tbname == undefined) continue; if(nodeObj.status) number_of_ok_luminaires++; diff --git a/flow/dido_controller.js b/flow/dido_controller.js index b429b14..05cf3b4 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -6,7 +6,6 @@ exports.color = '#2134B0'; exports.input = 3; exports.output = ["red", "white", "yellow"]; exports.click = false; -exports.author = 'Daniel Segeš'; exports.icon = 'bolt'; exports.options = { edge: "undefined" }; @@ -136,9 +135,11 @@ exports.install = function(instance) { //process.exit(1); }) - let previousValues = {temperature: 0}; + // temperature value is initialized to -1000. It can be literally anything, we just needs to be able to enter if block in ws.onmessage function, when first temperatere data comes + let previousValues = {temperature: {value: -1000, lastTimeTemperatureReceived: Date.now() / 1000}}; let rsPortReceivedData = []; + //to be able to get proper twilight values, when let twilight_sensor_interval = 5;//minutes let twilight_sensor = []; const twilight_sensor_array = []; @@ -146,7 +147,7 @@ exports.install = function(instance) { let edgeName = ""; - monitor.info("DI_DO_Relay_Controller installed"); + monitor.info("DIDO_Relay_Controller installed"); //key is PIN number , line: 0 = RVO /* @@ -173,28 +174,25 @@ exports.install = function(instance) { const dbRelays = TABLE("relays"); let relaysData = {};//key is line + const dbStatus = TABLE("status"); + let statusData = null; + //status for calculating Statecodes - let deviceStatuses = {};//key is device name: temperature,.... - deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič - deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód - deviceStatuses["door_condition"] = "closed";//Dverový kontakt - deviceStatuses["em"] = "OK";//elektromer rvo - deviceStatuses["temperature"] = "OK";//templomer - deviceStatuses["battery"] = "OK";//Batéria - deviceStatuses["power_supply"] = "OK";//Zdroj - deviceStatuses["master_node"] = "OK";//MN - FLOW.OMS_masterNodeIsResponding - deviceStatuses["no_voltage"] = "OK";//FLOW.OMS_no_voltage - výpadok napätia na fáze + let deviceStatus = {};//key is device name: temperature,.... + deviceStatus["state_of_main_switch"] = "Off";//Hlavný istič + deviceStatus["rotary_switch_state"] = "Off";//Prevádzkový mód + deviceStatus["door_condition"] = "closed";//Dverový kontakt + deviceStatus["em"] = "OK";//elektromer rvo + deviceStatus["temperature"] = "OK";//templomer + deviceStatus["battery"] = "OK";//Batéria + deviceStatus["power_supply"] = "OK";//Zdroj + deviceStatus["master_node"] = "OK";//MN - FLOW.OMS_masterNodeIsResponding + deviceStatus["no_voltage"] = "OK";//FLOW.OMS_no_voltage - výpadok napätia na fáze - deviceStatuses["state_of_breaker"] = {};//"Off";//Istič - deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač - deviceStatuses["twilight_sensor"] = "OK"; //lux sensor + deviceStatus["state_of_breaker"] = {};//"Off";//Istič + deviceStatus["state_of_contactor"] = {};//"Off";//Stykač + deviceStatus["twilight_sensor"] = "OK"; //lux sensor - /* - dbRelays.on('change', function(doc, old) { - console.log("'DI_DO_Controller - dbRelays.on('change'"); - instance.send(SEND_TO.cmd_manager, "reload_relays"); - }); - */ const SerialPort = require('serialport'); const WebSocket = require('ws'); @@ -202,12 +200,15 @@ exports.install = function(instance) { let ws = null; let rsPort = null; - //const { exec } = require('child_process'); - const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js'); + const { runSyncExec } = require('./helper/serialport_helper.js'); const { bytesToInt, resizeArray } = require('./helper/utils'); const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); - const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js'); + const { sendNotification } = require('./helper/notification_reporter.js'); const bitwise = require('bitwise'); + + const DataToTbHandler = require('./helper/DataToTbHandler.js'); + const tbHandler = new DataToTbHandler(SEND_TO.tb); + tbHandler.setSender(exports.title); const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); const errorHandler = new ErrorToServiceHandler(); @@ -219,6 +220,7 @@ exports.install = function(instance) { console.log(exports.title, "controller type: ", controller_type); + async function loadAllDb() { let responsePins = await promisifyBuilder(dbPins.find()); @@ -227,6 +229,10 @@ exports.install = function(instance) { let responseRelays = await promisifyBuilder(dbRelays.find()); relaysData = makeMapFromDbResult(responseRelays, "line"); + let responseStatus = await promisifyBuilder(dbStatus.find()); + statusData = responseStatus[0]; // {thermometer: '{"status":"OK","temperature":0}', em: 'OK', twilight_sensor: 'OK'} + deviceStatus["temperature"] = statusData.thermometer; + FLOW.OMS_rvo_tbname = relaysData[0].tbname; if(controller_type === "lm") @@ -300,7 +306,7 @@ exports.install = function(instance) { instance.send(SEND_TO.tb, dataToTb); - let time = 3*1000; + let time = 5*1000; setTimeout(function(){ instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"}); @@ -425,6 +431,7 @@ exports.install = function(instance) { }, 150000) }; + // SAMPLE DATA FROM WEBSOCKET // { // glob_dev_id: 1, @@ -453,25 +460,21 @@ exports.install = function(instance) { data = JSON.parse(data.data); // data comes in array except of "temperature" ==> it comes as an object - if(!Array.isArray(data)) + if(isObject(data)) { let value = data['value']; + previousValues["temperature"]["lastTimeTemperatureReceived"] = data['time']; - // temperature value comes very often. To handle it, we check if it change for more than 0.2 degrees, if yes, we send to TB - if(Math.abs(previousValues["temperature"] - value) > 0.21 ) + if(deviceStatus["temperature"] === "NOK") { - const dataToTb = { - [FLOW.OMS_rvo_tbname]: [ - { - "ts": Date.now(), - "values": {temperature: value} - } - ] - }; - - deviceStatuses["temperature"] = "OK"; - previousValues["temperature"] = value; - instance.send(SEND_TO.tb, dataToTb); + writeThermometerStatusToDb("OK"); + } + + // temperature value comes very often. To handle it, we check if it change for more than 0.3 degrees, if yes, we send to TB + if(Math.abs(previousValues["temperature"]["value"] - value) > 0.21 ) + { + previousValues["temperature"]["value"] = value; + sendTelemetry({temperature: value}, FLOW.OMS_rvo_tbname); } return; } @@ -517,7 +520,6 @@ exports.install = function(instance) { // }, 150000) // } - instance.on("close", () => { if(rsPort) rsPort.close(); if(ws) ws.close(); @@ -526,6 +528,7 @@ exports.install = function(instance) { loadAllDb(); + function getPin(line) { //conversionTable @@ -605,13 +608,13 @@ exports.install = function(instance) { let bits = []; - if(deviceStatuses["state_of_breaker"][line] == "On") + if(deviceStatus["state_of_breaker"][line] == "On") { bits.push(0); } else bits.push(1); - if(deviceStatuses["state_of_contactor"][line] == "On") + if(deviceStatus["state_of_contactor"][line] == "On") { bits.push(0); } @@ -720,7 +723,7 @@ exports.install = function(instance) { //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1}; ws.send(JSON.stringify(cmd)); - //switchLogic(pin, 1) + switchLogic(pin, 1) } } @@ -766,7 +769,7 @@ exports.install = function(instance) { // if(!rsPort.isOpen && !ws) if(!rsPort && !ws) { - errLogger.error("di do controller - port or websocket is not opened"); + errLogger.error("dido controller - port or websocket is not opened"); return; } @@ -797,7 +800,7 @@ exports.install = function(instance) { //monitor.info("turnOffLine pin (relay)", pin); let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0}; ws.send(JSON.stringify(cmd)); - //switchLogic(pin, 0) + switchLogic(pin, 0) } } @@ -817,15 +820,17 @@ exports.install = function(instance) { const status = flowdata.data.status; if(status == "NOK-twilight_sensor") { - deviceStatuses["twilight_sensor"] = "NOK"; + deviceStatus["twilight_sensor"] = "NOK"; } else if(status == "NOK-em340" || status == "NOK-em111") { - deviceStatuses["em"] = "NOK"; + deviceStatus["em"] = "NOK"; } + //"NOK-thermometer" comes just from LM. Unipi handles thermometer from ws evok. else if(status == "NOK-thermometer") { - deviceStatuses["temperature"] = "NOK"; + previousValues["temperature"]["lastTimeTemperatureReceived"] = null; + deviceStatus["temperature"] = "NOK"; } } else if(flowdata.data?.values) @@ -834,16 +839,18 @@ exports.install = function(instance) { if(values.hasOwnProperty("twilight_sensor")) { instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]}); - deviceStatuses["twilight_sensor"] = "OK" + deviceStatus["twilight_sensor"] = "OK" } + //"temperature" comes just from LM. Unipi handles thermometer from ws evok. else if(values.hasOwnProperty("temperature")) { - deviceStatuses["temperature"] = "OK"; + previousValues["temperature"]["lastTimeTemperatureReceived"] = Date.now() / 1000; //time in seconds + deviceStatus["temperature"] = "OK"; } // EM else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current")) { - deviceStatuses["em"] = "OK"; + deviceStatus["em"] = "OK"; } else { @@ -876,7 +883,6 @@ exports.install = function(instance) { else if(obj.command == "turnOnAlarm") turnOnAlarm(); else if(obj.command == "turnOffAlarm") turnOffAlarm(); - //! ake data prichadzaju z cmd_manager.js ??? //TODO transform to websocket if (Array.isArray(obj)){ @@ -897,11 +903,11 @@ exports.install = function(instance) { let bits = []; //Hlavný istič - state_of_main_switch - if(deviceStatuses["state_of_main_switch"] == "On") + if(deviceStatus["state_of_main_switch"] == "On") { bits.push(0); } - else if(deviceStatuses["state_of_main_switch"] == "Off") + else if(deviceStatus["state_of_main_switch"] == "Off") { bits.push(1); } @@ -909,19 +915,19 @@ exports.install = function(instance) { //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY if(!FLOW.OMS_maintenance_mode) { - if(deviceStatuses["rotary_switch_state"] == "Manual") + if(deviceStatus["rotary_switch_state"] == "Manual") { bits.push(0); bits.push(1); } - if(deviceStatuses["rotary_switch_state"] == "Automatic") + if(deviceStatus["rotary_switch_state"] == "Automatic") { bits.push(0); bits.push(0); } - if(deviceStatuses["rotary_switch_state"] == "Off") + if(deviceStatus["rotary_switch_state"] == "Off") { bits.push(1); bits.push(0); @@ -933,7 +939,7 @@ exports.install = function(instance) { } //Dverový kontakt - if(deviceStatuses["door_condition"] == "closed") + if(deviceStatus["door_condition"] == "closed") { bits.push(0); } @@ -943,7 +949,7 @@ exports.install = function(instance) { } //EM - if(deviceStatuses["em"] == "NOK") + if(deviceStatus["em"] == "NOK") { bits.push(1); } @@ -953,7 +959,7 @@ exports.install = function(instance) { } //Teplomer - if(deviceStatuses["temperature"] == "NOK") + if(deviceStatus["temperature"] == "NOK") { bits.push(1); } @@ -963,7 +969,7 @@ exports.install = function(instance) { } //Batéria - if(deviceStatuses["battery"] == "NOK") + if(deviceStatus["battery"] == "NOK") { bits.push(1); } @@ -973,7 +979,7 @@ exports.install = function(instance) { } //Zdroj - if(deviceStatuses["power_supply"] == "NOK") + if(deviceStatus["power_supply"] == "NOK") { bits.push(1); } @@ -983,7 +989,7 @@ exports.install = function(instance) { } //MN - if(deviceStatuses["master_node"] == "NOK") + if(deviceStatus["master_node"] == "NOK") { bits.push(1); } @@ -993,7 +999,7 @@ exports.install = function(instance) { } //výpadok napätia na fáze - if(deviceStatuses["no_voltage"] == "NOK") + if(deviceStatus["no_voltage"] == "NOK") { bits.push(1); } @@ -1002,7 +1008,7 @@ exports.install = function(instance) { bits.push(0); } - if(deviceStatuses["twilight_sensor"] == "NOK") + if(deviceStatus["twilight_sensor"] == "NOK") { bits.push(1); } @@ -1017,7 +1023,7 @@ exports.install = function(instance) { bits.push(0); } - // console.log("calculateStateCode - deviceStatuses", deviceStatuses); + // console.log("calculateStateCode - deviceStatus", deviceStatus); // console.log("calculateStateCode", bits); let byte0 = bitwise.byte.write(bits.slice(0,8).reverse()); @@ -1031,16 +1037,54 @@ exports.install = function(instance) { } + function sendDeviceStatus() { + + const table = { + "OK": 1, + "NOK": 0 + }; + + const dataToTb = { + "electrometer_status": table[deviceStatus["em"]], + "twilight_sensor_status": table[deviceStatus["twilight_sensor"]], + "thermometer_status": table[deviceStatus["temperature"]], + "phase_1_status": 1, + "phase_2_status": 1, + "phase_3_status": 1, + "master_node_status": table[deviceStatus["master_node"]] + }; + + for (const phase of FLOW.OMS_no_voltage) { + if(phase == 1) dataToTb["phase_1_status"] = 0; + if(phase == 2) dataToTb["phase_2_status"] = 0; + if(phase == 3) dataToTb["phase_3_status"] = 0; + } + + //thermometer did not send data for more than a hour. Time in seconds + if(deviceStatus["temperature"] === "OK") + { + if(previousValues["temperature"]["lastTimeTemperatureReceived"] + 3600 < Date.now() / 1000) + { + writeThermometerStatusToDb("NOK"); + dataToTb["thermometer_status"] = 0; + } + } + + sendTelemetry(dataToTb, FLOW.OMS_rvo_tbname); + } + + setInterval(sendDeviceStatus, 150000); + + function checkFinalRVOStatus() { // we check if any of these pins values are 0 --> it means status RVO is "NOK" - // pinIndex 6 is door_condition - if open in maintenance mode - status = OK + // pinIndex 6 is door_condition - if it is opened in maintenance mode - status = OK //set RVO state - let status = "OK"; - if(deviceStatuses["em"] == "NOK") + if(deviceStatus["em"] == "NOK") { let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: EM status is NOK"); if(writeToFile) errLogger.error("checkFinalRVOStatus: EM status is NOK"); @@ -1048,7 +1092,7 @@ exports.install = function(instance) { status = "NOK"; } - if(deviceStatuses["twilight_sensor"] == "NOK") + if(deviceStatus["twilight_sensor"] == "NOK") { let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: twilight_sensor is NOK"); if(writeToFile) errLogger.error("checkFinalRVOStatus: twilight sensor is NOK"); @@ -1057,7 +1101,7 @@ exports.install = function(instance) { } //ak teplomer NOK, rvo nok - if(deviceStatuses["temperature"] == "NOK") + if(deviceStatus["temperature"] == "NOK") { let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: temperature status is NOK"); @@ -1105,9 +1149,9 @@ exports.install = function(instance) { errorHandler.sendMessageToService("Master node is not responding"); status = "NOK"; - deviceStatuses["master_node"] = "NOK"; + deviceStatus["master_node"] = "NOK"; } - else deviceStatuses["master_node"] = "OK"; + else deviceStatus["master_node"] = "OK"; //console.log("checkFinalRVOStatus", status); if(FLOW.OMS_no_voltage.size > 0) @@ -1117,14 +1161,13 @@ exports.install = function(instance) { status = "NOK"; - deviceStatuses["no_voltage"] = "NOK"; + deviceStatus["no_voltage"] = "NOK"; } - else deviceStatuses["no_voltage"] = "OK"; + else deviceStatus["no_voltage"] = "OK"; if(status == "NOK") { sendTelemetry({status: "NOK"}, FLOW.OMS_rvo_tbname); - return false; } @@ -1155,13 +1198,13 @@ exports.install = function(instance) { } let obj = pinsData[pinIndex]; - if(obj == undefined) { previousValues[pinIndex] = newPinValue; return; } + //tbname is added to pinsData in initialSettings function let type = obj.type; let line = obj.line; let tbname = obj.tbname; @@ -1180,13 +1223,13 @@ exports.install = function(instance) { sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); values["status"] = "NOK"; - deviceStatuses["state_of_main_switch"] = "Off"; + deviceStatus["state_of_main_switch"] = "Off"; } else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); - deviceStatuses["state_of_main_switch"] = "On"; + deviceStatus["state_of_main_switch"] = "On"; } } @@ -1223,7 +1266,7 @@ exports.install = function(instance) { if (pin2 == 0 && pin3 == 0) value = "Off"; if (pin2 == 0 && pin3 == 1) value = "Automatic"; - deviceStatuses["rotary_switch_state"] = value; + deviceStatus["rotary_switch_state"] = value; //automatic - profilu pre nody sa vykonavaju //ak je spracovany, a automatic - tak ho zapnem @@ -1242,14 +1285,14 @@ exports.install = function(instance) { sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply"); values["status"] = "NOK"; - deviceStatuses["power_supply"] = "NOK"; + deviceStatus["power_supply"] = "NOK"; } else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", SEND_TO.tb, instance); sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply"); - deviceStatuses["power_supply"] = "OK"; + deviceStatus["power_supply"] = "OK"; } } //Batéria - pin 5 @@ -1261,14 +1304,14 @@ exports.install = function(instance) { sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level"); values["status"] = "NOK"; - deviceStatuses["battery"] = "NOK"; + deviceStatus["battery"] = "NOK"; } else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", SEND_TO.tb, instance); sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level"); - deviceStatuses["battery"] = "OK"; + deviceStatus["battery"] = "OK"; } } //Dverový kontakt - pin 6 @@ -1310,13 +1353,13 @@ exports.install = function(instance) { sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", SEND_TO.tb, instance, "rvo_door"); } - deviceStatuses["door_condition"] = value; + deviceStatus["door_condition"] = value; } //lux sensor else if(type == "twilight_sensor") { - //! TODO - to show nok status, if lux value is not changing more then 10 times. From unipi for example comes value from 0-1000. + //! TODO - to show nok status, if lux value is not changing more then 10 times. //Daylight is far more than 1000. So most of the day, when it is sunshine comes just value 1000. But lux sensor is not NOK. //This is not the case in LM. If value from LM is the same 10x, there is 99% possibility, that sensor is NOK. values["status"] = "OK"; @@ -1382,65 +1425,45 @@ exports.install = function(instance) { { //sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance ); - if(!(deviceStatuses["state_of_contactor"][line] == value)) + if(!(deviceStatus["state_of_contactor"][line] == value)) { sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", SEND_TO.tb, instance ); } - else - { - deviceStatuses["state_of_contactor"][line] = value; - } + + deviceStatus["state_of_contactor"][line] = value; //true, false if(value === "On") value = true; - else if(value === "Off") value = false; + else if(value === "Off") value = false; //modify table relays dbRelays.modify({ contactor: newPinValue }).where("line", line).make(function(builder) { - builder.callback(function(err, response) { - - /* - if(useTurnOffCounter) + if(!err) { - turnOffCounter--; + let time = 0; + if(value) time = 1000 * 10;//10 sekund - if(turnOffCounter <= 0) - { - useTurnOffCounter = false; - } - } - */ + let dataChanged = false; + if(relaysData[line].contactor != value) dataChanged = true; + relaysData[line].contactor = value; + + //ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles + //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor - if(err == undefined) - { + setTimeout(function(){ + instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); + }, time); - let time = 0; - if(value) time = 1000 * 10;//10 sekund + reportLineStatus(line); + } + else + { + errLogger.error("modify table relays failed", err); + } - let dataChanged = false; - if(relaysData[line].contactor != value) dataChanged = true; - relaysData[line].contactor = value; - - //ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles - //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor - - setTimeout(function(){ - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); - }, time); - - reportLineStatus(line); - - - } - else - { - errLogger.error("modify table relays failed", err); - } - - - }); }); + }); } if(type === "state_of_breaker") @@ -1467,7 +1490,7 @@ exports.install = function(instance) { instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: lineOnSameBraker[i]}); - deviceStatuses["state_of_breaker"][lineOnSameBraker[i]] = value; + deviceStatus["state_of_breaker"][lineOnSameBraker[i]] = value; reportLineStatus(lineOnSameBraker[i]); values[type] = value; @@ -1486,7 +1509,7 @@ exports.install = function(instance) { { instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line + 3}); - deviceStatuses["state_of_breaker"][line + 3] = value; + deviceStatus["state_of_breaker"][line + 3] = value; reportLineStatus(line + 3); values[type] = value; @@ -1495,14 +1518,13 @@ exports.install = function(instance) { delete values[type]; } - } } if(value == "Off") values["status"] = "NOK"; - deviceStatuses["state_of_breaker"][line] = value; + deviceStatus["state_of_breaker"][line] = value; reportLineStatus(line); } @@ -1569,8 +1591,7 @@ exports.install = function(instance) { } - function sendTelemetry(values, tbname) - { + function sendTelemetry(values, tbname) { let dataToTb = { [tbname]: [ { @@ -1578,12 +1599,33 @@ exports.install = function(instance) { "values": values } ] - } + }; - instance.send(SEND_TO.tb, dataToTb); + // instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); } -} + + function writeThermometerStatusToDb(status) { + dbStatus.modify({ thermometer: status }).make(function(builder) { + builder.callback(function(err, response) { + if(!err) + { + deviceStatus["temperature"] = status; + console.log(`Wrote to db status: thermometer ${status}`); + } + }); + }); + } + + + function isObject (item) { + return (typeof item === "object" && !Array.isArray(item) && item !== null); + } + + + +} //end of instance diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 8010581..cf29b82 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -3,6 +3,9 @@ class DataToTbHandler constructor(index) { this.index = index; + // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) + this.timeToHoldTbValue = 30*60; //30 minutes + this.previousValues = {}; this.debug = false; this.messageCounter = 0; @@ -99,14 +102,9 @@ class DataToTbHandler getDiffTimestamp(key) { - let seconds = 30*60;//30 minutes - //TODO set different value for given key!!! - //if(key == "status") seconds = 2*60*60;//2h - - let timestampDiffToRemoveKey = seconds*1000; - - return timestampDiffToRemoveKey; + //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h + return this.timeToHoldTbValue * 1000; } prepareValuesForTb(tbname, timestamp, values) diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index f34ec9a..3bee223 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -287,7 +287,6 @@ exports.install = function(instance) { let l = singleValue.split("_"); let phase = parseInt(l[1]); - if(FLOW.OMS_no_voltage == undefined) FLOW.OMS_no_voltage = new Set(); // console.log(values[singleValue], tbName); if(values[singleValue] == 0) diff --git a/flow/thermometer.js b/flow/thermometer.js index 1a5714f..9b51fac 100644 --- a/flow/thermometer.js +++ b/flow/thermometer.js @@ -216,5 +216,9 @@ exports.install = function(instance) { }, 15000); startRead = setInterval(start, timeoutMin * 1000 * 60); + + //testing + //setInterval(() => {instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"})}, 180000); + +}; -}; \ No newline at end of file diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index 794fedc..3064de1 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -73,6 +73,11 @@ let errLogger; let logger; let monitor; +//TODO brokerready and sendBrokerError seems to be the same. Moreover, we use FLOW_OMS_brokerready variable!! +// +// if there is an error in broker connection, flow logs to monitor.txt. Not to log messages every second, we use sendBrokerError variable +let sendBrokerError = true; + if(useLog4js) { var path = require('path'); @@ -217,6 +222,8 @@ exports.install = function(instance) { instance.status("Connected", "green"); monitor.info("MQTT broker connected"); + sendBrokerError = true; + brokerready = true; FLOW.OMS_brokerready = brokerready; wsmqtt_status = 'connected'; @@ -266,8 +273,10 @@ exports.install = function(instance) { broker.on('error', function(err) { instance.status("Err: "+ err.code, "red"); instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts }); - monitor.info('MQTT broker error', err); - + if(sendBrokerError) { + monitor.info('MQTT broker error', err); + sendBrokerError = false; + } brokerready = false; FLOW.OMS_brokerready = brokerready; wsmqtt_status = 'disconnected'; From ca39e1266c637d1e9a8152e798a84536136def9e Mon Sep 17 00:00:00 2001 From: rasta5man Date: Sun, 22 Sep 2024 22:18:46 +0200 Subject: [PATCH 03/25] Rvo status handled just in dido_controller --- flow/cmd_manager.js | 68 +----- flow/dido_controller.js | 372 ++++++++++++--------------------- flow/helper/DataToTbHandler.js | 31 ++- flow/modbus_reader.js | 6 +- flow/thermometer.js | 5 +- flow/wsmqttpublish.js | 2 +- 6 files changed, 166 insertions(+), 318 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 0fdfea1..613d1f6 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -142,7 +142,6 @@ let nodesData = {};//key is node, value data from db let cmdCounter = {};//key is node, value is counter let cmdNOKNodeCounter = {};//key is node, value is counter -let testTbName = "deleteAfterTesting" //for status testing purposes; //END OF VARIABLE SETTINGS //-------------------------------- @@ -319,8 +318,6 @@ function processNodeProfile(node) cmdCounter[node] = 1; tasks.push(params); - - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", SEND_TO.tb, instance ); } else { @@ -1794,9 +1791,7 @@ exports.install = function(instance) { message = "off"; } - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.INFO, "aplikovaný bod profilu línie " + params.line + " - stav: " + message, "", SEND_TO.tb, instance, null ); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance ); - interval = setInterval(runTasks, SHORT_INTERVAL); return; } @@ -1936,7 +1931,6 @@ exports.install = function(instance) { //TODO astrohodiny let dusk = "Time of dusk: " + sunCalcResult["dusk"]; - //sendNotification("CMD Manager: calculated Time of dusk", relaysData[0].tbname, ERRWEIGHT.INFO, dusk, "", SEND_TO.tb, instance, null ); } } @@ -1956,7 +1950,6 @@ exports.install = function(instance) { //TODO astrohodiny let dawn = "Time of dawn: " + sunCalcResult["dawn"]; - //sendNotification("CMD Manager: calculated Time of dusk", relaysData[0].tbname, ERRWEIGHT.INFO, dawn, "", SEND_TO.tb, instance, null ); } } @@ -1968,7 +1961,7 @@ exports.install = function(instance) { startTime = new Date(); let saveToTb = true; - if(!tbname) saveToTb = false; + if(!tbname) saveToTb = false; let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); @@ -1993,13 +1986,6 @@ exports.install = function(instance) { let message_type = result.type; let error = result.error; - //testing purpose node 638, tbname rDbQ84xzwgdqEoPm3kbJQWk9anOZY1RXyBv2LVM6 - if(tbname == testTbName) { - message = "NOK"; - message_type = "ERROR"; - error = "type is: ERROR"; - } - if(params.debug != "generated cmd") { //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug, params); @@ -2057,24 +2043,16 @@ exports.install = function(instance) { } //master node - if(nodeAddress == 0 && register == 4) - { - values.status = "OK"; - values["edge_fw_version"] = FLOW.OMS_edge_fw_version; - } - if(nodeAddress == 0) { - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", SEND_TO.tb, instance, "rvo_status" ); - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); FLOW.OMS_masterNodeIsResponding = true; + if(register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version; } //odoslanie príkazu z terminálu - dáta if(type == "cmd-terminal") { - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.DEBUG, "odoslanie príkazu z terminálu", params, SEND_TO.tb, instance, null ); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); } @@ -2169,23 +2147,23 @@ exports.install = function(instance) { let values = {}; - console.log(message); + // console.log(message); let updateStatus = updateNodeStatus(node, false); + //master node if(node == 0) { - //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", SEND_TO.tb, instance, "rvo_status"); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); logger.debug("master_node_is_not_responding", params); FLOW.OMS_masterNodeIsResponding = false; + + if(register == 4) values["master_node_version"] = "NOK"; } if(type == "set_node_profile") { delete cmdCounter[node]; - logger.debug( "profil nebol úspešne odoslaný na node č. ", params, resp); - - //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "odosielanie profilu na node č. " + node + " zlyhalo", "", SEND_TO.tb, instance, null ); + logger.debug( "profil nebol úspešne odoslaný na node č. ", params); sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", {node: node}, "", SEND_TO.tb, instance ); } @@ -2199,14 +2177,7 @@ exports.install = function(instance) { values.status = "NOK"; } - //master node - if(node == 0 && register == 4) - { - values.status = "NOK"; - values["master_node_version"] = "NOK"; - } - - console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); + // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); if(saveToTb && Object.keys(values).length > 0 && tbName) { @@ -2391,12 +2362,6 @@ exports.install = function(instance) { rsPort.close(); }); - instance.on("1", function(flowdata){ - if(flowdata.data.hasOwnProperty("testTbName")) - { - testTbName = flowdata.data.testTbName; - } - }) instance.on("data", async function(flowdata) { @@ -2535,7 +2500,6 @@ exports.install = function(instance) { tasks.push(params); - //process profiles turnOnOffLinesAccordingToLuxSensor(lux_sensor); } @@ -2552,14 +2516,7 @@ exports.install = function(instance) { state_of_breaker[line] = value; let status = "OK"; - let weight = ERRWEIGHT.NOTICE; - let message = `zapnutý istič línie č. ${line}`; - if(value == "Off") - { - weight = ERRWEIGHT.ERROR; - message = `vypnutý istič línie č. ${line}`; - status = "NOK"; - } + if(value == "Off") status = "NOK"; if(dataChanged) { @@ -2588,11 +2545,7 @@ exports.install = function(instance) { tbHandler.sendToTb(dataToTb, instance); //current value - if(value == "Off") - { - //vyreportovat vsetky svietdla na linii - reportOfflineNodeStatus(line); - } + if(value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii else reportOnlineNodeStatus(line); } @@ -2788,7 +2741,6 @@ exports.install = function(instance) { if(profile === "") logger.debug("worksys - update node profile done - profile is empty"); //profil úspešne prijatý pre node č. xx - //sendNotification("CMD manager", tbname, ERRWEIGHT.INFO, `profil úspešne poslaný z platformy na RVO pre node č. ${node}`, profile, SEND_TO.tb, instance, null ); sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, SEND_TO.tb, instance ); nodesData[node].processed = false; diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 05cf3b4..9e61344 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -3,7 +3,7 @@ exports.title = 'DIDO_Controller'; exports.version = '2.0.0'; exports.group = 'Worksys'; exports.color = '#2134B0'; -exports.input = 3; +exports.input = 2; exports.output = ["red", "white", "yellow"]; exports.click = false; exports.icon = 'bolt'; @@ -59,7 +59,8 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda //globals //FIRMWARE version -FLOW.OMS_edge_fw_version = "2024-07-08";//rok-mesiac-den +//TODO remove FLOW.OMS_edgeName variable, as we have FLOW.OMS_rvo_tbname +FLOW.OMS_edge_fw_version = "2024-09-23";//rok-mesiac-den FLOW.OMS_edgeName = ""; FLOW.OMS_maintenance_mode = false; @@ -83,6 +84,9 @@ const SEND_TO = { cmd_manager: 2 } +const TIME_AFTER_TEMPERATURE_NOK_STATUS = 3600; //seconds +const DIFFERENCE_TO_SEND_TEMPERATURE = 0.31; + var log4js = require("log4js"); var path = require('path'); @@ -178,21 +182,20 @@ exports.install = function(instance) { let statusData = null; //status for calculating Statecodes - let deviceStatus = {};//key is device name: temperature,.... - deviceStatus["state_of_main_switch"] = "Off";//Hlavný istič - deviceStatus["rotary_switch_state"] = "Off";//Prevádzkový mód - deviceStatus["door_condition"] = "closed";//Dverový kontakt - deviceStatus["em"] = "OK";//elektromer rvo - deviceStatus["temperature"] = "OK";//templomer - deviceStatus["battery"] = "OK";//Batéria - deviceStatus["power_supply"] = "OK";//Zdroj - deviceStatus["master_node"] = "OK";//MN - FLOW.OMS_masterNodeIsResponding - deviceStatus["no_voltage"] = "OK";//FLOW.OMS_no_voltage - výpadok napätia na fáze - - deviceStatus["state_of_breaker"] = {};//"Off";//Istič - deviceStatus["state_of_contactor"] = {};//"Off";//Stykač - deviceStatus["twilight_sensor"] = "OK"; //lux sensor - + let deviceStatus = { //key is device name: temperature,.... + "state_of_main_switch": "Off", //Hlavný istič + "rotary_switch_state": "Off", //Prevádzkový mód + "door_condition": "closed", //Dverový kontakt + "em": "OK", //elektromer rvo + "temperature": "OK", //templomer + "battery": "OK", //Batéria + "power_supply": "OK", //Zdroj + "master_node": "OK", //MN - FLOW.OMS_masterNodeIsResponding + "no_voltage": "OK", //FLOW.OMS_no_voltage - výpadok napätia na fáze + "state_of_breaker": {}, //"Off",//Istič + "state_of_contactor": {}, //"Off",//Stykač + "twilight_sensor": "OK" //lux sensor + }; const SerialPort = require('serialport'); const WebSocket = require('ws'); @@ -230,9 +233,9 @@ exports.install = function(instance) { relaysData = makeMapFromDbResult(responseRelays, "line"); let responseStatus = await promisifyBuilder(dbStatus.find()); - statusData = responseStatus[0]; // {thermometer: '{"status":"OK","temperature":0}', em: 'OK', twilight_sensor: 'OK'} + statusData = responseStatus[0]; // {thermometer: 'OK', em: 'OK', twilight_sensor: 'OK'} deviceStatus["temperature"] = statusData.thermometer; - + FLOW.OMS_rvo_tbname = relaysData[0].tbname; if(controller_type === "lm") @@ -290,7 +293,6 @@ exports.install = function(instance) { let values = {}; values["edge_fw_version"] = FLOW.OMS_edge_fw_version; values["maintenance_mode"] = FLOW.OMS_maintenance_mode; - values["status"] = "OK"; edgeName = relaysData[0].tbname; FLOW.OMS_edgeName = edgeName; @@ -455,7 +457,7 @@ exports.install = function(instance) { // dev: 'input', // mode: 'Simple' // }, - ws.onmessage = function(data) { + ws.onmessage = async function(data) { data = JSON.parse(data.data); @@ -463,18 +465,22 @@ exports.install = function(instance) { if(isObject(data)) { let value = data['value']; + const values = {}; previousValues["temperature"]["lastTimeTemperatureReceived"] = data['time']; + // we received data from thermometer, but thermometer status is NOK: if(deviceStatus["temperature"] === "NOK") { - writeThermometerStatusToDb("OK"); + await writeThermometerStatusToDb("OK"); + sendRvoStatus(); } // temperature value comes very often. To handle it, we check if it change for more than 0.3 degrees, if yes, we send to TB - if(Math.abs(previousValues["temperature"]["value"] - value) > 0.21 ) + if(Math.abs(previousValues["temperature"]["value"] - value) > DIFFERENCE_TO_SEND_TEMPERATURE) { previousValues["temperature"]["value"] = value; - sendTelemetry({temperature: value}, FLOW.OMS_rvo_tbname); + values['temperature'] = value; + sendTelemetry(values, FLOW.OMS_rvo_tbname); } return; } @@ -802,7 +808,6 @@ exports.install = function(instance) { ws.send(JSON.stringify(cmd)); switchLogic(pin, 0) } - } @@ -851,18 +856,13 @@ exports.install = function(instance) { else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current")) { deviceStatus["em"] = "OK"; - } - else - { - return; + FLOW.OMS_no_voltage.size > 0 ? deviceStatus["no_voltage"] = "NOK": deviceStatus["no_voltage"] = "OK"; } - const updateStatus = checkFinalRVOStatus(); - if(updateStatus) values.status = "OK"; - sendTelemetry(values, FLOW.OMS_rvo_tbname); } + sendRvoStatus(); }) @@ -903,11 +903,12 @@ exports.install = function(instance) { let bits = []; //Hlavný istič - state_of_main_switch - if(deviceStatus["state_of_main_switch"] == "On") + //TODO state_of main_switch is door contact in senica rvo - values should be "open" and "closed" + if(deviceStatus["state_of_main_switch"] == "closed") { bits.push(0); } - else if(deviceStatus["state_of_main_switch"] == "Off") + else { bits.push(1); } @@ -1037,7 +1038,7 @@ exports.install = function(instance) { } - function sendDeviceStatus() { + async function sendRvoStatus() { const table = { "OK": 1, @@ -1054,124 +1055,57 @@ exports.install = function(instance) { "master_node_status": table[deviceStatus["master_node"]] }; - for (const phase of FLOW.OMS_no_voltage) { - if(phase == 1) dataToTb["phase_1_status"] = 0; - if(phase == 2) dataToTb["phase_2_status"] = 0; - if(phase == 3) dataToTb["phase_3_status"] = 0; - } + for (const phase of FLOW.OMS_no_voltage) dataToTb[`phase_${phase}_status`] = 0; //thermometer did not send data for more than a hour. Time in seconds if(deviceStatus["temperature"] === "OK") { - if(previousValues["temperature"]["lastTimeTemperatureReceived"] + 3600 < Date.now() / 1000) + if(previousValues["temperature"]["lastTimeTemperatureReceived"] + TIME_AFTER_TEMPERATURE_NOK_STATUS < Date.now() / 1000) { - writeThermometerStatusToDb("NOK"); + // to be able to calculate proper RVO status, we need to await writeThermometerStatusToDb function + await writeThermometerStatusToDb("NOK"); dataToTb["thermometer_status"] = 0; } } + dataToTb["status"] = checkRvoStatus(); + dataToTb["statecode"] = calculateStateCode(); + sendTelemetry(dataToTb, FLOW.OMS_rvo_tbname); } - setInterval(sendDeviceStatus, 150000); - - function checkFinalRVOStatus() { + function checkRvoStatus() { // we check if any of these pins values are 0 --> it means status RVO is "NOK" // pinIndex 6 is door_condition - if it is opened in maintenance mode - status = OK - //set RVO state let status = "OK"; - if(deviceStatus["em"] == "NOK") - { - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: EM status is NOK"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: EM status is NOK"); - - status = "NOK"; + for (const [key, value] of Object.entries(deviceStatus)) { + if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; } - if(deviceStatus["twilight_sensor"] == "NOK") - { - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: twilight_sensor is NOK"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: twilight sensor is NOK"); - - status = "NOK"; - } - - //ak teplomer NOK, rvo nok - if(deviceStatus["temperature"] == "NOK") - { - - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: temperature status is NOK"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: temperature status is NOK"); - - status = "NOK"; - } - if(status == "OK") { let pinIndexes = [1, 4, 6]; if(controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05']; - //console.log('-------- previousValues', previousValues); - for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { - if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue; - - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type); - status = "NOK"; - break; } } } // battery status. If value is 1 - battery is NOK - if (previousValues[5] === 1) - { - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: NOK status generated by battery"); + if (previousValues[5] === 1) status = "NOK"; + if(!FLOW.OMS_masterNodeIsResponding) status = "NOK"; + if(FLOW.OMS_no_voltage.size > 0) status = "NOK"; - status = "NOK"; - } - - //console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding); - - if(!FLOW.OMS_masterNodeIsResponding) - { - //errLogger.error("Master node is not responding"); - errorHandler.sendMessageToService("Master node is not responding"); - status = "NOK"; - - deviceStatus["master_node"] = "NOK"; - } - else deviceStatus["master_node"] = "OK"; - - //console.log("checkFinalRVOStatus", status); - if(FLOW.OMS_no_voltage.size > 0) - { - let writeToFile = errorHandler.processMessage("no voltage detected"); - if(writeToFile) errLogger.error("no voltage detected", FLOW.OMS_no_voltage); - - status = "NOK"; - - deviceStatus["no_voltage"] = "NOK"; - } - else deviceStatus["no_voltage"] = "OK"; - - if(status == "NOK") - { - sendTelemetry({status: "NOK"}, FLOW.OMS_rvo_tbname); - return false; - } - - return true; + return status; } @@ -1179,8 +1113,8 @@ exports.install = function(instance) { // we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1] const switchLogic = (...args) => { - let values = {status: "OK"}; - let dataToTb, pinIndex, newPinValue, twilight; + let values = {}; + let pinIndex, newPinValue, twilight; //data from rsPort if(args.length == 1) @@ -1201,6 +1135,7 @@ exports.install = function(instance) { if(obj == undefined) { previousValues[pinIndex] = newPinValue; + logger.debug("dido-switchLogic ==> no pinIndex", pinIndex); return; } @@ -1216,22 +1151,22 @@ exports.install = function(instance) { //Hlavný istič //! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri //! takze ked pride z evoku signal pre 'input1_05', handlujeme ho ako 'door_condition' - if(type === "!!!state_of_main_switch") - { - if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) - { - sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); - values["status"] = "NOK"; + // if(type === "!!!state_of_main_switch") + // { + // if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) + // { + // sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); + // values["status"] = "NOK"; - deviceStatus["state_of_main_switch"] = "Off"; - } - else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) - { - sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); + // deviceStatus["state_of_main_switch"] = "Off"; + // } + // else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) + // { + // sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); - deviceStatus["state_of_main_switch"] = "On"; - } - } + // deviceStatus["state_of_main_switch"] = "On"; + // } + // } //Prevádzkový mód if(type == "rotary_switch_state") @@ -1271,8 +1206,8 @@ exports.install = function(instance) { //automatic - profilu pre nody sa vykonavaju //ak je spracovany, a automatic - tak ho zapnem //ak nie je spracovany, iba profil zapisem - - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value}); + + if(pin2 != undefined && pin3 != undefined) instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value}); //console.log("rotary_switch_state pin", pin2, pin3, value); } @@ -1283,7 +1218,6 @@ exports.install = function(instance) { { //sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", SEND_TO.tb, instance); sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply"); - values["status"] = "NOK"; deviceStatus["power_supply"] = "NOK"; } @@ -1302,7 +1236,6 @@ exports.install = function(instance) { { //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", SEND_TO.tb, instance); sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level"); - values["status"] = "NOK"; deviceStatus["battery"] = "NOK"; } @@ -1320,13 +1253,6 @@ exports.install = function(instance) { else if(type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; - - if (newPinValue != previousValues[pinIndex]) - { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, `RVO door ${value}`, "", SEND_TO.tb, instance, "rvo_door"); - //TODO ? sendNotification("switchLogic", edgeName, "door_value", {value: value}, "", SEND_TO.tb, instance, "rvo_door"); - } - if (value === "open" && FLOW.OMS_maintenance_mode) { sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", SEND_TO.tb, instance, "rvo_door"); @@ -1336,9 +1262,6 @@ exports.install = function(instance) { { //sendNotification("switchLogic", edgeName, ERRWEIGHT.WARNING, "RVO open door out of maintenance mode", "", SEND_TO.tb, instance); sendNotification("switchLogic", edgeName, "door_has_been_open_without_permision_alarm_is_on", {}, "", SEND_TO.tb, instance, "rvo_door"); - values["status"] = "NOK"; - - //console.log(door_has_been_open_without_permision_alarm_is_on); // zapneme sirenu // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition") @@ -1353,8 +1276,7 @@ exports.install = function(instance) { sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", SEND_TO.tb, instance, "rvo_door"); } - deviceStatus["door_condition"] = value; - + deviceStatus[type] = value; } //lux sensor else if(type == "twilight_sensor") @@ -1362,7 +1284,6 @@ exports.install = function(instance) { //! TODO - to show nok status, if lux value is not changing more then 10 times. //Daylight is far more than 1000. So most of the day, when it is sunshine comes just value 1000. But lux sensor is not NOK. //This is not the case in LM. If value from LM is the same 10x, there is 99% possibility, that sensor is NOK. - values["status"] = "OK"; value = newPinValue; if(controller_type === 'lm') @@ -1387,7 +1308,6 @@ exports.install = function(instance) { if(set.size === 1 && !twilightError) { twilightError = true; - values["status"] = "NOK"; let value = twilight_sensor_array.shift(); //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, SEND_TO.tb, instance ); newPinValue = 0; @@ -1401,13 +1321,11 @@ exports.install = function(instance) { } else if (set.size === 1 && twilightError) { - values["status"] = "NOK"; twilight_sensor_array.shift(); newPinValue = 0; } } - let diff = twilight_sensor[ twilight_sensor.length - 1 ].timestamp - twilight_sensor[0].timestamp; if(diff >= twilight_sensor_interval * 60 * 1000) { @@ -1424,7 +1342,6 @@ exports.install = function(instance) { else if(type == "state_of_contactor") { //sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance ); - if(!(deviceStatus["state_of_contactor"][line] == value)) { sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", SEND_TO.tb, instance ); @@ -1465,8 +1382,7 @@ exports.install = function(instance) { }); }); } - - if(type === "state_of_breaker") + else if(type === "state_of_breaker") { let valueChanged = false; @@ -1528,66 +1444,20 @@ exports.install = function(instance) { reportLineStatus(line); } + else return; values[type] = value; - - //if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode(); - if(pinsData.hasOwnProperty(pinIndex)) + if(type == "rotary_switch_state") { - let valueChanged = false; - if(newPinValue != previousValues[pinIndex]) - { - valueChanged = true; - //pin was changed - previousValues[pinIndex] = newPinValue; - } - - let result = checkFinalRVOStatus(); - if(!result && line == 0) - { - values["status"] = "NOK"; - } - - if(type == "state_of_contactor") valueChanged = true; - if(type == "rotary_switch_state") valueChanged = true; - if(type === "state_of_breaker") - { - //console.log(type, values, valueChanged); - } - - if(FLOW.OMS_rvo_tbname == "") - { - console.log("FLOW.OMS_rvo_tbname is EMPTY"); - } - - if(FLOW.OMS_rvo_tbname == tbname) - { - values["statecode"] = calculateStateCode(); - //console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname); - } - - if(valueChanged) - { - sendTelemetry(values, tbname); - } - - if(type == "rotary_switch_state") - { - if(FLOW.OMS_maintenance_mode) value = "maintenance"; - value = value.toLowerCase(); - - let values = {}; - values["power_mode"] = value; - - sendTelemetry(values, tbname); - } - } - else - { - logger.debug("no pinIndex", pinsData[pinIndex], pinsData); + if(FLOW.OMS_maintenance_mode) value = "maintenance"; + value = value.toLowerCase(); + values["power_mode"] = value; } + if(newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue; + if(FLOW.OMS_rvo_tbname == tbname) sendRvoStatus(); + if(Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname); } @@ -1607,15 +1477,20 @@ exports.install = function(instance) { function writeThermometerStatusToDb(status) { - dbStatus.modify({ thermometer: status }).make(function(builder) { - builder.callback(function(err, response) { - if(!err) - { - deviceStatus["temperature"] = status; - console.log(`Wrote to db status: thermometer ${status}`); - } - }); - }); + return new Promise(function(resolve, reject) { + + dbStatus.modify({ thermometer: status }).make(function(builder) { + builder.callback(function(err, response) { + if(!err) + { + deviceStatus["temperature"] = status; + console.log(`Wrote to db status: thermometer ${status}`); + resolve("ok") + } + reject("nok") + }); + }); + }) } @@ -1902,25 +1777,52 @@ exports.install = function(instance) { //! pins_data --> from UNIPI // { -// '16': { pin: '16', type: 'twilight_sensor', line: 0 }, -// al_mswitch: { pin: 'al_mswitch', type: 'state_of_main_switch', line: 0 }, -// al_rswitch1: { pin: 'al_rswitch1', type: 'rotary_switch_state', line: 0 }, -// al_rswitch2: { pin: 'al_rswitch2', type: 'rotary_switch_state', line: 0 }, -// al_power: { pin: 'al_power', type: 'power_supply', line: 0 }, -// al_battery: { pin: 'al_battery', type: 'battery', line: 0 }, -// al_door: { pin: 'al_door', type: 'door_condition', line: 0 }, -// al_breaker1: { pin: 'al_breaker1', type: 'state_of_breaker', line: 1 }, -// al_breaker2: { pin: 'al_breaker2', type: 'state_of_breaker', line: 2 }, -// al_breaker3: { pin: 'al_breaker3', type: 'state_of_breaker', line: 3 }, -// al_breaker4: { pin: 'al_breaker4', type: 'state_of_breaker', line: 4 }, -// al_relay_1: { pin: 'al_relay_1', type: 'state_of_contactor', line: 1 }, -// al_relay_2: { pin: 'al_relay_2', type: 'state_of_contactor', line: 2 }, -// al_relay_3: { pin: 'al_relay_3', type: 'state_of_contactor', line: 3 }, -// al_relay_4: { pin: 'al_relay_4', type: 'state_of_contactor', line: 4 }, -// '28744F7791180257': { pin: '28744F7791180257', type: 'temperature', line: 0 } +// input1_01: { +// pin: 'input1_01', +// type: 'door_condition', +// line: 0, +// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' +// }, +// input1_02: { +// pin: 'input1_02', +// type: 'rotary_switch_state', +// line: 0, +// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' +// }, +// input1_03: { +// pin: 'input1_03', +// type: 'rotary_switch_state', +// line: 0, +// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' +// }, +// input1_04: { +// pin: 'input1_04', +// type: 'power_supply', +// line: 0, +// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' +// }, +// input1_05: { +// pin: 'input1_05', +// type: 'state_of_main_switch', +// line: 0, +// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' +// }, +// input1_06: { +// pin: 'input1_06', +// type: 'state_of_breaker', +// line: 1, +// tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo' +// }, +// relay1_02: { +// pin: 'relay1_02', +// type: 'state_of_contactor', +// line: 1, +// tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo' +// }, +// '28F46E9D0E00008B': { pin: '28F46E9D0E00008B', type: 'temperature', line: 0 }, +// twilight_sensor: { pin: 'twilight_sensor', type: 'twilight_sensor', line: 0 } // } - //! relays_data // { // '0': { diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index cf29b82..06dd438 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -1,5 +1,5 @@ -class DataToTbHandler -{ +class DataToTbHandler { + constructor(index) { this.index = index; @@ -13,27 +13,24 @@ class DataToTbHandler this.sender = ""; } - dump() - { + dump() { console.log("----------------------------"); console.log("previousValues", this.previousValues); console.log("----------------------------"); } - setSender(sender) - { + setSender(sender) { this.sender = sender; } - isEmptyObject( obj ) { - for ( var name in obj ) { - return false; + isEmptyObject(obj) { + for (var name in obj) { + return false; } return true; } - sendToTb(dataToTb, instance) - { + sendToTb(dataToTb, instance) { if(!FLOW.OMS_brokerready) { @@ -44,11 +41,10 @@ class DataToTbHandler if(keys.length == 0) { - if(this.debug) console.log("sendToTb received epty object", dataToTb); + if(this.debug) console.log("sendToTb received empty object", dataToTb); return; } - let tbname = keys[0]; let ts; @@ -67,7 +63,7 @@ class DataToTbHandler if(!this.isEmptyObject(values)) { - arrayOfValuesToSend.push({ts: ts, values: values}); + arrayOfValuesToSend.push({ts: ts, values: values}); } } @@ -100,15 +96,14 @@ class DataToTbHandler instance.send(this.index, dataToTbModified); } - getDiffTimestamp(key) - { + getDiffTimestamp(key) { //TODO set different value for given key!!! //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h return this.timeToHoldTbValue * 1000; } - prepareValuesForTb(tbname, timestamp, values) - { + prepareValuesForTb(tbname, timestamp, values) { + let keys = Object.keys(values); if(!this.previousValues.hasOwnProperty(tbname)) { diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index 3bee223..e253bb2 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -173,9 +173,9 @@ exports.install = function(instance) { numberOfNotResponding[obj.device] += 1; } - console.error(require('util').inspect(arguments, { - depth: null - })) + // console.error(require('util').inspect(arguments, { + // depth: null + // })) // if reading out of device's last register returns error, we send accumulated allValues to dido_controller (if allValues are not an empty object) if(obj.index + 1 >= obj.lengthOfActualDeviceStream) diff --git a/flow/thermometer.js b/flow/thermometer.js index 9b51fac..b5ee420 100644 --- a/flow/thermometer.js +++ b/flow/thermometer.js @@ -213,12 +213,11 @@ exports.install = function(instance) { setTimeout(function(){ start(); - }, 15000); + }, 10000); startRead = setInterval(start, timeoutMin * 1000 * 60); //testing //setInterval(() => {instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"})}, 180000); -}; - +}; \ No newline at end of file diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index 3064de1..20f64fa 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -327,7 +327,7 @@ exports.install = function(instance) { else { - if(logger) logger.debug("Broker unavailable. Data not sent !", data.data); + if(logger) logger.debug("Broker unavailable. Data not sent !", JSON.stringify(data.data)); instance.send(instanceSendTo.debug, {"message":"Broker unavailable. Data not sent !", "data": data.data }); if(saveTelemetryOnError) From 1c131d0b63d6289602b9859c7a1c02d2069d4954 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 23 Sep 2024 17:57:23 +0200 Subject: [PATCH 04/25] loadRelaysData function removed from rsPort, DataToTbHandler-send data if broker not connected --- flow/cmd_manager.js | 56 +++++++++------------------------- flow/dido_controller.js | 4 +-- flow/helper/DataToTbHandler.js | 4 +-- flow/modbus_reader.js | 2 +- 4 files changed, 19 insertions(+), 47 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 613d1f6..43ffbcf 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -275,17 +275,19 @@ function processNodeProfile(node) return; } - let profile = nodeObj.profile; - logger.debug("processNodeProfile: start - set profile for ", node, profile); - let nodeProfile; - try { - nodeProfile = JSON.parse(profile); - if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined"); - } catch (error) { - logger.debug("Error parsing node profile", error); - } + let nodeProfile = nodeObj.profile; + if(nodeProfile) { + + try { + nodeProfile = JSON.parse(nodeProfile); + if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined"); + } catch (error) { + logger.debug("Error parsing node profile", error); + } + + } logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); @@ -293,7 +295,7 @@ function processNodeProfile(node) removeTask({type: "set_node_profile", address: node}); - if(nodeProfile === undefined) + if(nodeProfile === "") { //vypneme profil nodu, posleme cmd //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. @@ -1709,33 +1711,6 @@ exports.install = function(instance) { { tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - //select nespracovane nody - //node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean - - //buildTasks({processLineProfiles: true, line: line}); - - /* - let keys = Object.keys(nodesData); - for(let i = 0; i < keys.length; i++) - { - let node = keys[i]; - let line = node.line; - - if(node.processed) continue; - - if(relaysData[line] != undefined) - { - let relayStatus = relaysData[line].contactor; - if(relayStatus == 1) - { - //linia je zapnuta - //await loadRelaysData(flowdata.data.line); - } - } - - } - */ - //vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov loadRelaysData(); @@ -2143,7 +2118,7 @@ exports.install = function(instance) { let register = params.register; let type = params.type; let tbName = params.tbname; - tbName && (tbName = nodesData[node].tbname); + if(!tbName) return; let values = {}; @@ -2178,7 +2153,7 @@ exports.install = function(instance) { } // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); - if(saveToTb && Object.keys(values).length > 0 && tbName) + if(saveToTb && Object.keys(values).length > 0) { let dataToTb = { @@ -2318,7 +2293,7 @@ exports.install = function(instance) { logger.debug("CMD manager - rsPort opened sucess"); - loadRelaysData(); + //loadRelaysData(); await runSyncExec(`stty -F /dev/${FLOW.OMS_serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) { instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); @@ -2353,7 +2328,6 @@ exports.install = function(instance) { rsPort.close(); }); - //loadRelaysData(); rsPort.open(); instance.on("close", () => { diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 9e61344..1e8010e 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -235,7 +235,7 @@ exports.install = function(instance) { let responseStatus = await promisifyBuilder(dbStatus.find()); statusData = responseStatus[0]; // {thermometer: 'OK', em: 'OK', twilight_sensor: 'OK'} deviceStatus["temperature"] = statusData.thermometer; - + FLOW.OMS_rvo_tbname = relaysData[0].tbname; if(controller_type === "lm") @@ -1367,7 +1367,6 @@ exports.install = function(instance) { //ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor - setTimeout(function(){ instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); }, time); @@ -1439,7 +1438,6 @@ exports.install = function(instance) { } if(value == "Off") values["status"] = "NOK"; - deviceStatus["state_of_breaker"][line] = value; reportLineStatus(line); diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 06dd438..6f532a3 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -34,7 +34,8 @@ class DataToTbHandler { if(!FLOW.OMS_brokerready) { - return dataToTb; + instance.send(this.index, dataToTb); + return; } let keys = Object.keys(dataToTb); @@ -92,7 +93,6 @@ class DataToTbHandler { //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); //if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); - instance.send(this.index, dataToTbModified); } diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index e253bb2..24a9580 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -146,7 +146,7 @@ exports.install = function(instance) { }).catch (function () { - console.log("errors pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute); + //console.log("errors pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute); obj.errors++; if(obj.errors == obj.lengthOfActualDeviceStream) From ed0fe5b15d48a948d6ef3256495e56b59900c247 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Wed, 2 Oct 2024 18:21:31 +0200 Subject: [PATCH 05/25] ReportOfflineNodeStatus-add setTimeout, Handle broadcast cmd to rsPort --- flow/cmd_manager.js | 55 ++++++------ flow/dido_controller.js | 2 +- flow/helper/DataToTbHandler.js | 6 -- flow/helper/serialport_helper.js | 139 ++++++++++++++++--------------- 4 files changed, 101 insertions(+), 101 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 43ffbcf..63c1a1f 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -8,7 +8,6 @@ exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; //blue - send message to relays exports.input = 2; -exports.author = 'Daniel Segeš'; exports.icon = 'cloud-upload'; //exports.npm = ['serialport' , 'child_process']; @@ -140,7 +139,6 @@ let nodesData = {};//key is node, value data from db //helper container for counting resolved group of commands (commands related to set profile) let cmdCounter = {};//key is node, value is counter -let cmdNOKNodeCounter = {};//key is node, value is counter //END OF VARIABLE SETTINGS //-------------------------------- @@ -275,16 +273,14 @@ function processNodeProfile(node) return; } - logger.debug("processNodeProfile: start - set profile for ", node, profile); - let nodeProfile = nodeObj.profile; + logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); if(nodeProfile) { try { nodeProfile = JSON.parse(nodeProfile); - if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined"); } catch (error) { - logger.debug("Error parsing node profile", error); + logger.debug("Cmd_manager - Error parsing node profile", error); } } @@ -894,29 +890,33 @@ exports.install = function(instance) { values["current"] = 0;//prúd values["status"] = "OFFLINE"; - for (let k in nodesData) { + // it happens, that some data did not get to tb after sending + // we setTimeout to make more time for db to process telemetry (eg 150 messages at once) + Object.keys(nodesData).forEach((node, index) => { + + setTimeout(function() { - //potrebujem nody k danej linii - if(line == nodesData[k].line || line == undefined) - { - let tbname = nodesData[k].tbname; - - //logger.debug("node:", tbname); - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values + //potrebujem nody k danej linii + if(line == nodesData[node].line || line == undefined) + { + let tbname = nodesData[node].tbname; + + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] } - ] - } - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - } - } + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + } + },(index+1) * 300); + }) + } @@ -1784,7 +1784,6 @@ exports.install = function(instance) { //toto sa reportuje po prijati dat z dido_controlera if(disconnected) { - let values = {"status": "OFFLINE"}; logger.debug("disconnected", values); @@ -1941,7 +1940,7 @@ exports.install = function(instance) { let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); let readBytes = 11; - let timeout = 5000; + let timeout = 4000; // await keyword is important, otherwise incorrect data is returned! await writeData(rsPort, resp, readBytes, timeout).then(function (data) { @@ -2170,6 +2169,7 @@ exports.install = function(instance) { } + /** * function handles requests from terminal * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. @@ -2401,7 +2401,6 @@ exports.install = function(instance) { if(flowdata.data.hasOwnProperty("cmd")) { let cmd = flowdata.data.cmd; - if(cmd == "buildTasks") { diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 1e8010e..24f3269 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -235,7 +235,7 @@ exports.install = function(instance) { let responseStatus = await promisifyBuilder(dbStatus.find()); statusData = responseStatus[0]; // {thermometer: 'OK', em: 'OK', twilight_sensor: 'OK'} deviceStatus["temperature"] = statusData.thermometer; - + FLOW.OMS_rvo_tbname = relaysData[0].tbname; if(controller_type === "lm") diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 6f532a3..f60c296 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -32,12 +32,6 @@ class DataToTbHandler { sendToTb(dataToTb, instance) { - if(!FLOW.OMS_brokerready) - { - instance.send(this.index, dataToTb); - return; - } - let keys = Object.keys(dataToTb); if(keys.length == 0) diff --git a/flow/helper/serialport_helper.js b/flow/helper/serialport_helper.js index a84ab84..98aa235 100644 --- a/flow/helper/serialport_helper.js +++ b/flow/helper/serialport_helper.js @@ -1,94 +1,101 @@ const { exec } = require('child_process'); function openPort(port){ - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { - var callbackError = function(err) { - port.removeListener('error', callbackError); - port.removeListener('open', callbackError); + var callbackError = function(err) { + port.removeListener('error', callbackError); + port.removeListener('open', callbackError); - reject(err.message); - }; + reject(err.message); + }; - var callbackOpen = function(data) { - port.removeListener('error', callbackError); - port.removeListener('open', callbackOpen); + var callbackOpen = function(data) { + port.removeListener('error', callbackError); + port.removeListener('open', callbackOpen); - resolve("port open: ok"); - }; + resolve("port open: ok"); + }; - port.on('error', callbackError); - port.on('open', callbackOpen); + port.on('error', callbackError); + port.on('open', callbackOpen); - port.open(); + port.open(); - }) - } + }) +} - function runSyncExec(command){ - return new Promise((resolve, reject) => { - - exec(command, (error, stdout, stderr) => { - if(error == null) resolve(stdout); - reject(error); - }); - - }) - } +function runSyncExec(command){ + return new Promise((resolve, reject) => { - async function writeData(port, data, readbytes, timeout){ - return new Promise((resolve, reject) => { + exec(command, (error, stdout, stderr) => { + if(error == null) resolve(stdout); + reject(error); + }); - //readbytes = 0 = broadcast - if(readbytes == undefined) readbytes = 0; - if(timeout == undefined) timeout = 10000;//10s, default timeout MASTERA je 3s + }) +} - //cmd-manager mame http route POST / terminal a tomu sa tiez nastavuje timeout!!! - - var callback = function(data) { - rsPortReceivedData.push(...data); - let l = rsPortReceivedData.length; +async function writeData(port, data, readbytes, timeout){ + return new Promise((resolve, reject) => { - if(l >= readbytes) - { - port.removeListener('data', callback); - - clearTimeout(t); - resolve(rsPortReceivedData); - } - }; - - port.removeListener('data', callback); - - let t = setTimeout(() => { - port.removeListener('data', callback); - - console.log("serialport helper: writeData TIMEOUT READING", rsPortReceivedData); - - reject("TIMEOUT READING"); - }, timeout); - - let rsPortReceivedData = []; - - if(readbytes > 0) port.on('data', callback); + // If first item in data array is 255, we just write broadcast command to rsPort + // We wait 3 seconds and resolve([ "b", "r", "o", "a", "d", "c", "a", "s", "t" ]) + // It is important to resolve with array + if(data[0] == 255) { port.write(Buffer.from(data), function(err) { if (err) { port.removeListener('data', callback); reject(err.message); } - - if(readbytes == 0) - { - resolve(rsPortReceivedData); - } - }); - }) + + setTimeout(resolve, 3000, [ "b", "r", "o", "a", "d", "c", "a", "s", "t" ]); + return; + } + + //cmd-manager mame http route POST / terminal a tomu sa tiez nastavuje timeout!!! + + var callback = function(data) { + rsPortReceivedData.push(...data); + let l = rsPortReceivedData.length; + + if(l >= readbytes) + { + port.removeListener('data', callback); + + clearTimeout(t); + resolve(rsPortReceivedData); + } + }; + + port.removeListener('data', callback); + + let t = setTimeout(() => { + port.removeListener('data', callback); + + console.log("serialport helper: writeData TIMEOUT READING", rsPortReceivedData); + + reject("TIMEOUT READING"); + }, timeout); + + let rsPortReceivedData = []; + + port.on('data', callback); + + port.write(Buffer.from(data), function(err) { + if (err) { + port.removeListener('data', callback); + reject(err.message); + } + + }); + }) } module.exports = { openPort, runSyncExec, writeData -} \ No newline at end of file +} From f63ac50497c519af91e6211e358023bf9e323e5a Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 2 Dec 2024 16:57:26 +0100 Subject: [PATCH 06/25] Send to rado platform; show_dbdata, db loads after start --- databases/notifications.table | 8 +- databases/settings.table | 4 +- databases/tbdatacloud.nosql | 0 flow/cloudmqttconnect.js | 404 +++ flow/cmd_manager.js | 2109 +++++++-------- flow/cmd_manager131.js | 3603 ++++++++++++++++++++++++++ flow/db_init.js | 108 + flow/designer.json | 886 +++++-- flow/dido_controller.js | 605 ++--- flow/helper/DataToTbHandler.js | 16 - flow/helper/ErrorToServiceHandler.js | 2 +- flow/helper/error_reporter.js | 72 - flow/helper/error_reporting.js | 56 - flow/helper/logger.js | 30 + flow/helper/notification_reporter.js | 26 +- flow/helper/serialport_helper.js | 5 +- flow/infosender.js | 30 +- flow/modbus_reader.js | 38 +- flow/show_dbdata.js | 241 ++ flow/slack_filter.js | 12 +- flow/thermometer.js | 159 +- flow/wsmqttpublish.js | 245 +- 22 files changed, 6380 insertions(+), 2279 deletions(-) create mode 100644 databases/tbdatacloud.nosql create mode 100644 flow/cloudmqttconnect.js create mode 100644 flow/cmd_manager131.js create mode 100644 flow/db_init.js delete mode 100644 flow/helper/error_reporter.js delete mode 100644 flow/helper/error_reporting.js create mode 100644 flow/helper/logger.js create mode 100644 flow/show_dbdata.js diff --git a/databases/notifications.table b/databases/notifications.table index a0590d3..77f9e7a 100644 --- a/databases/notifications.table +++ b/databases/notifications.table @@ -20,9 +20,9 @@ key:string|weight:string|sk:string|en:string +|power_supply_works_correctly|NOTICE|Napájací zdroj pracuje správne|Power supply works correctly|............... +|battery_level_is_low|ERROR|Batéria má nízku úroveň napätia|Battery level is low|............... +|battery_level_is_ok|NOTICE|Batéria má správnu úroveň napätia|Battery level is OK|............... -+|door_has_been_open|NOTICE|Dvere boli otvorené|Door has been open|............... -+|door_has_been_closed|NOTICE|Dvere boli zatvorené|Door has been closed|............... -+|door_has_been_open_without_permision_alarm_is_on|WARNING|Dvere boli otvorené bez povolania - zapnutá siréna|Door has been open without permision - alarm is on|............... ++|door_opened|NOTICE|Dvere boli otvorené|Door has been opeed|............... ++|door_closed|NOTICE|Dvere boli zatvorené|Door has been closed|............... ++|door_opened_without_permission|WARNING|Dvere boli otvorené bez povoeania - zapnutá siréna|Door has been oed without permision - alarm is on|............... +|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|............... +|local_database_is_corrupted|CRITICAL|||............... +|electrometer_nok|ERROR|Elektromer neodpovedá|Electrometer is not responding|............... @@ -34,4 +34,4 @@ key:string|weight:string|sk:string|en:string +|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|............... +|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|............... +|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|............... -+|flow_restart|NOTICE|Restart flowu|Flow has been restarted|............... ++|flow_restart|NOTICE|Restart flowu|Flow has been restarted|............... \ No newline at end of file diff --git a/databases/settings.table b/databases/settings.table index a182df5..98ae003 100644 --- a/databases/settings.table +++ b/databases/settings.table @@ -1,2 +1,2 @@ -rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number -+|rvo_senica_39_10.0.0.132|en|28.427B45920702|48.70826502|17.28455203|192.168.252.1|rvo_senica_39_10.0.0.132|qzSNuCNrLP4OL1v47YEe|1883|0|68|unipi|ttyUSB0|1|20|5|........................................... +rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number ++|rvo_senica_42_10.0.0.118|en|28.427B45920702|48.70826502|17.28455203|192.168.252.1|rvo_senica_42_10.0.0.118|QMvF7etEvbYMMr8Q6baP|1883|0|59|unipi|ttyUSB0|1|20|5|6|3|.............. diff --git a/databases/tbdatacloud.nosql b/databases/tbdatacloud.nosql new file mode 100644 index 0000000..e69de29 diff --git a/flow/cloudmqttconnect.js b/flow/cloudmqttconnect.js new file mode 100644 index 0000000..79bc04e --- /dev/null +++ b/flow/cloudmqttconnect.js @@ -0,0 +1,404 @@ +exports.id = 'cloudmqttconnect'; +exports.title = 'Cloud connect mqtt'; +exports.group = 'MQTT'; +exports.color = '#888600'; +exports.version = '1.0.2'; +exports.icon = 'sign-out'; +exports.input = 1; +exports.output = 2; +exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" }; + +exports.html = `
+
+
+
Hostname or IP address (if not empty - setting will override db setting)
+
+
+
Port
+
+
+
+
+
@(Client id)
+
+
+
topic
+
+
+
`; + + + +const { promisifyBuilder } = require('./helper/db_helper'); +const fs = require('fs'); +const mqtt = require('mqtt'); + +const SEND_TO = { + debug: 0, + rpcCall: 1, +} + +//CONFIG +let saveTelemetryOnError = true;//backup_on_failure overrides this value +//------------------------ + +const noSqlFileSizeLimit = 4194304;//use 5MB - 4194304 +let insertNoSqlCounter = 0; +let insertBackupNoSqlCounter = 0; +let processingData = false; + +let backup_on_failure = true;//== saveTelemetryOnError - create backup client send failure +let restore_from_backup = 50; //how many rows process at once? +let restore_backup_wait = 3;//wait seconds +let lastRestoreTime = 0; + +// if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable +let sendClientError = true; + + +const nosql = NOSQL('tbdatacloud'); + + +exports.install = function(instance) { + + var client; + var opts; + var clientReady = false; + + let o = null; //options + + function main() + { + loadSettings(); + } + + //set opts according to db settings + function loadSettings() + { + + o = instance.options; + opts = { + host: o.host, + port: o.port, + clientId: o.clientid, + username: o.username, + rejectUnauthorized: false, + resubscribe: false + }; + + + console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); + + connectToTbServer(); + } + + function connectToTbServer() + { + var url = "mqtt://" + opts.host + ":" + opts.port; + console.log("MQTT URL: ", url); + + client = mqtt.connect(url, opts); + + client.on('connect', function() { + client.subscribe(`${o.topic}_backward`, (err) => { + if (!err) { + console.log("MQTT subscribed"); + } + }); + instance.status("Connected", "green"); + clientReady = true; + sendClientError = true; + }); + + client.on('reconnect', function() { + client.subscribe(`${o.topic}_backward`, (err) => { + if (!err) { + console.log("MQTT subscribed"); + } + }); + instance.status("Reconnecting", "yellow"); + clientReady = false; + }); + + client.on('message', function(topic, message) { + // message is type of buffer + message = message.toString(); + if (message[0] === '{') { + TRY(function() { + + message = JSON.parse(message); + if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { + client.publish(`${o.topic}_forward`, `{"device": "${message.device}", "id": ${message.data.id}, "data": {"success": true}}`, {qos:1}); + instance.send(SEND_TO.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}}); + } + + }, () => instance.debug('MQTT: Error parsing data', message)); + } + + instance.send(SEND_TO.rpcCall, {"topic":o.topic, "content":message }); + }); + + client.on('close', function(err) { + clientReady = false; + + if (err && err.toString().indexOf('Error')) { + instance.status("Err: "+err.code, "red"); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); + } else { + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); + } + + client.reconnect(); + }); + + client.on('error', function(err) { + instance.status("Err: "+ err.code, "red"); + instance.send(SEND_TO.debug, {"message":"Client ERROR signal received !", "error":err, "opt":opts }); + if(sendClientError) { + console.log('MQTT client error', err); + sendClientError = false; + } + clientReady = false; + }); + + } + + + instance.on('0', function(data) { + + if(clientReady) + { + //do we have some data in backup file? + //if any, process data from database + if(saveTelemetryOnError) + { + //read telemetry data and send back to server + if(!processingData) processDataFromDatabase(); + } + } + + if(clientReady) + { + client.publish(`${o.topic}_forward`, data.data, {qos: 1}); + //console.log("ondata..",data.data) + + + // Pokad ten error na 38 chápem tak tento parameter MUSÍ byt string... + // Tak to musim dekódovat na strane Cloudu zas + + } + else + { + + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); + + if(saveTelemetryOnError) + { + try { + let d = JSON.parse(data.data); + //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql + makeBackupFromDbFile(); + + //write to tb + d.id = UID(); + nosql.insert(d); + } catch(e) { + console.log("cloudconnect - unable to parse data from wsmqtt"); + } + } + + } + }); + + + instance.close = function(done) { + if(clientReady){ + client.end(); + } + }; + + + function getDbBackupFileCounter(type) + { + var files = fs.readdirSync(__dirname + "/../databases"); + + let counter = 0; + for(var i = 0; i < files.length; i++) + { + + if(files[i] == "tbdatacloud.nosql") continue; + + if(files[i].endsWith(".nosql")) + { + + let pos = files[i].indexOf("."); + if(pos > -1) + { + + let fileCounter = counter; + let firstDigit = files[i].slice(0, pos); + + fileCounter = parseInt(firstDigit); + if(isNaN(fileCounter)) fileCounter = 0; + //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); + + if(type == "max") + { + if(fileCounter > counter) + { + counter = fileCounter; + } + } + else if(type == "min") + { + if(counter == 0) counter = fileCounter; + + if(fileCounter < counter) + { + counter = fileCounter; + } + } + } + } + + } + + if(type == "max") counter++; + + return counter; + } + + const makeBackupFromDbFile = async () => { + + if(!saveTelemetryOnError) return; + + //to avoid large file: tbdata.nosql + + //init value is 0! + if(insertNoSqlCounter > 0) + { + --insertNoSqlCounter; + return; + } + + insertNoSqlCounter = 100; + + let source = __dirname + "/../databases/tbdatacloud.nosql"; + + var stats = fs.statSync(source); + var fileSizeInBytes = stats.size; + + if(fileSizeInBytes > noSqlFileSizeLimit) + { + + let counter = 1; + counter = getDbBackupFileCounter("max"); + + let destination = __dirname + "/../databases/" + counter + "." + "tbdatacloud.nosql"; + + //make backup file + fs.copyFileSync(source, destination); + //fs.renameSync(p, p + "." + counter); + + //clear tbdata.nosql + fs.writeFileSync(source, ""); + fs.truncateSync(source, 0); + + } + } + + const processDataFromDatabase = async () => { + + if(restore_from_backup <= 0) return; + + //calculate diff + const now = new Date(); + let currentTime = now.getTime(); + let diff = currentTime - lastRestoreTime; + + if( (diff / 1000) < restore_backup_wait) + { + //console.log("*********restore_backup_wait", diff, restore_backup_wait); + return; + } + + processingData = true; + + //get filename to process + let counter = getDbBackupFileCounter("min"); + + //we have some backup files + let dataBase = 'tbdata'; + + var nosql; + if(counter == 0) dataBase = 'tbdatacloud'; + else dataBase = counter + "." + 'tbdatacloud'; + + nosql = NOSQL(dataBase); + + //select all data - use limit restore_from_backup + let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); + + for(let i = 0; i < records.length; i++) + { + if(clientReady) { + + let item = records[i]; + let id = item.id; + + if(id !== undefined) + { + //console.log("------------processDataFromDatabase - remove", id, dataBase, i); + + try { + + let o = JSON.parse(JSON.stringify(item)); + delete o.id; + let message = JSON.stringify(o); + + client.publish(`${o.topic}_forward`, message, {qos:1}); + //console.log("db...", message) + //remove from database + await promisifyBuilder(nosql.remove().where("id", id)); + + } catch(error) { + //process error + console.log("processDataFromDatabase", error); + } + + + } + + } + else + { + processingData = false; + return; + } + } + + if(records.length > 0) + { + //clean backup file + if(counter > 0) nosql.clean(); + } + + //no data in db, remove + if(records.length == 0) + { + if(counter > 0) nosql.drop(); + } + + const d = new Date(); + lastRestoreTime = d.getTime(); + + processingData = false; + + } + + instance.on('options', main); + main(); + +}; diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 63c1a1f..d84daf3 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -3,13 +3,10 @@ exports.title = 'CMD Manager'; exports.group = 'Worksys'; exports.color = '#5D9CEC'; exports.version = '0.0.3'; -exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; - //blue - send message to relays - -exports.input = 2; +exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; +exports.input = 3; exports.icon = 'cloud-upload'; -//exports.npm = ['serialport' , 'child_process']; exports.html = `
@@ -29,669 +26,637 @@ exports.html = `
`; - exports.readme = `Manager for CMD calls`; -const SerialPort = require('serialport'); -const { exec } = require('child_process'); -const { crc8, crc16, crc32 } = require('easy-crc'); -const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js'); -const { bytesToInt, longToByteArray, addZeroBefore, isEmptyObject, convertUTCDateToLocalDate } = require('./helper/utils'); -const bitwise = require('bitwise'); - -var SunCalc = require('./helper/suncalc.js'); -const DataToTbHandler = require('./helper/DataToTbHandler.js'); -const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); -const { promisifyBuilder, makeMapFromDbResult} = require('./helper/db_helper.js'); -const { sendNotification, initNotifications, ERRWEIGHT } = require('./helper/notification_reporter.js'); - -const dbNodes = TABLE("nodes"); -const dbRelays = TABLE("relays"); -const dbSettings = TABLE("settings"); - -//https://github.com/log4js-node/log4js-node/blob/master/examples/example.js -//file: { type: 'file', filename: path.join(__dirname, 'log/file.log') } -var path = require('path'); -var log4js = require("log4js"); -const process = require('process'); - -//TODO - to remove? -// runTasks intervals -const SHORT_INTERVAL = 30; -const LONG_INTERVAL = 300; - -//send data to following instances: -const SEND_TO = { - debug: 0, - tb: 1, - http_response: 2, - dido_controller: 3, - infoSender: 4 -} - -const PRIORITY_TYPES = { - terminal: 0, - fw_detection: 1,//reserved only for FW detection - FLOW.OMS_masterNodeIsResponding - high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) - relay_profile: 3, - node_broadcast: 4, - node_profile: 5, - node_cmd: 6 -} - -const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes - -//list of command calls to process. Processing in runTasks function -let tasks = []; - -let interval = null;//timeout for procesing tasks -let customTasksInterval = null; -let refFlowdata = null;//holds reference to httprequest flowdata -let refFlowdataObj = {}; - -//load from settings -let latitude = 48.70826502;//48.682255758; -let longitude = 17.28455203;//17.278910807; - -const gmtOffset = 0; - -//ak nie je nastaveny -//https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ -//https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates - -//priorities for registers -let priorities = []; - -let minutes = 1; -priorities["0"] = minutes; -priorities["1"] = minutes; - -minutes = 5; -priorities["74"] = minutes; -priorities["75"] = minutes; -priorities["76"] = minutes; -priorities["77"] = minutes; -priorities["78"] = minutes; -priorities["79"] = minutes; -priorities["84"] = minutes; - -minutes = 10; -priorities["87"] = minutes; -priorities["6"] = minutes; -priorities["7"] = minutes; -priorities["80"] = minutes; -priorities["8"] = minutes; -priorities["3"] = minutes; -priorities["89"] = minutes; - -//prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) -let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89]; - -const errorHandler = new ErrorToServiceHandler(); - -let rotary_switch_state = "Off"; -let lux_sensor; -let state_of_breaker = {};//key is line, value is On/Off -let disconnectedReport = {};//key is tbname, value true/false - -let relaysData = {};//key is line, value is data from db -let nodesData = {};//key is node, value data from db - -//helper container for counting resolved group of commands (commands related to set profile) -let cmdCounter = {};//key is node, value is counter - -//END OF VARIABLE SETTINGS -//-------------------------------- - - -log4js.configure({ - appenders: { - errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } -}); - -const errLogger = log4js.getLogger("errLogs"); -const logger = log4js.getLogger(); -const monitor = log4js.getLogger("monitorLogs"); - -//USAGE -//logger.debug("text") -//monitor.info('info'); -//errLogger.error("some error"); - - -function cmdCounterResolve(address) -{ - if(cmdCounter.hasOwnProperty(address)) - { - cmdCounter[address] = cmdCounter[address] - 1; - - let result = cmdCounter[address]; - if(result == 0) delete cmdCounter[address]; - return result; - } - return -1; -} - - -function getParams(priority) -{ - let params = {}; - - //core rpc values - params.address = 0;//if(recipient === 0) address = 0; - params.byte1 = 0;//msb, podla dokumentacie data3 - params.byte2 = 0;//podla dokumentacie data2 - params.byte3 = 0;//podla dokumentacie data1 - params.byte4 = 0;//lsb, podla dokumentacie data0 - params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast - params.register = -1;//register number - params.rw = 0;//0: read, 1: write - - //other values - //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" - //params.tbname = tbname; - params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority - params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed - if(priority != undefined ) - { - params.timestamp = priority; - params.priority = priority; - } - - params.addMinutesToTimestamp = 0;//repeat task if value is > 0, - - //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" - //params.info = ""; - - return params; -} - - -async function loadSettings() -{ - let responseSettings = await promisifyBuilder(dbSettings.find()); - - latitude = responseSettings[0]["latitude"]; - longitude = responseSettings[0]["longitude"]; - - //globals - FLOW.OMS_language = responseSettings[0]["lang"]; - FLOW.OMS_rvo_name = responseSettings[0]["rvo_name"]; - FLOW.OMS_projects_id = responseSettings[0]["projects_id"]; - //FLOW.OMS_rvo_tbname = responseSettings[0]["tbname"]; - FLOW.OMS_temperature_adress = responseSettings[0]["temperature_adress"]; - FLOW.OMS_controller_type = responseSettings[0]["controller_type"]; - FLOW.OMS_serial_port = responseSettings[0]["serial_port"]; - FLOW.OMS_node_status_nok_time = responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000; // hour * minutes * seconds - //logger.debug('settings', responseSettings[0]); - - initNotifications(); -} - -loadSettings(); - - -async function loadNodes() -{ - const responseNodes = await promisifyBuilder(dbNodes.find()); - nodesData = makeMapFromDbResult(responseNodes, "node"); -} - -loadNodes(); - - -//nastav profil nodu -function processNodeProfile(node) -{ - if(rotary_switch_state != "Automatic") - { - logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); - return; - } - - let nodeObj = nodesData[node]; - let line = nodeObj.line; - - if(relaysData[line].contactor == 0) - { - logger.debug("line line is off", line, node); - return; - } - - if(nodeObj.processed == 1) - { - logger.debug("node was already processed", node); - return; - } - - let nodeProfile = nodeObj.profile; - logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); - if(nodeProfile) { - - try { - nodeProfile = JSON.parse(nodeProfile); - } catch (error) { - logger.debug("Cmd_manager - Error parsing node profile", error); - } - - } - - logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); - - let timestamp = PRIORITY_TYPES.node_cmd; - - removeTask({type: "set_node_profile", address: node}); - - if(nodeProfile === "") - { - //vypneme profil nodu, posleme cmd - //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. - //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia - - logger.debug("turn off profile"); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'turn off/reset node profile'; - - cmdCounter[node] = 1; - - tasks.push(params); - } - else - { - let tasksProfile = []; - - //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'turn off node profile'; - - tasksProfile.push(params); - - timestamp++; - - logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); - - //TS1 Time point a TS1 Time Point Levels - let register = 9; - for(let i = 0; i < nodeProfile.intervals.length; i++) - { - let obj = nodeProfile.intervals[i]; - //let timePoint = obj.time_point; - let dim_value = obj.value; - - - //Reg 9 až Reg 40 - - /* - Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. - Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). - Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. - Časový bod má formát: - Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované - Register úrovne má rovnaký formát ako dimming register (Reg 1). - */ - - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - let start_time = obj.start_time; - let t = start_time.split(":"); - //if(timePoint != undefined) t = timePoint.split(":"); - //else t = [0,0]; - - logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = parseInt(t[0]);//hh - params.byte2 = parseInt(t[1]);//mm - params.byte3 = 0;//ss - params.byte4 = 0;// - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0;//ss - params.byte4 = parseInt(dim_value) + 128;// - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point Levels ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - } - - //Threshold lux level for DUSK/DAWN - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - - //Time schedule settings na koniec - //if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor) - { - - logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 96; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = "Threshold lux level for DUSK/DAWN"; - - if(nodeProfile.dusk_lux_sensor) - { - let v = nodeProfile.dusk_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if(nodeProfile.dawn_lux_sensor) - { - let v = nodeProfile.dawn_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //DUSK/DAWN max. adjust period - { - - logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 97; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = "DUSK/DAWN max. adjust period"; - - if(nodeProfile.astro_clock) - { - let v = nodeProfile.dusk_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if(nodeProfile.astro_clock) - { - let v = nodeProfile.dawn_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //Static offset - { - - //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: - //Bity 0 – 6: hodnota v minútach - //Bit 7: znamienko (1 – mínus) - - logger.debug("processNodeProfile: Static offset", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 98; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = "Static offset"; - - if(nodeProfile.astro_clock) - { - let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); - let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); - - if(dusk_astro_clock_offset < 0) - { - params.byte3 = (dusk_astro_clock_offset * -1) + 128; - } - else - { - params.byte3 = dusk_astro_clock_offset; - } - - if(dawn_astro_clock_offset < 0) - { - params.byte4 = (dawn_astro_clock_offset * -1) + 128; - } - else - { - params.byte4 = dawn_astro_clock_offset; - } - } - - tasksProfile.push(params); - timestamp++; - } - - logger.debug("Time schedule settings - turn on", node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 8; - params.recipient = 1; - params.rw = 1;//write - - //Time schedule settings - let bits = []; - - //Byte 0 (LSB): - //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). - bits.push(1); - //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) - bits.push(0); - bits.push(0); - bits.push(0); - if(nodeProfile.astro_clock == true) - { - //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý - bits.push(1); - } - else bits.push(0); - - //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) - bits.push(0); - - //Bity 6-7 - zatiaľ nepoužité - bits.push(0); - bits.push(0); - - params.byte4 = bitwise.byte.write(bits.reverse()); - - //Byte 2 – nastavenie pre lux senzor: - bits = []; - - //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if(nodeProfile.dusk_lux_sensor == true)//sumrak - { - bits.push(1); - } - else bits.push(0); - - //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if(nodeProfile.dawn_lux_sensor == true)//usvit - { - bits.push(1); - } - else bits.push(0); - - //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. - bits.push(0);//zatial neimplementovane - - //Bit 3 – 7 - nepoužité - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - - params.byte2 = bitwise.byte.write(bits.reverse()); - params.timestamp = timestamp; - params.info = "Time schedule settings - turn on"; - - tasksProfile.push(params); - - //zaver - cmdCounter[node] = tasksProfile.length; - - //tasks.push(tasksProfile); - tasks = tasks.concat(tasksProfile); - - } - - logger.debug("finished set profile for ", node); - -} - - -function cleanUpRefFlowdataObj() -{ - let now = new Date(); - let timestamp = now.getTime(); - - //clear old refFlowdata references - let keys = Object.keys(refFlowdataObj); - for(let i = 0; i < keys.length; i++) - { - let timestampKey = keys[i]; - - if((timestamp - timestampKey) > 60*1000 ) - { - console.log("cleanUpRefFlowdataObj delete", timestampKey); - delete refFlowdataObj[ timestampKey ]; - } - } -} - - -function removeTask(obj) -{ - let keys = Object.keys(obj); - tasks = tasks.filter((task) => { - - let counter = 0; - for(let i = 0; i < keys.length; i++) - { - let key = keys[i]; - if(task.hasOwnProperty(key) && obj.hasOwnProperty(key)) - { - if(task[key] == obj[key]) counter++; - } - } - - if(counter == keys.length) return false; - return true; - }); -} - exports.install = function(instance) { - let now = new Date(); - console.log("CMD Manager installed", now.toLocaleString("sk-SK")); - - const tbHandler = new DataToTbHandler(SEND_TO.tb); - tbHandler.setSender(exports.title); + const SerialPort = require('serialport'); + const { exec } = require('child_process'); + const { crc16 } = require('easy-crc'); + const { runSyncExec, writeData } = require('./helper/serialport_helper'); + const { bytesToInt, longToByteArray, addZeroBefore } = require('./helper/utils'); + const bitwise = require('bitwise'); - //FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name - //const errorHandler = new ErrorToServiceHandler(instance, SEND_TO.infoSender); - errorHandler.setProjectsId(FLOW.OMS_projects_id); - //const errorHandler = new ErrorToServiceHandler(instance); - //errorHandler.sendMessageToService("ahoj", 0); + var SunCalc = require('./helper/suncalc'); + const DataToTbHandler = require('./helper/DataToTbHandler'); + const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler'); + const { sendNotification } = require('./helper/notification_reporter'); + const process = require('process'); + const { errLogger, logger, monitor } = require('./helper/logger'); - let sunCalcResult = calculateDuskDawn(); + const dbNodes = TABLE("nodes"); + const dbRelays = TABLE("relays"); - let reportDuskDawn = { - dusk_time: sunCalcResult.dusk_time, - dawn_time: sunCalcResult.dawn_time, - dusk_time_reported: undefined, - dawn_time_reported: undefined - }; + let GLOBALS; + let SETTINGS; + let rsPort; + let tbHandler; + + // runTasks intervals + const SHORT_INTERVAL = 30; + const LONG_INTERVAL = 300; + + //send data to following instances: + const SEND_TO = { + debug: 0, + tb: 1, + http_response: 2, + dido_controller: 3, + infoSender: 4 + } + + const PRIORITY_TYPES = { + terminal: 0, + fw_detection: 1,//reserved only for FW detection - SETTINGS.masterNodeIsResponding + high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) + relay_profile: 3, + node_broadcast: 4, + node_profile: 5, + node_cmd: 6 + } + + const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes + + //list of command calls to process. Processing in runTasks function + let tasks = []; + + let interval = null;//timeout for procesing tasks + let customTasksInterval = null; // interval for reportEdgeDateTimeAndNumberOfLuminaires(); + let setCorrectTime = null; // interval for setting a correct edgeTime + + let refFlowdataObj = {}; + + //load from settings + let latitude = 48.70826502;//48.682255758; + let longitude = 17.28455203;//17.278910807; + + const gmtOffset = 0; + + //ak nie je nastaveny + //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ + //https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates + + //priorities for registers + let priorities = []; + + let minutes = 1; + priorities["0"] = minutes; + priorities["1"] = minutes; + + minutes = 5; + priorities["74"] = minutes; + priorities["75"] = minutes; + priorities["76"] = minutes; + priorities["77"] = minutes; + priorities["78"] = minutes; + priorities["79"] = minutes; + priorities["84"] = minutes; + + minutes = 10; + priorities["87"] = minutes; + priorities["6"] = minutes; + priorities["7"] = minutes; + priorities["80"] = minutes; + priorities["8"] = minutes; + priorities["3"] = minutes; + priorities["89"] = minutes; + + //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) + let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89]; + + const errorHandler = new ErrorToServiceHandler(); + + let rotary_switch_state; + let lux_sensor; + let state_of_breaker = {};//key is line, value is On/Off + let disconnectedReport = {};//key is tbname, value true/false + + let relaysData; + let nodesData; + let rvoTbName; + + let sunCalcResult; + let reportDuskDawn; + + //helper container for counting resolved group of commands (commands related to set profile) + let cmdCounter = {};//key is node, value is counter + + //END OF VARIABLE SETTINGS + //-------------------------------- + + + function main() + { + GLOBALS = FLOW.GLOBALS; + SETTINGS = FLOW.GLOBALS.settings; + relaysData = GLOBALS.relaysData; + nodesData = GLOBALS.nodesData; + latitude = GLOBALS.settings.latitude; + longitude = GLOBALS.settings.longitude; + + tbHandler = new DataToTbHandler(SEND_TO.tb); + tbHandler.setSender(exports.title); + + //SETTINGS.project_id, name: SETTINGS.rvo_name; + //const errorHandler = new ErrorToServiceHandler(instance, SEND_TO.infoSender); + errorHandler.setProjectsId(SETTINGS.project_id); + //const errorHandler = new ErrorToServiceHandler(instance); + //errorHandler.sendMessageToService("ahoj", 0); + + let now = new Date(); + console.log("CMD Manager installed", now.toLocaleString("sk-SK")); + + sunCalcResult = calculateDuskDawn(); + + reportDuskDawn = { + dusk_time: sunCalcResult.dusk_time, + dawn_time: sunCalcResult.dawn_time, + dusk_time_reported: undefined, + dawn_time_reported: undefined + }; + + handleRsPort(); + + //to ensure, edgeDateTime will be send to tb at full minute + customTasksInterval = setInterval(function() { + if(new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); + }, 1000); + + setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour + setCorrectPlcTimeOnceADay(); + } + + + function cmdCounterResolve(address) + { + if(cmdCounter.hasOwnProperty(address)) + { + cmdCounter[address] = cmdCounter[address] - 1; + + let result = cmdCounter[address]; + if(result == 0) delete cmdCounter[address]; + return result; + } + return -1; + } + + + function getParams(priority) + { + let params = {}; + + //core rpc values + params.address = 0;//if(recipient === 0) address = 0; + params.byte1 = 0;//msb, podla dokumentacie data3 + params.byte2 = 0;//podla dokumentacie data2 + params.byte3 = 0;//podla dokumentacie data1 + params.byte4 = 0;//lsb, podla dokumentacie data0 + params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast + params.register = -1;//register number + params.rw = 0;//0: read, 1: write + + //other values + //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" + //params.tbname = tbname; + params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority + params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed + if(priority != undefined ) + { + params.timestamp = priority; + params.priority = priority; + } + + params.addMinutesToTimestamp = 0;//repeat task if value is > 0, + + //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" + //params.info = ""; + //params.debug = true; // will console.log params in writeData response + + return params; + } + + + //nastav profil nodu + function processNodeProfile(node) + { + if(rotary_switch_state != "Automatic") + { + logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); + return; + } + + let nodeObj = nodesData[node]; + let line = nodeObj.line; + + if(relaysData[line].contactor == 0) + { + logger.debug("line line is off", line, node); + return; + } + + if(nodeObj.processed == 1) + { + //logger.debug("node was already processed", node); + return; + } + + let nodeProfile = nodeObj.profile; + logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); + if(nodeProfile) { + + try { + nodeProfile = JSON.parse(nodeProfile); + } catch (error) { + logger.debug("Cmd_manager - Error parsing node profile", error); + } + + } + + logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); + + let timestamp = PRIORITY_TYPES.node_cmd; + + removeTask({type: "set_node_profile", address: node}); + + if(nodeProfile === "") + { + //vypneme profil nodu, posleme cmd + //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. + //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia + + logger.debug("turn off profile"); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'turn off/reset node profile'; + + cmdCounter[node] = 1; + + tasks.push(params); + } + else + { + let tasksProfile = []; + + //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'turn off node profile'; + + tasksProfile.push(params); + + timestamp++; + + logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); + + //TS1 Time point a TS1 Time Point Levels + let register = 9; + for(let i = 0; i < nodeProfile.intervals.length; i++) + { + let obj = nodeProfile.intervals[i]; + //let timePoint = obj.time_point; + let dim_value = obj.value; + + + //Reg 9 až Reg 40 + + /* + Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. + Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). + Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. + Časový bod má formát: + Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované + Register úrovne má rovnaký formát ako dimming register (Reg 1). + */ + + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //params.byte1 = 0;//msb, podla dokumentacie data3 + //params.byte2 = 0;//podla dokumentacie data2 + //params.byte3 = 0;//podla dokumentacie data1 + //params.byte4 = 0;//lsb, podla dokumentacie data0 + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + let start_time = obj.start_time; + let t = start_time.split(":"); + //if(timePoint != undefined) t = timePoint.split(":"); + //else t = [0,0]; + + logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = parseInt(t[0]);//hh + params.byte2 = parseInt(t[1]);//mm + params.byte3 = 0;//ss + params.byte4 = 0;// + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0;//ss + params.byte4 = parseInt(dim_value) + 128;// + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point Levels ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + } + + //Threshold lux level for DUSK/DAWN + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //params.byte1 = 0;//msb, podla dokumentacie data3 + //params.byte2 = 0;//podla dokumentacie data2 + //params.byte3 = 0;//podla dokumentacie data1 + //params.byte4 = 0;//lsb, podla dokumentacie data0 + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + //Time schedule settings na koniec + //if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor) + { + + logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 96; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = "Threshold lux level for DUSK/DAWN"; + + if(nodeProfile.dusk_lux_sensor) + { + let v = nodeProfile.dusk_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if(nodeProfile.dawn_lux_sensor) + { + let v = nodeProfile.dawn_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //DUSK/DAWN max. adjust period + { + + logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 97; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = "DUSK/DAWN max. adjust period"; + + if(nodeProfile.astro_clock) + { + let v = nodeProfile.dusk_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if(nodeProfile.astro_clock) + { + let v = nodeProfile.dawn_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //Static offset + { + + //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: + //Bity 0 – 6: hodnota v minútach + //Bit 7: znamienko (1 – mínus) + + logger.debug("processNodeProfile: Static offset", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 98; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = "Static offset"; + + if(nodeProfile.astro_clock) + { + let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); + let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); + + if(dusk_astro_clock_offset < 0) + { + params.byte3 = (dusk_astro_clock_offset * -1) + 128; + } + else + { + params.byte3 = dusk_astro_clock_offset; + } + + if(dawn_astro_clock_offset < 0) + { + params.byte4 = (dawn_astro_clock_offset * -1) + 128; + } + else + { + params.byte4 = dawn_astro_clock_offset; + } + } + + tasksProfile.push(params); + timestamp++; + } + + logger.debug("Time schedule settings - turn on", node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 8; + params.recipient = 1; + params.rw = 1;//write + + //Time schedule settings + let bits = []; + + //Byte 0 (LSB): + //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). + bits.push(1); + //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) + bits.push(0); + bits.push(0); + bits.push(0); + if(nodeProfile.astro_clock == true) + { + //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý + bits.push(1); + } + else bits.push(0); + + //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) + bits.push(0); + + //Bity 6-7 - zatiaľ nepoužité + bits.push(0); + bits.push(0); + + params.byte4 = bitwise.byte.write(bits.reverse()); + + //Byte 2 – nastavenie pre lux senzor: + bits = []; + + //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if(nodeProfile.dusk_lux_sensor == true)//sumrak + { + bits.push(1); + } + else bits.push(0); + + //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if(nodeProfile.dawn_lux_sensor == true)//usvit + { + bits.push(1); + } + else bits.push(0); + + //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. + bits.push(0);//zatial neimplementovane + + //Bit 3 – 7 - nepoužité + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + + params.byte2 = bitwise.byte.write(bits.reverse()); + params.timestamp = timestamp; + params.info = "Time schedule settings - turn on"; + + tasksProfile.push(params); + + //zaver + cmdCounter[node] = tasksProfile.length; + + //tasks.push(tasksProfile); + tasks = tasks.concat(tasksProfile); + + } + + logger.debug("finished set profile for ", node); + + console.log("proces profile finished *********************") + } + + + function cleanUpRefFlowdataObj() + { + let now = new Date(); + let timestamp = now.getTime(); + + //clear old refFlowdata references + let keys = Object.keys(refFlowdataObj); + for(let i = 0; i < keys.length; i++) + { + let timestampKey = keys[i]; + + if((timestamp - timestampKey) > 60*1000 ) + { + console.log("cleanUpRefFlowdataObj delete", timestampKey); + delete refFlowdataObj[ timestampKey ]; + } + } + } + + + function removeTask(obj) + { + let keys = Object.keys(obj); + tasks = tasks.filter((task) => { + + let counter = 0; + for(let i = 0; i < keys.length; i++) + { + let key = keys[i]; + if(task.hasOwnProperty(key) && obj.hasOwnProperty(key)) + { + if(task[key] == obj[key]) counter++; + } + } + + if(counter == keys.length) return false; + return true; + }); + } process.on('uncaughtException', function (err) { @@ -709,46 +674,32 @@ exports.install = function(instance) { function processAllNodeProfilesOnLine(line) { - for (let k in nodesData) { - //node:number|tbname:string|line:number|profile:string|processed:boolean - if(line == nodesData[k].line) { let node = nodesData[k].node; let processed = nodesData[k].processed; - if(!processed) - { - processNodeProfile(node); - } - else - { - logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); - } + if(!processed) processNodeProfile(node); + //else logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); } } } - async function loadRelaysData(line) { - - relaysData = await promisifyBuilder(dbRelays.find()); - relaysData = makeMapFromDbResult(relaysData, "line"); - + function loadRelaysData(line) + { for (const [key, value] of Object.entries(relaysData)) { if(key == "0") continue; if(line != undefined) { //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData - if(line != value.line ) continue; + if(line != value.line) continue; } if(value.contactor == 1) processAllNodeProfilesOnLine(value.line); } - -// console.log('.........', relaysData); } @@ -809,18 +760,7 @@ exports.install = function(instance) { nodesData[k].time_of_last_communication = time; } - let dataToTb = { - [tbname]: [ - { - ts: time, - values: { - status: status - } - } - ] - } - - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry({status: status}, tbname, time); //prud, vykon - current, input power pre liniu pre vsetky nody @@ -890,6 +830,8 @@ exports.install = function(instance) { values["current"] = 0;//prúd values["status"] = "OFFLINE"; + const date = Date.now(); + // it happens, that some data did not get to tb after sending // we setTimeout to make more time for db to process telemetry (eg 150 messages at once) Object.keys(nodesData).forEach((node, index) => { @@ -900,18 +842,7 @@ exports.install = function(instance) { if(line == nodesData[node].line || line == undefined) { let tbname = nodesData[node].tbname; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry(values, tbname, date) } },(index+1) * 300); @@ -949,17 +880,20 @@ exports.install = function(instance) { { //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let type = "RESPONSE"; - if(bytes[4] == 0) type = "RESPONSE"; + if(bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] + else if(bytes[4] == 0) type = "RESPONSE"; else if(bytes[4] == 1) type = "ERROR"; else if(bytes[4] == 2) type = "EVENT"; else type = "UNKNOWN"; + let message = "OK"; + let error = ""; + if(type == "BROADCAST") return {message, type, error}; + let crc = crc16('ARC', bytes.slice(0, 9)); let c1 = (crc >> 8) & 0xFF; let c2 = crc & 0xFF; - let message = "OK"; - let error = ""; if(c1 != bytes[9]) { //CRC_ERROR @@ -996,10 +930,11 @@ exports.install = function(instance) { //BUILD TASKS// function buildTasks(params) { - //report FLOW.OMS_edge_fw_version as fw_version + //report SETTINGS.edge_fw_version as fw_version //report date as startdate //return; + console.log("buidTAaasks start ****************", params); monitor.info("buildTasks - params", params); @@ -1056,8 +991,7 @@ exports.install = function(instance) { monitor.info("buildTasks: profile for line", line); monitor.info("profile:", profile); - let time_points = profile.time_points; - if(time_points == undefined) time_points = profile.intervals; + let time_points= profile.intervals; // add name to regular profile timepoint and delete unused end_time key: time_points.forEach(point => { @@ -1158,7 +1092,7 @@ exports.install = function(instance) { //timepoint is in past, we add 24 hours start_time.setDate(start_time.getDate() + 1); } - + let params = getParams(PRIORITY_TYPES.relay_profile); params.type = "relay"; params.line = parseInt(line); @@ -1230,10 +1164,7 @@ exports.install = function(instance) { //PROCESS DEFAULT BROADCASTS - - //RPC pre nody / broadcast - //Time of dusk, Time of dawn - //Actual Time + //Time of dusk, Time of dawn, Actual Time if(processBroadcast) { @@ -1270,7 +1201,6 @@ exports.install = function(instance) { { //run broadcast Time of dawn - // addMinutesToTimestamp = 60*5; addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn let params = getParams(PRIORITY_TYPES.node_broadcast); @@ -1298,7 +1228,7 @@ exports.install = function(instance) { } { - //run broadcast //Actual time + //run broadcast Actual time addMinutesToTimestamp = 5; let params = getParams(PRIORITY_TYPES.node_broadcast); @@ -1371,15 +1301,6 @@ exports.install = function(instance) { params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "generated cmd - buildTasks (node)"; - //monitor last node && last command - /* - if(register == listOfCommands[ listOfCommands.length - 1 ]) - { - //if(k == 632) params.debug = true; - if(k == 698) params.debug = true; - } - */ - tasks.push(params); } @@ -1402,11 +1323,11 @@ exports.install = function(instance) { params.address = 0; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = 5; - params.tbname = FLOW.OMS_edgeName; + params.tbname = rvoTbName; params.info = "Master node FW verzia"; //params.debug = true; - //this will set FLOW.OMS_masterNodeIsResponding + //this will set SETTINGS.masterNodeIsResponding tasks.push(params); } @@ -1515,7 +1436,7 @@ exports.install = function(instance) { * function updates status and time_of_last_communication of node in the dbNodes * it only updates if conditions are met * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) - * if newStatus of node is always receiving false, and it is already for more than FLOW.OMS_node_status_nok_time value, we update status to "NOK" in tb + * if newStatus of node is always receiving false, and it is already for more than SETTINGS.node_status_nok_time value, we update status to "NOK" in tb * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). */ function updateNodeStatus(node, newStatus) @@ -1529,65 +1450,43 @@ exports.install = function(instance) { let nodeCurrentStatus = nodeObj.status; const now = Date.now(); - if(newStatus == false && nodeCurrentStatus == false) { - if(node == 638 || node == 637) console.log("false, false, return", node, now) - return true; - } + let data = null; - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION){ - - if(node == 638 || node == 637) console.log("true true, return", node, now); + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; + else if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) + { + data = {time_of_last_communication: now}; + nodeDbStatusModify(node, data); return; } - - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) + else if(newStatus == false && nodeCurrentStatus == false) return true; + else if(newStatus == false && nodeCurrentStatus == true) { - dbNodes.modify({ time_of_last_communication: now}).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if(!err) { - nodeObj.time_of_last_communication = now; - - if(node == 638 || node == 637) console.log('zapisane do db => status true & true', node, now) - } - }); - }); - return; - } - - if(newStatus == false && nodeCurrentStatus == true) - { - if(nodeObj.time_of_last_communication + FLOW.OMS_node_status_nok_time > now) { - if(node == 638 || node == 637) console.log('false true, return', node, now); - return; - } + if(nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; else { - dbNodes.modify({ status: newStatus}).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if(!err) { - nodeObj.status = newStatus; - - if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) - } - }); - }); + data = {status: newStatus}; + nodeDbStatusModify(node, data); return true; } } - - if(newStatus == true && nodeCurrentStatus == false) + else if(newStatus == true && nodeCurrentStatus == false) { - dbNodes.modify({ status: newStatus, time_of_last_communication: now}).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if(!err) { - nodeObj.status = newStatus; - nodeObj.time_of_last_communication = now; - - if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) - } - }); - }); + data = {status: newStatus, time_of_last_communication: now}; + nodeDbStatusModify(node, data); return; } + + } + + + function nodeDbStatusModify(node, data){ + dbNodes.modify(data).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(!err) { + nodesData[node] = {...nodesData[node], ...data}; + } + }); + }); } @@ -1606,7 +1505,7 @@ exports.install = function(instance) { //reportovali sme? if(reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { - sendNotification("CMD Manager: calculated Time of dusk", FLOW.OMS_edgeName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", SEND_TO.tb, instance); + sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", SEND_TO.tb, instance); reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; } } @@ -1626,7 +1525,7 @@ exports.install = function(instance) { //reportovali sme? if(reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { - sendNotification("CMD Manager: calculated Time of dawn", FLOW.OMS_edgeName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", SEND_TO.tb, instance); + sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", SEND_TO.tb, instance); reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; } } @@ -1660,6 +1559,7 @@ exports.install = function(instance) { { instance.send(SEND_TO.debug, "!rsPort.isOpen"); //await rsPort.open(); + console.log("Cmd_manager - !rsPort.isOpen"); } let currentTask = tasks[0]; @@ -1674,7 +1574,7 @@ exports.install = function(instance) { let params = {...tasks[0]}; //allow terminal commands - if(FLOW.OMS_maintenance_mode && params.type !== "cmd-terminal") + if(SETTINGS.maintenance_mode && params.type !== "cmd-terminal") { interval = setInterval(runTasks, LONG_INTERVAL); return; @@ -1718,7 +1618,6 @@ exports.install = function(instance) { return; } - //relay if(type == "relay") { @@ -1766,7 +1665,7 @@ exports.install = function(instance) { message = "off"; } - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance ); + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance ); interval = setInterval(runTasks, SHORT_INTERVAL); return; } @@ -1790,22 +1689,12 @@ exports.install = function(instance) { logger.debug("rotary_switch_state", rotary_switch_state); logger.debug("state_of_breaker", state_of_breaker[line]); - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - //report only once! if(!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; if(!disconnectedReport[tbname]) { - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry(values, tbname) } interval = setInterval(runTasks, SHORT_INTERVAL); @@ -1818,7 +1707,7 @@ exports.install = function(instance) { const register = params.register; //high_priority - if(!FLOW.OMS_masterNodeIsResponding) + if(!SETTINGS.masterNodeIsResponding) { //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version errorHandler.sendMessageToService("Master node is not responding"); @@ -1840,27 +1729,17 @@ exports.install = function(instance) { relayStatus = relaysData[line].contactor; } + if(line == 0) relayStatus = 0; if(type == "cmd-terminal") relayStatus = 1; //check if rotary_switch_state == "Off" - if(relayStatus == 0) { //console.log("------------------------------------relayStatus", relayStatus, line); let values = {"status": "OFFLINE"}; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry(values, tbname) interval = setInterval(runTasks, SHORT_INTERVAL); return; @@ -1948,12 +1827,9 @@ exports.install = function(instance) { endTime = new Date(); var timeDiff = endTime - startTime; - //--1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data - //let bytes = data.slice(0); - let bytes = data; + //data je array z 11 bytov: 1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data a 2 byty CRC let dataBytes = data.slice(5,9); - - let result = detectIfResponseIsValid(bytes); + let result = detectIfResponseIsValid(data); //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let message = result.message; // OK, NOK @@ -1995,7 +1871,7 @@ exports.install = function(instance) { dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { builder.callback(function(err, response) { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); nodesData[nodeAddress].processed = true; @@ -2019,15 +1895,15 @@ exports.install = function(instance) { //master node if(nodeAddress == 0) { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); - FLOW.OMS_masterNodeIsResponding = true; - if(register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version; + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); + SETTINGS.masterNodeIsResponding = true; + if(register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; } //odoslanie príkazu z terminálu - dáta if(type == "cmd-terminal") { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); } if(params.debug) @@ -2037,17 +1913,7 @@ exports.install = function(instance) { if(saveToTb) { - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry(values, tbname) } else { @@ -2127,9 +1993,9 @@ exports.install = function(instance) { //master node if(node == 0) { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); logger.debug("master_node_is_not_responding", params); - FLOW.OMS_masterNodeIsResponding = false; + SETTINGS.masterNodeIsResponding = false; if(register == 4) values["master_node_version"] = "NOK"; } @@ -2154,17 +2020,7 @@ exports.install = function(instance) { // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); if(saveToTb && Object.keys(values).length > 0) { - - let dataToTb = { - [tbName]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry(values, tbName) } } @@ -2217,7 +2073,7 @@ exports.install = function(instance) { if(responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; else responseObj["bytes"] = data; - let refFlowdata = refFlowdataObj[params.refFlowdataKey]; + let refFlowdata = refFlowdataObj[params.refFlowdataKey]; //holds reference to httprequest flowdata if(refFlowdata) { refFlowdata.data = responseObj; @@ -2231,8 +2087,6 @@ exports.install = function(instance) { */ function reportEdgeDateTimeAndNumberOfLuminaires(){ - if(!FLOW.OMS_edgeName) return; - //Number of ok and nok nodes on platform does not equals to total number of nodes. //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); @@ -2261,83 +2115,77 @@ exports.install = function(instance) { "edge_date_time": ts }; - let dataToTb = { - [FLOW.OMS_edgeName]: [ - { - "ts": ts, - "values": values - } - ] - } - - tbHandler.sendToTb(dataToTb, instance); - //instance.send(SEND_TO.tb, dataToTb); + sendTelemetry(values, SETTINGS.rvoTbName, ts); } - //to ensure, edgeDateTime will be send to tb at full minute - customTasksInterval = setInterval(function() { - if(new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); - }, 1000); + function handleRsPort() { - //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" - // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM - // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI + //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" + // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM + // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI - if(FLOW.OMS_serial_port == "" || FLOW.OMS_serial_port == undefined || FLOW.OMS_serial_port.length === 1) FLOW.OMS_serial_port = "ttymxc4"; - const rsPort = new SerialPort(`/dev/${FLOW.OMS_serial_port}`, { autoOpen: false }); - //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit - //rsPort.setMaxListeners(0); + if(SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; + rsPort = new SerialPort(`/dev/${SETTINGS.serial_port}`, { autoOpen: false }); + //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit + //rsPort.setMaxListeners(0); - rsPort.on('open', async function() { + rsPort.on('open', async function() { - logger.debug("CMD manager - rsPort opened sucess"); + logger.debug("CMD manager - rsPort opened success"); - //loadRelaysData(); + //loadRelaysData(); - await runSyncExec(`stty -F /dev/${FLOW.OMS_serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) { - instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); + await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) { + instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); - logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); + logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); - //APP START - let dataToInfoSender = {id: FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name}; - dataToInfoSender.fw_version = FLOW.OMS_edge_fw_version; - dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); - dataToInfoSender.__force__ = true; - - instance.send(SEND_TO.infoSender, dataToInfoSender); + //APP START + let dataToInfoSender = {id: SETTINGS.project_id, name: SETTINGS.rvo_name}; + dataToInfoSender.fw_version = SETTINGS.edge_fw_version; + dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); + dataToInfoSender.__force__ = true; + + instance.send(SEND_TO.infoSender, dataToInfoSender); - logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); + logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); - }).catch(function (reason) { - instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); + }).catch(function (reason) { + instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); + }); }); - }); - rsPort.on('error', function(err) { - - //TODO report to service!!! - //errLogger.error(exports.title, "unable to open port", FLOW.OMS_serial_port, err.message); - errorHandler.sendMessageToService([exports.title, "unable to open port", FLOW.OMS_serial_port, err.message], 0); + rsPort.on('error', function(err) { + + //TODO report to service!!! + //errLogger.error(exports.title, "unable to open port", SETTINGS.serial_port, err.message); + errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); - instance.send(SEND_TO.debug, err.message); - }); + instance.send(SEND_TO.debug, err.message); + }); - rsPort.on("close", () => { - rsPort.close(); - }); + rsPort.on("close", () => { + setTimeout(() => rsPort.open(), 1000); + }); - rsPort.open(); + rsPort.open(); + } + instance.on("close", () => { clearInterval(interval); clearInterval(customTasksInterval); + clearInterval(setCorrectTime); rsPort.close(); }); - instance.on("data", async function(flowdata) { + instance.on("0", flowdata => { + main(); + }) + + instance.on("1", async function(flowdata) { //instance.send(SEND_TO.debug, "on Data"); //instance.send(SEND_TO.debug, flowdata); @@ -2434,19 +2282,19 @@ exports.install = function(instance) { } else if(cmd == "rotary_switch_state") { + let value = flowdata.data.value; + //state was changed - if(rotary_switch_state != flowdata.data.value) + if(rotary_switch_state != value) { - if(rotary_switch_state == "Off") + if(value == "Off") { //vyreportovat vsetky svietdla reportOfflineNodeStatus(); } - else reportOnlineNodeStatus(); + rotary_switch_state = value; } - - rotary_switch_state = flowdata.data.value; } else if(cmd == "lux_sensor") { @@ -2501,25 +2349,10 @@ exports.install = function(instance) { else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); //report status liniu - let values = { - "status": status - }; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); + sendTelemetry({status: status}, tbname) //current value if(value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii - else reportOnlineNodeStatus(line); } } @@ -2537,7 +2370,11 @@ exports.install = function(instance) { if(flowdata.data.hasOwnProperty("topic")) { - let data = flowdata.data.content.data; + let data = getNested(flowdata.data, "content", "data"); + if(data == undefined) { + console.log("Invalid rpc command came from platform"); + return; + } let command = data.params.command; let method = data.method; @@ -2572,109 +2409,109 @@ exports.install = function(instance) { let node = keys[i]; //logger.debug( node, nodesData[node], tbname); - if(tbname == nodesData[node].tbname.trim()) + if(tbname == nodesData[node].tbname) { - let params = getParams(PRIORITY_TYPES.high_priority); + let params = getParams(PRIORITY_TYPES.high_priority); - value = parseInt(value); - if(value > 0) value = value + 128; + value = parseInt(value); + if(value > 0) value = value + 128; - //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.byte4 = value; - params.rw = 1;//write - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'set dimming from platform'; - //params.debug = true; + //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.byte4 = value; + params.rw = 1;//write + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'set dimming from platform'; + //params.debug = true; - //ak linia je + //ak linia je - //debug(params); - logger.debug("dimming", params); + //debug(params); + logger.debug("dimming", params); - tasks.push(params); + tasks.push(params); - setTimeout(function(){ + setTimeout(function(){ - //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority - //a pridame aj vyreportovanie dimmingu - { - let params = getParams(PRIORITY_TYPES.high_priority); + //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority + //a pridame aj vyreportovanie dimmingu + { + let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read dimming (after set dimming from platform)'; - params.debug = true; + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read dimming (after set dimming from platform)'; + params.debug = true; - tasks.push(params); - } + tasks.push(params); + } - //pridame aj vyreportovanie - vykon - { - let params = getParams(PRIORITY_TYPES.high_priority); + //pridame aj vyreportovanie - vykon + { + let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 76; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Power (after set dimming from platform)'; - params.debug = true; + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 76; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Power (after set dimming from platform)'; + params.debug = true; - tasks.push(params); - } + tasks.push(params); + } - //pridame aj vyreportovanie - prud svietidla - { - let params = getParams(PRIORITY_TYPES.high_priority); + //pridame aj vyreportovanie - prud svietidla + { + let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 75; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Current (after set dimming from platform)'; - params.debug = true; + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 75; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Current (after set dimming from platform)'; + params.debug = true; - tasks.push(params); - } + tasks.push(params); + } - //pridame aj vyreportovanie - power faktor - ucinnik - { - let params = getParams(PRIORITY_TYPES.high_priority); + //pridame aj vyreportovanie - power faktor - ucinnik + { + let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 77; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read power factor - Cos phi (after set dimming from platform)'; - params.debug = true; + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 77; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read power factor - Cos phi (after set dimming from platform)'; + params.debug = true; - tasks.push(params); - } + tasks.push(params); + } - },4000); - + },4000); + - nodeWasFound = true; + nodeWasFound = true; - break; + break; } } @@ -2702,7 +2539,7 @@ exports.install = function(instance) { for(let i = 0; i < keys.length; i++) { let node = keys[i]; - if(tbname == nodesData[node].tbname.trim()) + if(tbname == nodesData[node].tbname) { if(profile != "") profile = JSON.stringify(profile); @@ -2719,11 +2556,9 @@ exports.install = function(instance) { nodesData[node].processed = false; nodesData[node].profile = profile; - let line = nodesData[node].line; processNodeProfile(node); - - }); }); + }); } } } @@ -2768,10 +2603,16 @@ exports.install = function(instance) { logger.debug("worksys - update relay profile done:", profile); instance.send(SEND_TO.debug, "worksys - update relay profile done"); - loadRelaysData(line).then(function (data) { - logger.debug("loadRelaysData DONE for line", line); - buildTasks({processLineProfiles: true, line: line}); - }); + relaysData[line].profile = profile; + + loadRelaysData(line) + + //TODO build tasks by mala bezat az ked je vsetko loadRelaysData + //spracovane, pravdepodobne treba spravit promisy + logger.debug("loadRelaysData DONE for line", line); + console.log("zacina buildTasks po loadRelaysData.........") + + buildTasks({processLineProfiles: true, line: line}); sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", {line: line}, profile, SEND_TO.tb, instance ); @@ -2788,16 +2629,16 @@ exports.install = function(instance) { if(command === "switch") { - // if we receive rpc from platform, to switch maintenance mode, we set OMS_maintenance_mode flow variable to value; - if(entity_type === "edb" || entity_type === "edb_ver4_se") FLOW.variables.OMS_maintenance_mode = value; - - let responseRelays = await promisifyBuilder(dbRelays.find().where("tbname", tbname)); + // if we receive rpc from platform, to switch maintenance mode, we set SETTINGS.maintenance_mode flow variable to value; + if(entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; + const relayObject = getObjectByTbValue(relaysData, tbname); let line = 0; - if(responseRelays.length == 1) line = responseRelays[0].line; + if(isObject(relayObject)) line = relayObject.line; - if(value == false) turnOffLine(line, "command received form platform"); - else turnOnLine(line, "command received form platform"); + // v relaysData je contactor bud 0 alebo 1, ale z platformy prichadza true, false; + if(value == false) turnOffLine(line, "command received from platform"); + else turnOnLine(line, "command received from platform"); } } else @@ -2859,7 +2700,11 @@ exports.install = function(instance) { } }) -} // end of instance.export + +//function gets value of a nested property in an object and returns undefined if it does not exists: +function getNested(obj, ...args) { + return args.reduce((obj, level) => obj && obj[level], obj) +} /** @@ -2977,10 +2822,6 @@ function detectReadOnlyFilesystem() }); } -let setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour -setCorrectPlcTimeOnceADay(); - - @@ -2989,6 +2830,19 @@ setCorrectPlcTimeOnceADay(); ///helper functions +function sendTelemetry(values, tbname, date=Date.now()) +{ + const dataToTb = { + [tbname]: [ + { + "ts": date, + "values": values + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); +} function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) { @@ -3268,7 +3122,8 @@ function processResponse(register, bytes) var d = new Date(); d.setHours(h); d.setMinutes(m); - d.setSeconds(s); + d.setSeconds(0); + d.setMilliseconds(0); timestamp = d.getTime(); } @@ -3381,223 +3236,13 @@ function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4 } - - - - - -// SAMPLE DATA - -const relaysDataExample = -{ - '0': { - line: 0, - tbname: 'jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV', - contactor: 1, - profile: '' - }, - '1': { - line: 1, - tbname: 'MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O', - contactor: 1, - profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' - }, - '2': { - line: 2, - tbname: 'jBL12pg63eX4N9P7zy0lJLyEJKmlbkGwZMx0avQV', - contactor: 1, - profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' - }, - '3': { - line: 3, - tbname: 'aAOzENGrvpbe0VoK7D6E1a819PZmdg3nl24JLQMk', - contactor: 1, - profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' + function getObjectByTbValue(object, tbname) { + return object[Object.keys(object).find(key => object[key].tbname === tbname)]; } -} - -const rpcSwitchOffLine = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 8, - "method": "set_command", - "params": { - "entities": [ - { - "entity_type": "edb_line", - "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" - } - ], - "command": "switch", - "payload": { - "value": false - } - } - } + function isObject (item) { + return (typeof item === "object" && !Array.isArray(item) && item !== null); } -} -const rpcSetNodeDimming = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 10, - "method": "set_command", - "params": { - "entities": [ - { - "entity_type": "street_luminaire", - "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" - } - ], - "command": "dimming", - "payload": { - "value": 5 - } - } - } - } -} +} // end of instance.export -const rpcLineProfile = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 9, - "method": "set_profile", - "params": { - "entities": [ - { - "entity_type": "edb_line", - "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" - } - ], - "payload": { - "intervals": [ - { - "value": 0, - "end_time": "20:00", - "start_time": "13:00" - }, - { - "value": 1, - "end_time": "05:30", - "start_time": "20:00" - }, - { - "value": 0, - "end_time": "13:00", - "start_time": "05:30" - } - ], - "astro_clock": true, - "dawn_lux_sensor": false, - "dusk_lux_sensor": false, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 0, - "dusk_astro_clock_offset": 0, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - } - } - } - } -} - - -const rpcNodeProfile = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 11, - "method": "set_profile", - "params": { - "entities": [ - { - "entity_type": "street_luminaire", - "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" - } - ], - "payload": { - "intervals": [ - { - "cct": 3000, - "value": 0, - "end_time": "17:50", - "start_time": "13:00" - }, - { - "cct": 3000, - "value": 100, - "end_time": "21:30", - "start_time": "17:50" - }, - { - "cct": 3000, - "value": 0, - "end_time": "13:00", - "start_time": "07:10" - }, - { - "cct": 3000, - "value": 50, - "end_time": "00:00", - "start_time": "21:30" - }, - { - "cct": 3000, - "value": 10, - "end_time": "04:30", - "start_time": "00:00" - }, - { - "cct": 3000, - "value": 100, - "end_time": "07:10", - "start_time": "04:30" - } - ], - "astro_clock": true, - "dawn_lux_sensor": false, - "dusk_lux_sensor": false, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 30, - "dusk_astro_clock_offset": 20, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - } - } - } - } -} - - const sunCalcExample = { - dusk_no_offset: '20:18', - dawn_no_offset: '05:19', - dusk: '20:18', - dusk_hours: 20, - dusk_minutes: 18, - dawn: '05:19', - dawn_hours: 5, - dawn_minutes: 19, - dusk_time: 1715278688962, - dawn_time: 1715224744357, - dusk_astro_clock_offset: 0, - dawn_astro_clock_offset: 0 -} diff --git a/flow/cmd_manager131.js b/flow/cmd_manager131.js new file mode 100644 index 0000000..63c1a1f --- /dev/null +++ b/flow/cmd_manager131.js @@ -0,0 +1,3603 @@ +exports.id = 'cmd_manager'; +exports.title = 'CMD Manager'; +exports.group = 'Worksys'; +exports.color = '#5D9CEC'; +exports.version = '0.0.3'; +exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; + +//blue - send message to relays + +exports.input = 2; +exports.icon = 'cloud-upload'; +//exports.npm = ['serialport' , 'child_process']; + +exports.html = ` +
+
+
+
RPC - run RPC calls

+
+
+
@(User)
+
+
+
@(Password)
+
+
+
@(My edge)
+
+
+
+`; + +exports.readme = `Manager for CMD calls`; + +const SerialPort = require('serialport'); +const { exec } = require('child_process'); +const { crc8, crc16, crc32 } = require('easy-crc'); +const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js'); +const { bytesToInt, longToByteArray, addZeroBefore, isEmptyObject, convertUTCDateToLocalDate } = require('./helper/utils'); +const bitwise = require('bitwise'); + +var SunCalc = require('./helper/suncalc.js'); +const DataToTbHandler = require('./helper/DataToTbHandler.js'); +const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); +const { promisifyBuilder, makeMapFromDbResult} = require('./helper/db_helper.js'); +const { sendNotification, initNotifications, ERRWEIGHT } = require('./helper/notification_reporter.js'); + +const dbNodes = TABLE("nodes"); +const dbRelays = TABLE("relays"); +const dbSettings = TABLE("settings"); + +//https://github.com/log4js-node/log4js-node/blob/master/examples/example.js +//file: { type: 'file', filename: path.join(__dirname, 'log/file.log') } +var path = require('path'); +var log4js = require("log4js"); +const process = require('process'); + +//TODO - to remove? +// runTasks intervals +const SHORT_INTERVAL = 30; +const LONG_INTERVAL = 300; + +//send data to following instances: +const SEND_TO = { + debug: 0, + tb: 1, + http_response: 2, + dido_controller: 3, + infoSender: 4 +} + +const PRIORITY_TYPES = { + terminal: 0, + fw_detection: 1,//reserved only for FW detection - FLOW.OMS_masterNodeIsResponding + high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) + relay_profile: 3, + node_broadcast: 4, + node_profile: 5, + node_cmd: 6 +} + +const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes + +//list of command calls to process. Processing in runTasks function +let tasks = []; + +let interval = null;//timeout for procesing tasks +let customTasksInterval = null; +let refFlowdata = null;//holds reference to httprequest flowdata +let refFlowdataObj = {}; + +//load from settings +let latitude = 48.70826502;//48.682255758; +let longitude = 17.28455203;//17.278910807; + +const gmtOffset = 0; + +//ak nie je nastaveny +//https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ +//https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates + +//priorities for registers +let priorities = []; + +let minutes = 1; +priorities["0"] = minutes; +priorities["1"] = minutes; + +minutes = 5; +priorities["74"] = minutes; +priorities["75"] = minutes; +priorities["76"] = minutes; +priorities["77"] = minutes; +priorities["78"] = minutes; +priorities["79"] = minutes; +priorities["84"] = minutes; + +minutes = 10; +priorities["87"] = minutes; +priorities["6"] = minutes; +priorities["7"] = minutes; +priorities["80"] = minutes; +priorities["8"] = minutes; +priorities["3"] = minutes; +priorities["89"] = minutes; + +//prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) +let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89]; + +const errorHandler = new ErrorToServiceHandler(); + +let rotary_switch_state = "Off"; +let lux_sensor; +let state_of_breaker = {};//key is line, value is On/Off +let disconnectedReport = {};//key is tbname, value true/false + +let relaysData = {};//key is line, value is data from db +let nodesData = {};//key is node, value data from db + +//helper container for counting resolved group of commands (commands related to set profile) +let cmdCounter = {};//key is node, value is counter + +//END OF VARIABLE SETTINGS +//-------------------------------- + + +log4js.configure({ + appenders: { + errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') }, + monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, + console: { type: 'console' } + }, + categories: { + errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, + monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, + //another: { appenders: ['console'], level: 'trace' }, + default: { appenders: ['console'], level: 'trace' } + } +}); + +const errLogger = log4js.getLogger("errLogs"); +const logger = log4js.getLogger(); +const monitor = log4js.getLogger("monitorLogs"); + +//USAGE +//logger.debug("text") +//monitor.info('info'); +//errLogger.error("some error"); + + +function cmdCounterResolve(address) +{ + if(cmdCounter.hasOwnProperty(address)) + { + cmdCounter[address] = cmdCounter[address] - 1; + + let result = cmdCounter[address]; + if(result == 0) delete cmdCounter[address]; + return result; + } + return -1; +} + + +function getParams(priority) +{ + let params = {}; + + //core rpc values + params.address = 0;//if(recipient === 0) address = 0; + params.byte1 = 0;//msb, podla dokumentacie data3 + params.byte2 = 0;//podla dokumentacie data2 + params.byte3 = 0;//podla dokumentacie data1 + params.byte4 = 0;//lsb, podla dokumentacie data0 + params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast + params.register = -1;//register number + params.rw = 0;//0: read, 1: write + + //other values + //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" + //params.tbname = tbname; + params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority + params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed + if(priority != undefined ) + { + params.timestamp = priority; + params.priority = priority; + } + + params.addMinutesToTimestamp = 0;//repeat task if value is > 0, + + //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" + //params.info = ""; + + return params; +} + + +async function loadSettings() +{ + let responseSettings = await promisifyBuilder(dbSettings.find()); + + latitude = responseSettings[0]["latitude"]; + longitude = responseSettings[0]["longitude"]; + + //globals + FLOW.OMS_language = responseSettings[0]["lang"]; + FLOW.OMS_rvo_name = responseSettings[0]["rvo_name"]; + FLOW.OMS_projects_id = responseSettings[0]["projects_id"]; + //FLOW.OMS_rvo_tbname = responseSettings[0]["tbname"]; + FLOW.OMS_temperature_adress = responseSettings[0]["temperature_adress"]; + FLOW.OMS_controller_type = responseSettings[0]["controller_type"]; + FLOW.OMS_serial_port = responseSettings[0]["serial_port"]; + FLOW.OMS_node_status_nok_time = responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000; // hour * minutes * seconds + //logger.debug('settings', responseSettings[0]); + + initNotifications(); +} + +loadSettings(); + + +async function loadNodes() +{ + const responseNodes = await promisifyBuilder(dbNodes.find()); + nodesData = makeMapFromDbResult(responseNodes, "node"); +} + +loadNodes(); + + +//nastav profil nodu +function processNodeProfile(node) +{ + if(rotary_switch_state != "Automatic") + { + logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); + return; + } + + let nodeObj = nodesData[node]; + let line = nodeObj.line; + + if(relaysData[line].contactor == 0) + { + logger.debug("line line is off", line, node); + return; + } + + if(nodeObj.processed == 1) + { + logger.debug("node was already processed", node); + return; + } + + let nodeProfile = nodeObj.profile; + logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); + if(nodeProfile) { + + try { + nodeProfile = JSON.parse(nodeProfile); + } catch (error) { + logger.debug("Cmd_manager - Error parsing node profile", error); + } + + } + + logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); + + let timestamp = PRIORITY_TYPES.node_cmd; + + removeTask({type: "set_node_profile", address: node}); + + if(nodeProfile === "") + { + //vypneme profil nodu, posleme cmd + //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. + //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia + + logger.debug("turn off profile"); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'turn off/reset node profile'; + + cmdCounter[node] = 1; + + tasks.push(params); + } + else + { + let tasksProfile = []; + + //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'turn off node profile'; + + tasksProfile.push(params); + + timestamp++; + + logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); + + //TS1 Time point a TS1 Time Point Levels + let register = 9; + for(let i = 0; i < nodeProfile.intervals.length; i++) + { + let obj = nodeProfile.intervals[i]; + //let timePoint = obj.time_point; + let dim_value = obj.value; + + + //Reg 9 až Reg 40 + + /* + Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. + Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). + Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. + Časový bod má formát: + Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované + Register úrovne má rovnaký formát ako dimming register (Reg 1). + */ + + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //params.byte1 = 0;//msb, podla dokumentacie data3 + //params.byte2 = 0;//podla dokumentacie data2 + //params.byte3 = 0;//podla dokumentacie data1 + //params.byte4 = 0;//lsb, podla dokumentacie data0 + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + let start_time = obj.start_time; + let t = start_time.split(":"); + //if(timePoint != undefined) t = timePoint.split(":"); + //else t = [0,0]; + + logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = parseInt(t[0]);//hh + params.byte2 = parseInt(t[1]);//mm + params.byte3 = 0;//ss + params.byte4 = 0;// + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0;//ss + params.byte4 = parseInt(dim_value) + 128;// + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point Levels ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + } + + //Threshold lux level for DUSK/DAWN + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //params.byte1 = 0;//msb, podla dokumentacie data3 + //params.byte2 = 0;//podla dokumentacie data2 + //params.byte3 = 0;//podla dokumentacie data1 + //params.byte4 = 0;//lsb, podla dokumentacie data0 + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + //Time schedule settings na koniec + //if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor) + { + + logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 96; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = "Threshold lux level for DUSK/DAWN"; + + if(nodeProfile.dusk_lux_sensor) + { + let v = nodeProfile.dusk_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if(nodeProfile.dawn_lux_sensor) + { + let v = nodeProfile.dawn_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //DUSK/DAWN max. adjust period + { + + logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 97; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = "DUSK/DAWN max. adjust period"; + + if(nodeProfile.astro_clock) + { + let v = nodeProfile.dusk_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if(nodeProfile.astro_clock) + { + let v = nodeProfile.dawn_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //Static offset + { + + //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: + //Bity 0 – 6: hodnota v minútach + //Bit 7: znamienko (1 – mínus) + + logger.debug("processNodeProfile: Static offset", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 98; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = "Static offset"; + + if(nodeProfile.astro_clock) + { + let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); + let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); + + if(dusk_astro_clock_offset < 0) + { + params.byte3 = (dusk_astro_clock_offset * -1) + 128; + } + else + { + params.byte3 = dusk_astro_clock_offset; + } + + if(dawn_astro_clock_offset < 0) + { + params.byte4 = (dawn_astro_clock_offset * -1) + 128; + } + else + { + params.byte4 = dawn_astro_clock_offset; + } + } + + tasksProfile.push(params); + timestamp++; + } + + logger.debug("Time schedule settings - turn on", node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 8; + params.recipient = 1; + params.rw = 1;//write + + //Time schedule settings + let bits = []; + + //Byte 0 (LSB): + //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). + bits.push(1); + //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) + bits.push(0); + bits.push(0); + bits.push(0); + if(nodeProfile.astro_clock == true) + { + //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý + bits.push(1); + } + else bits.push(0); + + //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) + bits.push(0); + + //Bity 6-7 - zatiaľ nepoužité + bits.push(0); + bits.push(0); + + params.byte4 = bitwise.byte.write(bits.reverse()); + + //Byte 2 – nastavenie pre lux senzor: + bits = []; + + //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if(nodeProfile.dusk_lux_sensor == true)//sumrak + { + bits.push(1); + } + else bits.push(0); + + //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if(nodeProfile.dawn_lux_sensor == true)//usvit + { + bits.push(1); + } + else bits.push(0); + + //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. + bits.push(0);//zatial neimplementovane + + //Bit 3 – 7 - nepoužité + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + + params.byte2 = bitwise.byte.write(bits.reverse()); + params.timestamp = timestamp; + params.info = "Time schedule settings - turn on"; + + tasksProfile.push(params); + + //zaver + cmdCounter[node] = tasksProfile.length; + + //tasks.push(tasksProfile); + tasks = tasks.concat(tasksProfile); + + } + + logger.debug("finished set profile for ", node); + +} + + +function cleanUpRefFlowdataObj() +{ + let now = new Date(); + let timestamp = now.getTime(); + + //clear old refFlowdata references + let keys = Object.keys(refFlowdataObj); + for(let i = 0; i < keys.length; i++) + { + let timestampKey = keys[i]; + + if((timestamp - timestampKey) > 60*1000 ) + { + console.log("cleanUpRefFlowdataObj delete", timestampKey); + delete refFlowdataObj[ timestampKey ]; + } + } +} + + +function removeTask(obj) +{ + let keys = Object.keys(obj); + tasks = tasks.filter((task) => { + + let counter = 0; + for(let i = 0; i < keys.length; i++) + { + let key = keys[i]; + if(task.hasOwnProperty(key) && obj.hasOwnProperty(key)) + { + if(task[key] == obj[key]) counter++; + } + } + + if(counter == keys.length) return false; + return true; + }); +} + + +exports.install = function(instance) { + + let now = new Date(); + console.log("CMD Manager installed", now.toLocaleString("sk-SK")); + + const tbHandler = new DataToTbHandler(SEND_TO.tb); + tbHandler.setSender(exports.title); + + //FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name + //const errorHandler = new ErrorToServiceHandler(instance, SEND_TO.infoSender); + errorHandler.setProjectsId(FLOW.OMS_projects_id); + //const errorHandler = new ErrorToServiceHandler(instance); + //errorHandler.sendMessageToService("ahoj", 0); + + let sunCalcResult = calculateDuskDawn(); + + let reportDuskDawn = { + dusk_time: sunCalcResult.dusk_time, + dawn_time: sunCalcResult.dawn_time, + dusk_time_reported: undefined, + dawn_time_reported: undefined + }; + + + process.on('uncaughtException', function (err) { + //TODO send to service + + errLogger.error('uncaughtException:', err.message) + errLogger.error(err.stack); + + errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); + //process.exit(1); + }) + + //te();//force error + + + function processAllNodeProfilesOnLine(line) + { + + for (let k in nodesData) { + //node:number|tbname:string|line:number|profile:string|processed:boolean + + if(line == nodesData[k].line) + { + let node = nodesData[k].node; + let processed = nodesData[k].processed; + + if(!processed) + { + processNodeProfile(node); + } + else + { + logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); + } + } + } + } + + + async function loadRelaysData(line) { + + relaysData = await promisifyBuilder(dbRelays.find()); + relaysData = makeMapFromDbResult(relaysData, "line"); + + for (const [key, value] of Object.entries(relaysData)) + { + if(key == "0") continue; + if(line != undefined) + { + //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData + if(line != value.line ) continue; + } + + if(value.contactor == 1) processAllNodeProfilesOnLine(value.line); + } + +// console.log('.........', relaysData); + } + + + function reportOnlineNodeStatus(line) + { + //broadcast cas, o 3 sek neskor - status, brightness + //Po zapnutí línie broadcastovo aktualizovať predtým čas. + + logger.debug("--->reportOnlineNodeStatus for line", line); + + //return; + + //run broadcast //Actual time + addMinutesToTimestamp = 0; + + let params = {}; + + var d = new Date(); + let hours = d.getHours(); + let minutes = d.getMinutes(); + let seconds = d.getSeconds(); + + let time = d.getTime(); // time in ms + + params.address = 0xffffffff;//Broadcast + params.byte1 = hours;//h + params.byte2 = minutes;//m + params.byte3 = seconds;//s + params.byte4 = 0; + params.recipient = 2;//2 broadcast, address = 0 + params.register = 87;//Actual time + params.rw = 1;//write + + //other values + params.type = "cmd"; + params.timestamp = Date.now() + 60000; + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "run broadcast: Actual time"; + + tasks.push(params); + + let sec = 3; + setTimeout(function(){ + //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel + + for (let k in nodesData) { + + //potrebujem nody k danej linii + if(line == nodesData[k].line || line == undefined) + { + let tbname = nodesData[k].tbname; + let node = nodesData[k].node; + let status = "NOK"; + + // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. + if(nodesData[k].status) { + status = "OK"; + nodesData[k].time_of_last_communication = time; + } + + let dataToTb = { + [tbname]: [ + { + ts: time, + values: { + status: status + } + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); + + //prud, vykon - current, input power pre liniu pre vsetky nody + + //a pridame aj vyreportovanie dimmingu + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read dimming'; + //params.debug = true; + + tasks.push(params); + } + + //Prúd + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 75;//prud + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read current'; + //params.debug = true; + + tasks.push(params); + } + + //výkon + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 76;//výkon + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read power'; + //params.debug = true; + + tasks.push(params); + } + } + } + },sec*1000); + } + + + function reportOfflineNodeStatus(line) + { + logger.debug("--->reportOfflineNodeStatus for line", line); + + values = {}; + values["dimming"] = 0;//brightness + values["power"] = 0;//výkon + values["current"] = 0;//prúd + values["status"] = "OFFLINE"; + + // it happens, that some data did not get to tb after sending + // we setTimeout to make more time for db to process telemetry (eg 150 messages at once) + Object.keys(nodesData).forEach((node, index) => { + + setTimeout(function() { + + //potrebujem nody k danej linii + if(line == nodesData[node].line || line == undefined) + { + let tbname = nodesData[node].tbname; + + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + } + + },(index+1) * 300); + }) + + } + + + function turnOnLine(line, info) + { + let obj = { + line: line, + command: "turnOn", + info: info + }; + + logger.debug("linia", line, obj); + instance.send(SEND_TO.dido_controller, obj); + } + + function turnOffLine(line, info) + { + let obj = { + line: line, + command: "turnOff", + info: info + }; + + logger.debug("linia", line, obj); + instance.send(SEND_TO.dido_controller, obj); + } + + + function detectIfResponseIsValid(bytes) + { + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let type = "RESPONSE"; + if(bytes[4] == 0) type = "RESPONSE"; + else if(bytes[4] == 1) type = "ERROR"; + else if(bytes[4] == 2) type = "EVENT"; + else type = "UNKNOWN"; + + let crc = crc16('ARC', bytes.slice(0, 9)); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; + + let message = "OK"; + let error = ""; + if(c1 != bytes[9]) + { + //CRC_ERROR + message = "NOK"; + error = "CRC_ERROR c1"; + instance.send(SEND_TO.debug, "CRC_ERROR c1"); + } + + if(c2 != bytes[10]) + { + //CRC_ERROR + message = "NOK"; + error = "CRC_ERROR c2"; + instance.send(SEND_TO.debug, "CRC_ERROR c2"); + } + + //crc error + if(type != "RESPONSE") + { + instance.send(SEND_TO.debug, bytes); + instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); + + //logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes); + + error = "type is: " + type; + + message = "NOK"; + } + + return {message, type, error}; + } + + + //BUILD TASKS// + function buildTasks(params) + { + //report FLOW.OMS_edge_fw_version as fw_version + //report date as startdate + + //return; + + monitor.info("buildTasks - params", params); + + let processLine; //defined line + let init = false; + let processLineProfiles = true; + let processBroadcast = true; + let processNodes = true; + + if(params == undefined) + { + init = true; + tasks = []; + logger.debug("-->buildTasks clear tasks"); + } + else + { + processLineProfiles = false; + processBroadcast = false; + processNodes = false; + + processLineProfiles = params.processLineProfiles; + processLine = params.line; + } + + //load profiles pre linie + //relaysData[ record["line"] ] + + let now = new Date(); + + if(processLineProfiles) + { + //process line profiles + let keys = Object.keys(relaysData); + for(let i = 0; i < keys.length; i++) + { + let line = parseInt(keys[i]); //line is turned off by default + let profilestr = relaysData[line].profile; + + if(processLine != undefined) + { + if(processLine != line) continue; + } + + try { + + /** + * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... + */ + if(profilestr === "") throw ("Profile is not defined"); + let profile = JSON.parse(profilestr); + if(Object.keys(profile).length === 0) throw ("Profile is empty"); + + monitor.info("buildTasks: profile for line", line); + monitor.info("profile:", profile); + + let time_points = profile.time_points; + if(time_points == undefined) time_points = profile.intervals; + + // add name to regular profile timepoint and delete unused end_time key: + time_points.forEach(point => { + point.name = "profileTimepoint" + delete point.end_time; + }); + + //monitor.info("buildTasks: time_points", time_points); + + let currentValue = 0; + if(time_points.length > 0) currentValue = time_points[time_points.length - 1].value; + + + /** + * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. + * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn + * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching + */ + if(profile.astro_clock == true) + { + + // if astro clock true, we remove all regular profile points + time_points = []; + + let sunCalcResult = calculateDuskDawn(new Date(), line); + + // adding dusk dawn to timpoints + if(profile.dawn_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dawn"], "value": 0, "name":"dawn"}); + if(profile.dusk_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dusk"], "value": 1, "name":"dusk"}); + + //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) + //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window + if(profile.dawn_lux_sensor == true) + { + let [ahours, aminutes, aseconds] = sunCalcResult["dawn"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours)); + ad.setMinutes(parseInt(aminutes) + profile.dawn_lux_sensor_time_window); + ad.setSeconds(0); + + let strDate = ad.getHours() + ":" + ad.getMinutes(); + + time_points.push({"value": 0, "start_time": strDate, "name": "luxOff"}); + } + + if(profile.dusk_lux_sensor == true) + { + let [ahours, aminutes, aseconds] = sunCalcResult["dusk"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours)); + ad.setMinutes(parseInt(aminutes) + profile.dusk_lux_sensor_time_window); + ad.setSeconds(0); + + let strDate = ad.getHours() + ":" + ad.getMinutes(); + + time_points.push({"value": 1, "start_time": strDate, "name": "luxOn"}); + //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing + } + } + + //sort time_points + time_points.sort(function (a, b) { + + let [ahours, aminutes, aseconds] = a.start_time.split(':'); + let [bhours, bminutes, bseconds] = b.start_time.split(':'); + + let ad = new Date(); + ad.setHours( parseInt(ahours) ); + ad.setMinutes( parseInt(aminutes) ); + ad.setSeconds(0); + + let bd = new Date(); + bd.setHours( parseInt(bhours) ); + bd.setMinutes( parseInt(bminutes) ); + ad.setSeconds(0); + + return ad.getTime() - bd.getTime(); + }); + + console.log("line timepoints ........", time_points); + + monitor.info("-->comming events turn on/off lines:"); + for(let t = 0; t < time_points.length; t++) + { + + let start_time = new Date(); + let [hours, minutes, seconds] = time_points[t].start_time.split(':'); + + start_time.setHours( parseInt(hours) ); + start_time.setMinutes( parseInt(minutes) ); + start_time.setSeconds(0); + + //task is the past + if(now.getTime() > start_time.getTime()) + { + currentValue = time_points[t].value; + + //timepoint is in past, we add 24 hours + start_time.setDate(start_time.getDate() + 1); + } + + let params = getParams(PRIORITY_TYPES.relay_profile); + params.type = "relay"; + params.line = parseInt(line); + params.value = time_points[t].value; + params.tbname = relaysData[line].tbname; + params.timestamp = start_time.getTime(); + + params.addMinutesToTimestamp = 0; + + // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day + if(time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24*60; + + //astro timepoints will be recalculated dynamically: + params.timePointName = time_points[t].name; + + // if astro timepoint, we save time window: + if(['luxOn', 'luxOff', 'dusk','dawn'].includes(params.timePointName)) + { + params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; + params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; + } + + if(params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; + else if(params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; + + params.debug = true; + + //turn on/off line + tasks.push(params); + monitor.info(params.info, start_time); + + } + + monitor.info("-->time_points final", line, time_points); + + //ensure to turn on/off according to calculated value + let params = getParams(PRIORITY_TYPES.terminal); + params.type = "relay"; + params.line = parseInt(line); + params.tbname = relaysData[line].tbname; + params.value = currentValue; + + params.timestamp = PRIORITY_TYPES.terminal; + params.addMinutesToTimestamp = 0; + params.debug = true; + + //logger.debug(now.toLocaleString("sk-SK")); + monitor.info("-->currentValue for relay", line, currentValue); + + //turn on/off line + if(params.value == 0) params.info = "turn off line on startup: " + line; + else if(params.value == 1) params.info = "turn on line on startup: " + line; + + tasks.push(params); + + } catch (error) { + if(profilestr !=="" ) + { + //errLogger.error(profilestr, error); + errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); + } + } + + } + + //logger.debug("tasks:"); + //logger.debug(tasks); + } + + + //PROCESS DEFAULT BROADCASTS + + //RPC pre nody / broadcast + //Time of dusk, Time of dawn + //Actual Time + + if(processBroadcast) + { + let addMinutesToTimestamp = 5; + + { + //run broadcast Time of dusk + addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk + + let params = getParams(PRIORITY_TYPES.node_broadcast); + + let sunCalcResult = calculateDuskDawn(); + let dusk_hours = sunCalcResult["dusk_hours"]; + let dusk_minutes = sunCalcResult["dusk_minutes"]; + + params.address = 0xffffffff;//broadcast + params.byte1 = dusk_hours;//h + params.byte2 = dusk_minutes;//m + params.byte3 = 0;//s + params.byte4 = 0; + params.recipient = 2;//2 broadcast, + params.register = 6;//Time of dusk - Reg 6 + params.rw = 1;//write + + //other values + params.type = "cmd"; + params.timestamp = Date.now() + 60000; + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "Broadcast-duskTime"; + + tasks.push(params); + } + + { + + //run broadcast Time of dawn + // addMinutesToTimestamp = 60*5; + addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn + + let params = getParams(PRIORITY_TYPES.node_broadcast); + + let sunCalcResult = calculateDuskDawn(); + let dawn_hours = sunCalcResult["dawn_hours"]; + let dawn_minutes = sunCalcResult["dawn_minutes"]; + + params.address = 0xffffffff;//broadcast + params.byte1 = dawn_hours;//h + params.byte2 = dawn_minutes;//m + params.byte3 = 0;//s + params.byte4 = 0; + params.recipient = 2; //2 broadcast + params.register = 7;//Time of dawn - Reg 6 + params.rw = 1;//write + + //other values + params.type = "cmd"; + params.timestamp = Date.now() + 60000; + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "Broadcast-dawnTime"; + + tasks.push(params); + } + + { + //run broadcast //Actual time + addMinutesToTimestamp = 5; + + let params = getParams(PRIORITY_TYPES.node_broadcast); + + var d = new Date(); + let hours = d.getHours(); + let minutes = d.getMinutes(); + let seconds = d.getSeconds(); + + params.address = 0xffffffff;//broadcast + params.byte1 = hours;//h + params.byte2 = minutes;//m + params.byte3 = seconds;//s + params.byte4 = 0; + params.recipient = 2; //2 broadcast + params.register = 87;//Actual time + params.rw = 1;//write + + //other values + params.type = "cmd"; + params.timestamp = Date.now() + 60000; + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "run broadcast: Actual time"; + + tasks.push(params); + } + + } + + //process nodes & tasks + //reportovanie pre platformu + if(processNodes) + { + for (let k in nodesData) { + let address = parseInt(k); + let tbname = nodesData[k].tbname; + let register = 0; + + //logger.debug("generated cmd - buildTasks for node:", address); + + //listOfCommands - READ + for(let i = 0; i < listOfCommands.length; i++) + { + register = listOfCommands[i]; + + let params = getParams(PRIORITY_TYPES.node_cmd); + + //core rpc values + params.address = address; + params.byte1 = 0; + params.byte2 = 0; + params.byte3 = 0; + params.byte4 = 0; + params.recipient = 1; + params.register = register; + params.rw = 0; + + let addMinutesToTimestamp = priorities[register]; + + let timestampStart = PRIORITY_TYPES.node_cmd; //run imediatelly in function runTasks + if(addMinutesToTimestamp > 1) + { + timestampStart = timestampStart + addMinutesToTimestamp * 60000; + } + + //other values + params.type = "cmd"; + params.tbname = tbname; + params.timestamp = timestampStart; + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "generated cmd - buildTasks (node)"; + + //monitor last node && last command + /* + if(register == listOfCommands[ listOfCommands.length - 1 ]) + { + //if(k == 632) params.debug = true; + if(k == 698) params.debug = true; + } + */ + + tasks.push(params); + + } + } + } + + + //niektore ulohy sa vygeneruju iba 1x pri starte!!! + if(!init) return; + + + //Priebežne (raz za cca 5 minút) je potrebné vyčítať z Master nodu verziu jeho FW. + //Jedná sa o register 10. Rovnaká interpretácia ako pri FW verzii nodu. + //Adresa mastera je 0. V prípade že kedykoľvek nastane situácia že Master Node neodpovedá (napríklad pri vyčítaní telemetrie z nodu nevráti žiadne dáta), + //tak treba vyreportovať string "NOK". + { + let params = getParams(PRIORITY_TYPES.fw_detection); + params.type = "cmd-master"; + params.register = 4; + params.address = 0; + params.timestamp = Date.now() + 60000; + params.addMinutesToTimestamp = 5; + params.tbname = FLOW.OMS_edgeName; + params.info = "Master node FW verzia"; + //params.debug = true; + + //this will set FLOW.OMS_masterNodeIsResponding + + tasks.push(params); + } + + //kazdu hodinu skontrolovat nastavenie profilov + { + let params = getParams(PRIORITY_TYPES.fw_detection); + params.type = "process_profiles"; + params.timestamp = Date.now() + 60000; + params.addMinutesToTimestamp = 60;//60 = every hour + params.info = "detekcia nespracovaných profilov linie a nodov"; + //params.debug = true; + + tasks.push(params); + } + + monitor.info("tasks created:", tasks.length); + } + + + /** + * We process line profile, where "astro_clock": true + * example profile: + * + "dawn_lux_sensor": true, + "dusk_lux_sensor": true, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 0, + "dusk_astro_clock_offset": 10, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + + * if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line. + * if dusk: we do oposite + * + * dawn: usvit - lux je nad hranicou - vypnem + * dusk: sumrak - lux je pod hranicou - zapnem + */ + function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) + { + + let now = new Date(); + let currentTimestamp = now.getTime(); + let keys = Object.keys(relaysData); + + for(let i = 0; i < keys.length; i++) + { + + let line = keys[i]; //line is turned off by default + let profilestr = relaysData[line].profile; + const contactor = relaysData[line].contactor; + + try { + + let profile = JSON.parse(profilestr); + if(Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); + + if(profile.astro_clock == true) + { + let sunCalcResult = calculateDuskDawn(now, line); + + //usvit + if(profile.dawn_lux_sensor == true) + { + let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut + let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); + + if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) + { + if(lux_sensor_value > profile.dawn_lux_sensor_value) + { + if(contactor) turnOffLine(line, "Profile: dawn - turnOff line according to lux sensor"); + } + } + } + + //sumrak + if(profile.dusk_lux_sensor == true) + { + let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60); + let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60); + + if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) + { + if(lux_sensor_value < profile.dusk_lux_sensor_value) + { + if(!contactor) turnOnLine(line, "Profile: dusk - turnOn line according to lux sensor"); + } + } + } + + } + + } catch (error) { + if(profilestr !== "" ) monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); + } + + } + + } + + /** + * function updates status and time_of_last_communication of node in the dbNodes + * it only updates if conditions are met + * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) + * if newStatus of node is always receiving false, and it is already for more than FLOW.OMS_node_status_nok_time value, we update status to "NOK" in tb + * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). + */ + function updateNodeStatus(node, newStatus) + { + //MASTER + if(node == 0) return; + + let nodeObj = nodesData[node]; + if(nodeObj == undefined) return; + + let nodeCurrentStatus = nodeObj.status; + const now = Date.now(); + + if(newStatus == false && nodeCurrentStatus == false) { + if(node == 638 || node == 637) console.log("false, false, return", node, now) + return true; + } + + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION){ + + if(node == 638 || node == 637) console.log("true true, return", node, now); + return; + } + + if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) + { + dbNodes.modify({ time_of_last_communication: now}).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(!err) { + nodeObj.time_of_last_communication = now; + + if(node == 638 || node == 637) console.log('zapisane do db => status true & true', node, now) + } + }); + }); + return; + } + + if(newStatus == false && nodeCurrentStatus == true) + { + if(nodeObj.time_of_last_communication + FLOW.OMS_node_status_nok_time > now) { + if(node == 638 || node == 637) console.log('false true, return', node, now); + return; + } + else { + dbNodes.modify({ status: newStatus}).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(!err) { + nodeObj.status = newStatus; + + if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) + } + }); + }); + return true; + } + } + + if(newStatus == true && nodeCurrentStatus == false) + { + dbNodes.modify({ status: newStatus, time_of_last_communication: now}).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(!err) { + nodeObj.status = newStatus; + nodeObj.time_of_last_communication = now; + + if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) + } + }); + }); + return; + } + } + + + async function runTasks(){ + + clearInterval(interval); + + let currentTimestamp = Date.now(); + + //report dusk, dawn--------------------------------- + if(reportDuskDawn.dusk_time < currentTimestamp) + { + //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund + if( (currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) + { + //reportovali sme? + if(reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) + { + sendNotification("CMD Manager: calculated Time of dusk", FLOW.OMS_edgeName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", SEND_TO.tb, instance); + reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; + } + } + + var nextDay = new Date(); + nextDay.setDate(nextDay.getDate() + 1); + + sunCalcResult = calculateDuskDawn(nextDay); + reportDuskDawn.dusk_time = sunCalcResult.dusk_time; + } + + if(reportDuskDawn.dawn_time < currentTimestamp) + { + //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund + if( (currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) + { + //reportovali sme? + if(reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) + { + sendNotification("CMD Manager: calculated Time of dawn", FLOW.OMS_edgeName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", SEND_TO.tb, instance); + reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; + } + } + + var nextDay = new Date(); + nextDay.setDate(nextDay.getDate() + 1); + + sunCalcResult = calculateDuskDawn(nextDay); + reportDuskDawn.dawn_time = sunCalcResult.dawn_time; + + } + //-------------------------------------------------------- + + //sort tasks based on timestamp + tasks.sort(function (a, b) { + if(a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) + { + return a.priority - b.priority; + } + return a.timestamp - b.timestamp; + }); + + if(tasks.length == 0 ) + { + instance.send(SEND_TO.debug, "no tasks created"); + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + if(!rsPort.isOpen) + { + instance.send(SEND_TO.debug, "!rsPort.isOpen"); + //await rsPort.open(); + } + + let currentTask = tasks[0]; + + if(currentTask.debug) + { + //logger.debug("--->task to process", currentTask); + } + + if(currentTask.timestamp <= currentTimestamp) + { + let params = {...tasks[0]}; + + //allow terminal commands + if(FLOW.OMS_maintenance_mode && params.type !== "cmd-terminal") + { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + let type = params.type; + let tbname = params.tbname; + let nodeAddress = params.address; + + let line = null; + + //rpc related + if(nodesData[nodeAddress] !== undefined) line = nodesData[nodeAddress].line; + if(params.line !== undefined) line = params.line; + + let repeatTask = false; + if(params.addMinutesToTimestamp > 0 || params.timePointName) repeatTask = true; + + if(repeatTask) + { + if(type === "cmd" || type === "cmd-master") + { + //set next start time automatically + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } + } + else + { + tasks.shift(); + } + + //kontrola nespracovanych profilov nodov + if(type == "process_profiles") + { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + + //vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov + loadRelaysData(); + + interval = setInterval(runTasks, SHORT_INTERVAL); + return; + } + + + //relay + if(type == "relay") + { + + const timePointName = params.timePointName; + const value = params.value; + + let date = new Date(); + date.setDate(date.getDate() + 1);//next day + + let sunCalcResult; + sunCalcResult = calculateDuskDawn(date, params.line); + + if(timePointName == "dawn") + { + tasks[0].timestamp = sunCalcResult.dawn_time; + } + else if(timePointName == "dusk") + { + tasks[0].timestamp = sunCalcResult.dusk_time; + } + else if(timePointName == "luxOn") + { + tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; + } + else if(timePointName == "luxOff") + { + tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; + } + else if(timePointName == "profileTimepoint") + { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } + + let info = "aplikovany bod profilu"; + let message = ""; + if(value == 1) + { + turnOnLine(params.line, info); + message = "on"; + } + else if(value == 0) + { + turnOffLine(params.line, info); + message = "off"; + } + + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance ); + interval = setInterval(runTasks, SHORT_INTERVAL); + return; + } + + //zhodeny hlavny istic + let disconnected = false; + //if(rotary_switch_state == "Off") disconnected = true; + + //state_of_breaker[line] - alebo istic linie + if(state_of_breaker.hasOwnProperty(line)) + { + //if(state_of_breaker[line] == "Off") disconnected = true; + } + + //toto sa reportuje po prijati dat z dido_controlera + if(disconnected) + { + let values = {"status": "OFFLINE"}; + + logger.debug("disconnected", values); + logger.debug("rotary_switch_state", rotary_switch_state); + logger.debug("state_of_breaker", state_of_breaker[line]); + + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + //report only once! + if(!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; + + if(!disconnectedReport[tbname]) + { + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + } + + interval = setInterval(runTasks, SHORT_INTERVAL); + + return; + } + + disconnectedReport[tbname] = false; + + const register = params.register; + + //high_priority + if(!FLOW.OMS_masterNodeIsResponding) + { + //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version + errorHandler.sendMessageToService("Master node is not responding"); + + let stop = true; + + //fw version - register == 4 + if(type == "cmd-terminal" || register == 4) stop = false; + if(stop) + { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + } + + let relayStatus = 1; + if(relaysData[line] != undefined) + { + relayStatus = relaysData[line].contactor; + } + + if(line == 0) relayStatus = 0; + if(type == "cmd-terminal") relayStatus = 1; + + //check if rotary_switch_state == "Off" + + if(relayStatus == 0) + { + //console.log("------------------------------------relayStatus", relayStatus, line); + let values = {"status": "OFFLINE"}; + + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + + interval = setInterval(runTasks, SHORT_INTERVAL); + return; + } + + if(!rsPort.isOpen) + { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + //RE-CALCULATE VALUES + //set actual time for broadcast + if(register == 87 && params.recipient === 2) + { + var d = new Date(); + let hours = d.getHours(); + let minutes = d.getMinutes(); + let seconds = d.getSeconds(); + + params.byte1 = hours;//h + params.byte2 = minutes;//m + params.byte3 = seconds;//s + params.byte4 = 0; + } + + //SET DUSK/DAWN FOR BROADCAST + //Time of dusk + if(register == 6 && params.recipient === 2) + { + + if(type != "cmd-terminal") + { + let sunCalcResult = calculateDuskDawn(); + let dusk_hours = sunCalcResult["dusk_hours"]; + let dusk_minutes = sunCalcResult["dusk_minutes"]; + + params.byte1 = dusk_hours;//h + params.byte2 = dusk_minutes;//m + params.byte3 = 0;//s + params.byte4 = 0; + + //TODO astrohodiny + let dusk = "Time of dusk: " + sunCalcResult["dusk"]; + } + } + + //Time of dawn + if(register == 7 && params.recipient === 2) + { + if(type != "cmd-terminal") + { + let sunCalcResult = calculateDuskDawn(); + let dawn_hours = sunCalcResult["dawn_hours"]; + let dawn_minutes = sunCalcResult["dawn_minutes"]; + + params.byte1 = dawn_hours;//h + params.byte2 = dawn_minutes;//m + params.byte3 = 0;//s + params.byte4 = 0; + + //TODO astrohodiny + let dawn = "Time of dawn: " + sunCalcResult["dawn"]; + } + + } + //----------------------- + + instance.send(SEND_TO.debug, "address: " + nodeAddress + " register:" + register + "type: " + type); + + var startTime, endTime; + startTime = new Date(); + + let saveToTb = true; + if(!tbname) saveToTb = false; + let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) + + let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); + let readBytes = 11; + let timeout = 4000; + + // await keyword is important, otherwise incorrect data is returned! + await writeData(rsPort, resp, readBytes, timeout).then(function (data) { + + endTime = new Date(); + var timeDiff = endTime - startTime; + + //--1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data + //let bytes = data.slice(0); + let bytes = data; + let dataBytes = data.slice(5,9); + + let result = detectIfResponseIsValid(bytes); + + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let message = result.message; // OK, NOK + let message_type = result.type; + let error = result.error; + + if(params.debug != "generated cmd") + { + //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug, params); + } + + // if(params.hasOwnProperty("debug")) + // { + // if(params.debug) + // { + // console.log("detected response:", result); + + // logger.debug("writeData: done " + message_typetype + " duration: " + timeDiff + " type: " + params.debug, params, result); + // } + // } + + //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug); + //debug("writeData done", message_type, "duration", timeDiff, "message_type", params.debug, result); + + let values = {}; + + //CMD FINISHED + if(message == "OK") + { + + updateNodeStatus(nodeAddress, true); + + //write + if(type == "set_node_profile") + { + let result = cmdCounterResolve(nodeAddress); + if(result == 0) + { + dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { + builder.callback(function(err, response) { + + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); + + logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); + nodesData[nodeAddress].processed = true; + }); + }); + } + } + + //parse read response + if(params.rw == 0) + { + values = processResponse(register, dataBytes); //read + } + + if(itIsNodeCommand) + { + values.comm_status = "OK"; + values.status = "OK"; + } + + //master node + if(nodeAddress == 0) + { + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); + FLOW.OMS_masterNodeIsResponding = true; + if(register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version; + } + + //odoslanie príkazu z terminálu - dáta + if(type == "cmd-terminal") + { + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); + } + + if(params.debug) + { + //logger.debug("saveToTb", saveToTb, tbname, values); + } + + if(saveToTb) + { + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + } + else + { + if(type == "cmd-terminal") + { + terminalCommandResponse(params, "SUCCESS", data); + } + } + + } + else + { + + terminalCommandResponse(params, "ERROR", data) + handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); + + if(params.hasOwnProperty("debug")) + { + if(params.debug) + { + //logger.debug("writeData err: ", error, result, params); + logger.debug("writeData err: ", tbname, nodeAddress, register, values); + } + } + + //logger.debug(error, result, params); + } + }).catch(function(reason) { + + console.log("writeData catch exception", reason); + instance.send(SEND_TO.debug, reason); + + terminalCommandResponse(params, "FAILURE", null, reason); + handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); + + if(params.hasOwnProperty("debug")) + { + if(params.debug) + { + logger.debug("-->WRITE FAILED: " + reason, params.debug, params); + } + } + + }); + + } + else + { + // if(currentTask.debug) + // { + // //currentTask.timestamp <= currentTimestamp + // logger.debug("currentTask is not processed - task is in the future", currentTask); + // } + + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + //console.log("----->runTasks - setInterval", new Date()); + interval = setInterval(runTasks, SHORT_INTERVAL); + } + + + function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb){ + + let node = params.address; + let register = params.register; + let type = params.type; + let tbName = params.tbname; + if(!tbName) return; + + let values = {}; + + // console.log(message); + let updateStatus = updateNodeStatus(node, false); + + //master node + if(node == 0) + { + sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); + logger.debug("master_node_is_not_responding", params); + FLOW.OMS_masterNodeIsResponding = false; + + if(register == 4) values["master_node_version"] = "NOK"; + } + + if(type == "set_node_profile") + { + delete cmdCounter[node]; + logger.debug( "profil nebol úspešne odoslaný na node č. ", params); + sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", {node: node}, "", SEND_TO.tb, instance ); + } + + if(itIsNodeCommand) + { + values.comm_status = "NOK"; + } + + if(updateStatus) + { + values.status = "NOK"; + } + + // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); + if(saveToTb && Object.keys(values).length > 0) + { + + let dataToTb = { + [tbName]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); + } + + } + + + /** + * function handles requests from terminal + * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. + * FAILURE means, that we got into catch block of writeData function. + */ + function terminalCommandResponse(params, responseType, data=null, reason="") { //success, error, failure + + if(params.refFlowdataKey == undefined) + { + //console.log("params.refFlowdataKey is undefined", params); + return; + } + else + { + console.log("params.refFlowdataKey: ", params); + } + + let message = null; + let type = null; + + switch (responseType) { + case "SUCCESS": + message = "cmd-terminal SUCCESS"; + type = "SUCCESS"; + break; + case "ERROR": + message = "cmd-terminal FAILED"; + type = "ERROR"; + break; + case "FAILURE": + message = "ERROR WRITE FAILED: " + reason; + type = "ERROR"; + break; + default: + type = undefined; + } + + logger.debug(message); + logger.debug(params); + + //make http response + let responseObj = {} + responseObj["type"] = type; + + if(responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; + else responseObj["bytes"] = data; + + let refFlowdata = refFlowdataObj[params.refFlowdataKey]; + if(refFlowdata) + { + refFlowdata.data = responseObj; + instance.send(SEND_TO.http_response, refFlowdata); + } + } + + + /** + * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function + */ + function reportEdgeDateTimeAndNumberOfLuminaires(){ + + if(!FLOW.OMS_edgeName) return; + + //Number of ok and nok nodes on platform does not equals to total number of nodes. + //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + + const ts = Date.now(); + const keys = Object.keys(nodesData_clone); + + const number_of_luminaires = keys.length; + let number_of_ok_luminaires = 0; + let number_of_nok_luminaires = 0; + + for(let i = 0; i < keys.length; i++) + { + let key = keys[i]; + let nodeObj = nodesData_clone[key]; + if(nodeObj.tbname == undefined) continue; + + if(nodeObj.status) number_of_ok_luminaires++; + else number_of_nok_luminaires++; + } + + const values = { + "number_of_luminaires": number_of_luminaires, + "number_of_ok_luminaires": number_of_ok_luminaires, + "number_of_nok_luminaires": number_of_nok_luminaires, + "edge_date_time": ts + }; + + let dataToTb = { + [FLOW.OMS_edgeName]: [ + { + "ts": ts, + "values": values + } + ] + } + + tbHandler.sendToTb(dataToTb, instance); + //instance.send(SEND_TO.tb, dataToTb); + } + + //to ensure, edgeDateTime will be send to tb at full minute + customTasksInterval = setInterval(function() { + if(new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); + }, 1000); + + + //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" + // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM + // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI + + if(FLOW.OMS_serial_port == "" || FLOW.OMS_serial_port == undefined || FLOW.OMS_serial_port.length === 1) FLOW.OMS_serial_port = "ttymxc4"; + const rsPort = new SerialPort(`/dev/${FLOW.OMS_serial_port}`, { autoOpen: false }); + //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit + //rsPort.setMaxListeners(0); + + rsPort.on('open', async function() { + + logger.debug("CMD manager - rsPort opened sucess"); + + //loadRelaysData(); + + await runSyncExec(`stty -F /dev/${FLOW.OMS_serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) { + instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); + + logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); + + //APP START + let dataToInfoSender = {id: FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name}; + dataToInfoSender.fw_version = FLOW.OMS_edge_fw_version; + dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); + dataToInfoSender.__force__ = true; + + instance.send(SEND_TO.infoSender, dataToInfoSender); + + logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); + + }).catch(function (reason) { + instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); + }); + }); + + rsPort.on('error', function(err) { + + //TODO report to service!!! + //errLogger.error(exports.title, "unable to open port", FLOW.OMS_serial_port, err.message); + errorHandler.sendMessageToService([exports.title, "unable to open port", FLOW.OMS_serial_port, err.message], 0); + + instance.send(SEND_TO.debug, err.message); + }); + + rsPort.on("close", () => { + rsPort.close(); + }); + + rsPort.open(); + + instance.on("close", () => { + clearInterval(interval); + clearInterval(customTasksInterval); + rsPort.close(); + }); + + + instance.on("data", async function(flowdata) { + + //instance.send(SEND_TO.debug, "on Data"); + //instance.send(SEND_TO.debug, flowdata); + + //logger.debug(flowdata.data); + + //just testing functions + if(flowdata.data == "open") + { + if(!rsPort.isOpen) rsPort.open(); + return; + } + else if(flowdata.data == "close") + { + rsPort.close(); + return; + } + else if(flowdata.data == "clean") + { + tasks = []; + return; + } + else if(flowdata.data == "buildtasks") + { + //build & run + return; + } + else if(flowdata.data == "run") + { + //durations = []; + + if(tasks.length == 0) + { + + buildTasks(); + + if(rsPort.isOpen) + { + interval = setInterval(runTasks, 100); + } + else + { + instance.send(SEND_TO.debug, "port is not opened!!!"); + } + } + } + else + { + //terminal data - object + //logger.debug("flowdata", flowdata.data); + + if(typeof flowdata.data === 'object') + { + //logger.debug("dido", flowdata.data); + if(flowdata.data.hasOwnProperty("sender")) + { + //data from dido_controller + if(flowdata.data.sender == "dido_controller") + { + + if(flowdata.data.hasOwnProperty("cmd")) + { + let cmd = flowdata.data.cmd; + + if(cmd == "buildTasks") + { + clearInterval(interval); + + logger.debug("-->CMD MANAGER - BUILD TASKS"); + buildTasks(); + + //logger.debug("tasks:"); + //logger.debug(tasks); + + logger.debug("-->CMD MANAGER - RUN TASKS"); + interval = setInterval(runTasks, LONG_INTERVAL); + } + else if(cmd == "reload_relays") + { + loadRelaysData(flowdata.data.line); + + if(flowdata.data.dataChanged) + { + if(!flowdata.data.value) + { + reportOfflineNodeStatus(flowdata.data.line); + } + else + { + reportOnlineNodeStatus(flowdata.data.line); + } + } + + } + else if(cmd == "rotary_switch_state") + { + //state was changed + if(rotary_switch_state != flowdata.data.value) + { + if(rotary_switch_state == "Off") + { + //vyreportovat vsetky svietdla + reportOfflineNodeStatus(); + } + else reportOnlineNodeStatus(); + + } + + rotary_switch_state = flowdata.data.value; + } + else if(cmd == "lux_sensor") + { + lux_sensor = parseInt(flowdata.data.value); + + // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? + if(lux_sensor < 100) + { + + // we send lux_sensor value to all nodes: + let params = getParams(PRIORITY_TYPES.node_broadcast); + + params.recipient = 2;//2 broadcast, address = 0 + params.address = 0xffffffff;//Broadcast + + let ba = longToByteArray(lux_sensor); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + params.timestamp = PRIORITY_TYPES.node_broadcast; + params.info = "run broadcast: Actual Lux level from cabinet"; + params.register = 95;//Actual Lux level from cabinet + params.rw = 1;//write + + tasks.push(params); + + //process profiles + turnOnOffLinesAccordingToLuxSensor(lux_sensor); + } + } + else if(cmd == "state_of_breaker") + { + //istic linie + let value = flowdata.data.value; + let line = parseInt(flowdata.data.line); + + let dataChanged = false; + if(state_of_breaker[line] != value) dataChanged = true; + + state_of_breaker[line] = value; + + let status = "OK"; + if(value == "Off") status = "NOK"; + + if(dataChanged) { + + if(relaysData.hasOwnProperty(line)) + { + let tbname = relaysData[line].tbname; + + if(value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); + else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); + + //report status liniu + let values = { + "status": status + }; + + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] + } + + //instance.send(SEND_TO.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + + //current value + if(value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii + else reportOnlineNodeStatus(line); + } + + } + } + else{ + logger.debug("undefined cmd", cmd); + } + } + } + + return; + } + + //data from worksys + if(flowdata.data.hasOwnProperty("topic")) + { + + let data = flowdata.data.content.data; + + let command = data.params.command; + let method = data.method; + let profile = data.params.payload; + if(profile == undefined) profile = ""; + let entity = data.params.entities[0]; + let entity_type = entity.entity_type; + let tbname = entity.tb_name; + + instance.send(SEND_TO.debug, flowdata.data); + logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); + logger.debug("----------------------------"); + + if(entity_type == "street_luminaire"|| entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") + { + if(method == "set_command") + { + + //let command = data.params.command; + let value = data.params.payload.value; + + if(command == "dimming") + { + + let nodeWasFound = false; + let keys = Object.keys(nodesData); + + //logger.debug("-----", keys); + + for(let i = 0; i < keys.length; i++) + { + let node = keys[i]; + //logger.debug( node, nodesData[node], tbname); + + if(tbname == nodesData[node].tbname.trim()) + { + let params = getParams(PRIORITY_TYPES.high_priority); + + value = parseInt(value); + if(value > 0) value = value + 128; + + //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.byte4 = value; + params.rw = 1;//write + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'set dimming from platform'; + //params.debug = true; + + //ak linia je + + //debug(params); + logger.debug("dimming", params); + + tasks.push(params); + + setTimeout(function(){ + + //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority + //a pridame aj vyreportovanie dimmingu + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read dimming (after set dimming from platform)'; + params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - vykon + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 76; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Power (after set dimming from platform)'; + params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - prud svietidla + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 75; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Current (after set dimming from platform)'; + params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - power faktor - ucinnik + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 77; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read power factor - Cos phi (after set dimming from platform)'; + params.debug = true; + + tasks.push(params); + } + + },4000); + + + nodeWasFound = true; + + break; + } + } + + if(!nodeWasFound) + { + logger.debug("set dimming from platform", "unable to find tbname", tbname); + } + } + else + { + instance.send(SEND_TO.debug, "undefined command " + command); + logger.debug("undefined command", command); + } + + return; + } + else if(method == "set_profile") + { + //nastav profil nodu + logger.debug("-->set_profile for node", data.params); + logger.debug("------profile data", profile); + //instance.send(SEND_TO.debug, "set_profile" + command); + + let keys = Object.keys(nodesData); + for(let i = 0; i < keys.length; i++) + { + let node = keys[i]; + if(tbname == nodesData[node].tbname.trim()) + { + + if(profile != "") profile = JSON.stringify(profile); + dbNodes.modify({ processed: false, profile: profile }).where("node", node).make(function(builder) { + + builder.callback(function(err, response) { + + logger.debug("worksys - update node profile done", profile); + if(profile === "") logger.debug("worksys - update node profile done - profile is empty"); + + //profil úspešne prijatý pre node č. xx + sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, SEND_TO.tb, instance ); + + nodesData[node].processed = false; + nodesData[node].profile = profile; + + let line = nodesData[node].line; + processNodeProfile(node); + + }); + }); + } + } + } + else + { + + instance.send(SEND_TO.debug, "unknown method " + method); + logger.debug("unknown method", method); + + return; + } + } + + //nastav profil linie z platformy + else if(entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") + { + //profil linie + //relays.table line:number|tbname:string|contactor:number|profile:string + //najdeme line relaysData + + if(method == "set_profile") + { + + logger.debug("-->set_profile for line", data.params); + logger.debug("profile data:", profile); + + let keys = Object.keys(relaysData); + for(let i = 0; i < keys.length; i++) + { + let line = keys[i]; + if(tbname == relaysData[line].tbname) + { + //zmazeme tasky + removeTask({type: "relay", line: line}); + + if(profile != "") profile = JSON.stringify(profile); + dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) { + + builder.callback(function(err, response) { + + //update profile + logger.debug("worksys - update relay profile done:", profile); + instance.send(SEND_TO.debug, "worksys - update relay profile done"); + + loadRelaysData(line).then(function (data) { + logger.debug("loadRelaysData DONE for line", line); + buildTasks({processLineProfiles: true, line: line}); + }); + + sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", {line: line}, profile, SEND_TO.tb, instance ); + + }); + }); + break; + } + } + } + else if(method == "set_command") + { + let value = data.params.payload.value; + + if(command === "switch") + { + + // if we receive rpc from platform, to switch maintenance mode, we set OMS_maintenance_mode flow variable to value; + if(entity_type === "edb" || entity_type === "edb_ver4_se") FLOW.variables.OMS_maintenance_mode = value; + + let responseRelays = await promisifyBuilder(dbRelays.find().where("tbname", tbname)); + + let line = 0; + if(responseRelays.length == 1) line = responseRelays[0].line; + + if(value == false) turnOffLine(line, "command received form platform"); + else turnOnLine(line, "command received form platform"); + } + } + else + { + instance.send(SEND_TO.debug, "undefined method " + method); + logger.debug("undefined method", method); + } + + return; + } + else + { + instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); + logger.debug("UNKNOW entity_type", entity_type); + } + return; + } + + //terminal + if(!rsPort.isOpen) await rsPort.open(); + + let params = flowdata.data.body; + if(params == undefined) + { + //logger.debug("CMD manager flowdata.data.body is undefined"); + return; + } + + params.priority = PRIORITY_TYPES.terminal; + params.type = "cmd-terminal"; + params.tbname = ""; + params.timestamp = PRIORITY_TYPES.terminal; + params.addMinutesToTimestamp = 0;// do not repeat task!!! + params.debug = true; + + let timestamp = Date.now(); + params.refFlowdataKey = timestamp; + //params.refFlowdata = flowdata; + //refFlowdata = flowdata; + + //console.log("flowdata", flowdata); + + cleanUpRefFlowdataObj(); + + refFlowdataObj[ timestamp ] = flowdata; + + //fix + //params.address = params.adress; + logger.debug("received from terminal", params); + logger.debug("date/time:", new Date()); + logger.debug("tasks length:", tasks.length); + + //tasks = []; + + //add to tasks + tasks.push(params); + + } + } + }) + +} // end of instance.export + + +/** + * setCorrectTime function runs once per hour + * If it is 3 o'clock, it sets actual time, which is got from services + * https://service-prod01.worksys.io/gettime + * If also detects Read Only Filesystem once a day + */ +function setCorrectPlcTimeOnceADay() +{ + + const currentTime = new Date(); + if(currentTime.getHours() != 3) return; + + RESTBuilder.make(function(builder) { + + if(!builder) return; + + builder.method('GET'); + builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); + + builder.callback(function(err, response, output) { + + if (err) { + console.log(err); + return; + } + + const res = output.response; + + try { + + const obj = JSON.parse(res); + let d = new Date(obj.date); + + const now = new Date(); + + let diffInMinutes = now.getTimezoneOffset(); + console.log("---->TimezoneOffset", diffInMinutes); + + if(d instanceof Date) + { + + // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); + + let year = d.getFullYear(); + let month = addZeroBefore(d.getMonth() + 1); + let day = addZeroBefore(d.getDate()); + + let hours = addZeroBefore(d.getHours()); + let minutes = addZeroBefore(d.getMinutes() ); + let seconds = addZeroBefore(d.getSeconds()); + + let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); + console.log(dateStr); + + monitor.info("failed timedatectl set-time", err, stderr); + } + else + { + monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); + } + + }); + } + + } catch (error) { + logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); + monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); + } + + // we detect readOnlyFileSystem once an hour as well + detectReadOnlyFilesystem(); + + }); + }); + +} + + +function detectReadOnlyFilesystem() +{ + exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); + + } else { + //console.log("Read-only", stdout); + + let lines = stdout + ""; + lines = lines.split("\n"); + + let readOnlyDetected = ""; + for(let i = 0; i < lines.length; i++) + { + if(lines[i].startsWith("/dev/mmcblk0p2")) + { + readOnlyDetected = lines[i]; + } + } + + if(readOnlyDetected !== "") + { + errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); + monitor.info("Read only filesystem detected"); + } + + } + }); +} + +let setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour +setCorrectPlcTimeOnceADay(); + + + + + + + + + +///helper functions + +function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) +{ + + if(date === undefined) date = new Date(); + //if(duskOffset === undefined) duskOffset = 0; + //if(dawnOffset === undefined) dawnOffset = 0; + + //let line = keys[i]; + let profilestr = ""; + if(relaysData[line] != undefined) profilestr = relaysData[line].profile; + + let result = {}; + + var times = SunCalc.getTimes(date, latitude, longitude); + let dawn = new Date(times.sunrise);//usvit + let dusk = new Date(times.sunset);//sumrak + + + //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 + //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 + + + let dusk_astro_clock_offset = duskOffset;//minutes + let dawn_astro_clock_offset = dawnOffset;//minutes + + try { + + let profile = JSON.parse(profilestr); + if(Object.keys(profile).length === 0) throw ("profile is not defined"); + + //Jednoduchý režim + if(profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) + { + + } + + //Režim astrohodín + if(profile.astro_clock == true) + { + //if(profile.dusk_lux_sensor == false) + { + if(profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt( profile.dusk_astro_clock_offset ); + } + + //if(profile.dawn_lux_sensor == false) + { + if(profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt( profile.dawn_astro_clock_offset ); + } + + } + + //dusk - súmrak + //down, sunrise - svitanie + + } catch (error) { + if(profilestr != "") + { + logger.debug(profilestr); + logger.debug(error); + } + } + + result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + + dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset*60000); + dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset*60000); + + result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dusk_hours = dusk.getHours(); + result.dusk_minutes = dusk.getMinutes(); + + result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + result.dawn_hours = dawn.getHours(); + result.dawn_minutes = dawn.getMinutes(); + + result.dusk_time = dusk.getTime(); + result.dawn_time = dawn.getTime(); + + result.dusk_astro_clock_offset = dusk_astro_clock_offset; + result.dawn_astro_clock_offset = dawn_astro_clock_offset; + + return result; +} + + +function processResponse(register, bytes) +{ + + let values = {}; + + let byte3 = bytes[0]; + let byte2 = bytes[1]; + let byte1 = bytes[2]; + let byte0 = bytes[3]; + + //status + if(register == 0) + { + let statecode = bytesToInt(bytes); + values = {"statecode": statecode}; + return values; + } + + //Dimming, CCT + if(register == 1) + { + let brightness = 0; + let dimming = byte0; + if(dimming > 128) { + //dimming = -128; + brightness = dimming - 128; + } + + //cct + //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 + let cct; + if(byte3 == 1) cct = byte2*256 + byte1; + else cct = bytesToInt(bytes.slice(0, 3)); + + //cct podla auditu + + values["dimming"] = brightness; + return values; + } + + // + if(register == 4) + { + values["master_node_version"] = bytes[1] + "." + bytes[2]; + //logger.debug("FW Version", register, bytes); + } + + //Napätie + if(register == 74) + { + let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); + values["voltage"] = Number(voltage); + } + + //Prúd + if(register == 75) + { + let current = bytesToInt(bytes); + values["current"] = current; + } + + //výkon + if(register == 76) + { + let power = (bytesToInt(bytes) * 0.1).toFixed(2); + values["power"] = Number(power); + } + + //účinník + if(register == 77) + { + let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); + values["power_factor"] = Number(power_factor); + } + + //frekvencia + if(register == 78) + { + let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); + values["frequency"] = Number(frequency); + } + + //energia + if(register == 79) + { + let energy = bytesToInt(bytes); + + //Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal. + + values["energy"] = energy / 1000; + } + + //doba života + if(register == 80) + { + let lifetime = ( bytesToInt(bytes) / 60).toFixed(2); + values["lifetime"] = Number(lifetime); + } + + //nastavenie profilu + if(register == 8) + { + let time_schedule_settings = bytesToInt(bytes); + values["time_schedule_settings"] = time_schedule_settings; + } + + //skupinová adresa 1 + if(register == 3) + { + let gr_add_1 = bytesToInt(byte0); + values["gr_add_1"] = gr_add_1; + + let gr_add_2 = bytesToInt(byte1); + values["gr_add_2"] = gr_add_2; + + let gr_add_3 = bytesToInt(byte2); + values["gr_add_3"] = gr_add_3; + + let gr_add_4 = bytesToInt(byte3); + values["gr_add_4"] = gr_add_4; + } + + //naklon + if(register == 84) + { + let temp; + if(byte3 >= 128) + { + temp = (byte3 - 128) * (-1); + } + else + { + temp = byte3; + } + + let inclination_x; + if(byte2 >= 128) + { + inclination_x = (byte2 - 128) * (-1); + } + else + { + inclination_x = byte2; + } + + let inclination_y; + if(byte1 >= 128) + { + inclination_y = (byte1 - 128) * (-1); + } + else + { + inclination_y = byte1; + } + + let inclination_z; + if(byte0 >= 128) + { + inclination_z = (byte0 - 128) * (-1); + } + else + { + inclination_z = byte0; + } + + values["temperature"] = temp; + + //náklon x + values["inclination_x"] = inclination_x; + + //náklon y + values["inclination_y"] = inclination_y; + + //náklon z + values["inclination_z"] = inclination_z; + } + + let h = byte3; + let m = byte2; + let s = byte1; + + let timestamp; + + if(register == 87 || register == 6 || register == 7 ) + { + //if(byte3 < 10) h = "0" + byte3; + //if(byte2 < 10) m = "0" + byte2; + //if(byte1 < 10) s = "0" + byte1; + + var d = new Date(); + d.setHours(h); + d.setMinutes(m); + d.setSeconds(s); + + timestamp = d.getTime(); + } + + //aktuálny čas + if(register == 87) + { + //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. + //values["actual_time"] = h + ":" + m + ":" + s; + + values["actual_time"] = timestamp; + } + + //čas súmraku + if(register == 6) + { + //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. + //values["dusk_time"] = h + ":" + m + ":" + s; + + values["dusk_time"] = timestamp; + } + + //čas úsvitu + if(register == 7) + { + //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. + //values["dawn_time"] = h + ":" + m + ":" + s; + + values["dawn_time"] = timestamp; + } + + //FW verzia + if(register == 89) + { + //formát: "Byte3: Byte2.Byte1 (Byte0)" + values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; + } + + return values; +} + + +//byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB +function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { + let resp = []; + + let cmd = register; + + if (typeof adresa === 'string') adresa = parseInt(adresa); + if (typeof byte1 === 'string') byte1 = parseInt(byte1); + if (typeof byte2 === 'string') byte2 = parseInt(byte2); + if (typeof byte3 === 'string') byte3 = parseInt(byte3); + if (typeof byte4 === 'string') byte4 = parseInt(byte4); + + if (rw === 0) + { + cmd = cmd + 0x8000; + } + + //master + if(rec === 0) adresa = 0; + + if(rec === 2) + { + adresa = 0xffffffff;//Broadcast + } + + //recipient + if (rec === 3) + { + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push( adresa & 0xFF );//band + } + else + { + resp.push( (adresa >> 24) & 0xFF);//rshift + resp.push( (adresa >> 16) & 0xFF); + resp.push( (adresa >> 8) & 0xFF); + resp.push( adresa & 0xFF ); + + if (rec === 2) + { + resp.push(0xFF); + } + else resp.push(0); + } + + resp.push( (cmd >> 8) & 0xFF);//rshift + resp.push( cmd & 0xFF );//band + resp.push( byte1 & 0xFF );//band + resp.push( byte2 & 0xFF );//band + resp.push( byte3 & 0xFF );//band + resp.push( byte4 & 0xFF );//band + + //let data = '12345'; + let crc = crc16('ARC', resp); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; + + resp.push(c1); + resp.push(c2); + + //logger.debug("checksum", crc); + //logger.debug("resp", resp); + + return resp; + +} + + + + + + +// SAMPLE DATA + +const relaysDataExample = +{ + '0': { + line: 0, + tbname: 'jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV', + contactor: 1, + profile: '' + }, + '1': { + line: 1, + tbname: 'MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O', + contactor: 1, + profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' + }, + '2': { + line: 2, + tbname: 'jBL12pg63eX4N9P7zy0lJLyEJKmlbkGwZMx0avQV', + contactor: 1, + profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' + }, + '3': { + line: 3, + tbname: 'aAOzENGrvpbe0VoK7D6E1a819PZmdg3nl24JLQMk', + contactor: 1, + profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' + } +} + + +const rpcSwitchOffLine = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 8, + "method": "set_command", + "params": { + "entities": [ + { + "entity_type": "edb_line", + "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" + } + ], + "command": "switch", + "payload": { + "value": false + } + } + } + } +} + +const rpcSetNodeDimming = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 10, + "method": "set_command", + "params": { + "entities": [ + { + "entity_type": "street_luminaire", + "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" + } + ], + "command": "dimming", + "payload": { + "value": 5 + } + } + } + } +} + +const rpcLineProfile = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 9, + "method": "set_profile", + "params": { + "entities": [ + { + "entity_type": "edb_line", + "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" + } + ], + "payload": { + "intervals": [ + { + "value": 0, + "end_time": "20:00", + "start_time": "13:00" + }, + { + "value": 1, + "end_time": "05:30", + "start_time": "20:00" + }, + { + "value": 0, + "end_time": "13:00", + "start_time": "05:30" + } + ], + "astro_clock": true, + "dawn_lux_sensor": false, + "dusk_lux_sensor": false, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 0, + "dusk_astro_clock_offset": 0, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + } + } + } + } +} + + +const rpcNodeProfile = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 11, + "method": "set_profile", + "params": { + "entities": [ + { + "entity_type": "street_luminaire", + "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" + } + ], + "payload": { + "intervals": [ + { + "cct": 3000, + "value": 0, + "end_time": "17:50", + "start_time": "13:00" + }, + { + "cct": 3000, + "value": 100, + "end_time": "21:30", + "start_time": "17:50" + }, + { + "cct": 3000, + "value": 0, + "end_time": "13:00", + "start_time": "07:10" + }, + { + "cct": 3000, + "value": 50, + "end_time": "00:00", + "start_time": "21:30" + }, + { + "cct": 3000, + "value": 10, + "end_time": "04:30", + "start_time": "00:00" + }, + { + "cct": 3000, + "value": 100, + "end_time": "07:10", + "start_time": "04:30" + } + ], + "astro_clock": true, + "dawn_lux_sensor": false, + "dusk_lux_sensor": false, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 30, + "dusk_astro_clock_offset": 20, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + } + } + } + } +} + + const sunCalcExample = { + dusk_no_offset: '20:18', + dawn_no_offset: '05:19', + dusk: '20:18', + dusk_hours: 20, + dusk_minutes: 18, + dawn: '05:19', + dawn_hours: 5, + dawn_minutes: 19, + dusk_time: 1715278688962, + dawn_time: 1715224744357, + dusk_astro_clock_offset: 0, + dawn_astro_clock_offset: 0 +} diff --git a/flow/db_init.js b/flow/db_init.js new file mode 100644 index 0000000..5fb447e --- /dev/null +++ b/flow/db_init.js @@ -0,0 +1,108 @@ +exports.id = 'db_init'; +exports.title = 'DB Initialization'; +exports.group = 'Worksys'; +exports.color = '#888600'; +exports.version = '1.0.2'; +exports.icon = 'sign-out'; +exports.input = 1; +exports.output = ["blue"]; + +exports.html = `
+
+
+
Hostname or IP address (if not empty - setting will override db setting)
+
+
+
Port
+
+
+
+
+
@(Client id)
+
+
+
@(Username)
+
+
+
`; + + +exports.readme = ` +# DB initialization +`; + +const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); +const { initNotification } = require('./helper/notification_reporter'); + + +exports.install = async function(instance) { + + const dbNodes = TABLE("nodes"); + const dbRelays = TABLE("relays"); + const dbSettings = TABLE("settings"); + const dbStatus = TABLE("status"); + const dbPins = TABLE("pins"); + const dbNotifications = TABLE("notifications"); + + FLOW.GLOBALS = {}; + const dbs = FLOW.GLOBALS; + + const responseSettings = await promisifyBuilder(dbSettings.find()); + const responseNodes = await promisifyBuilder(dbNodes.find()); + const responsePins = await promisifyBuilder(dbPins.find()); + const responseStatus = await promisifyBuilder(dbStatus.find()); + const responseRelays = await promisifyBuilder(dbRelays.find()); + const response = await promisifyBuilder(dbNotifications.find()); + + dbs.pinsData = makeMapFromDbResult(responsePins, "pin"); + dbs.relaysData = makeMapFromDbResult(responseRelays, "line"); + dbs.nodesData = makeMapFromDbResult(responseNodes, "node"); + dbs.statusData = responseStatus[0]; + dbs.notificationsData = makeMapFromDbResult(response, "key"); + + //+|354|nodesdata.....+|482|nodesdata.... + //for some reason, if last line in nodes.table is not empty, flow wrote more nodes data in one row, + //so we have to add empty line at the bottom of nodes table to avoid this. + //now, remove empty lines from nodesData database: + if(dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; + + dbs.settings = { + edge_fw_version : "2024-11-04", //rok-mesiac-den + language : responseSettings[0]["lang"], + rvo_name : responseSettings[0]["rvo_name"], + project_id : responseSettings[0]["project_id"], + rvoTbName : dbs.relaysData[0]["tbname"], + temperature_address : responseSettings[0]["temperature_address"], + controller_type : responseSettings[0]["controller_type"], + serial_port : responseSettings[0]["serial_port"], + node_status_nok_time : responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000 ,// hour * minutes * + latitude : responseSettings[0]["latitude"], + longitude : responseSettings[0]["longitude"], + no_voltage : new Set(),//modbus_citysys - elektromer + backup_on_failure : responseSettings[0]["backup_on_failure"], + restore_from_backup : responseSettings[0]["restore_from_backup"], + restore_backup_wait : responseSettings[0]["restore_backup_wait"], + mqtt_host : responseSettings[0]["mqtt_host"], + mqtt_clientid : responseSettings[0]["mqtt_clientid"], + mqtt_username : responseSettings[0]["mqtt_username"], + mqtt_port : responseSettings[0]["mqtt_port"], + phases: responseSettings[0]["phases"], + + //dynamic values + masterNodeIsResponding : true, //cmd_manager + maintenance_mode : false, + } + + FLOW.dbLoaded = true; + initNotification(); + + setTimeout(()=> { + console.log("DB_INIT - data loaded"); + instance.send(0, "_") + }, 5000) + +}; + + + + diff --git a/flow/designer.json b/flow/designer.json index 7d57d69..09abdc3 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -25,8 +25,8 @@ "component": "debug", "tab": "1611921777196", "name": "ERROR", - "x": 401, - "y": 31, + "x": 598, + "y": 60, "connections": {}, "disabledio": { "input": [], @@ -49,8 +49,8 @@ "component": "wsmqttpublish", "tab": "1612772287426", "name": "WS MQTT publish", - "x": 311.75, - "y": 248, + "x": 310.75, + "y": 263, "connections": { "0": [ { @@ -77,6 +77,12 @@ "index": "0", "id": "1634303685503" } + ], + "3": [ + { + "index": "0", + "id": "1731068754606" + } ] }, "disabledio": { @@ -101,17 +107,17 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "tb-push", - "x": 68.75, - "y": 269, + "x": 85.75, + "y": 352, "connections": { "0": [ { "index": "0", - "id": "1612776786008" + "id": "1612783322136" }, { - "index": "0", - "id": "1612783322136" + "index": "1", + "id": "1612776786008" } ] }, @@ -134,8 +140,8 @@ "component": "debug", "tab": "1612772287426", "name": "to TB", - "x": 317.75, - "y": 154, + "x": 323.75, + "y": 429, "connections": {}, "disabledio": { "input": [ @@ -160,8 +166,8 @@ "component": "debug", "tab": "1612772287426", "name": "errors from MQTT Broker", - "x": 610, - "y": 111, + "x": 743, + "y": 65, "connections": {}, "disabledio": { "input": [ @@ -186,8 +192,8 @@ "component": "debug", "tab": "1615551125555", "name": "Debug", - "x": 755, - "y": 19, + "x": 753, + "y": 150, "connections": {}, "disabledio": { "input": [ @@ -212,8 +218,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "tb-push", - "x": 753, - "y": 112, + "x": 761, + "y": 251, "connections": {}, "disabledio": { "input": [], @@ -234,8 +240,8 @@ "component": "debug", "tab": "1615551125555", "name": "CMD_debug", - "x": 750, - "y": 197, + "x": 765, + "y": 350, "connections": {}, "disabledio": { "input": [ @@ -260,8 +266,8 @@ "component": "debug", "tab": "1611921777196", "name": "Debug", - "x": 398.8833312988281, - "y": 528.3500061035156, + "x": 595.8833312988281, + "y": 557.3500061035156, "connections": {}, "disabledio": { "input": [ @@ -286,8 +292,8 @@ "component": "debug", "tab": "1611921777196", "name": "Debug", - "x": 401.8833312988281, - "y": 625.3500061035156, + "x": 598.8833312988281, + "y": 654.3500061035156, "connections": {}, "disabledio": { "input": [ @@ -312,8 +318,8 @@ "component": "virtualwireout", "tab": "1611921777196", "name": "tb-push", - "x": 400.8833312988281, - "y": 328.25, + "x": 594.8833312988281, + "y": 350.25, "connections": {}, "disabledio": { "input": [], @@ -334,17 +340,17 @@ "component": "httproute", "tab": "1615551125555", "name": "POST /terminal", - "x": 72, - "y": 350, + "x": 114, + "y": 546, "connections": { "0": [ { "index": "0", - "id": "1619515097737" + "id": "1684060205000" }, { - "index": "0", - "id": "1684060205000" + "index": "1", + "id": "1619515097737" } ] }, @@ -380,8 +386,8 @@ "component": "httpresponse", "tab": "1615551125555", "name": "HTTP Response", - "x": 751, - "y": 273, + "x": 772, + "y": 443, "connections": {}, "disabledio": { "input": [], @@ -402,8 +408,8 @@ "component": "debug", "tab": "1615551125555", "name": "DIDO_Debug", - "x": 739, - "y": 635, + "x": 669, + "y": 1040, "connections": {}, "disabledio": { "input": [ @@ -428,8 +434,8 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOff line", - "x": 71, - "y": 829, + "x": 88, + "y": 1158, "connections": { "0": [ { @@ -458,8 +464,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "tb-push", - "x": 741, - "y": 736, + "x": 669, + "y": 1150, "connections": {}, "disabledio": { "input": [], @@ -480,8 +486,8 @@ "component": "debug", "tab": "1615551125555", "name": "Debug", - "x": 605, - "y": 1024, + "x": 700, + "y": 1495, "connections": {}, "disabledio": { "input": [], @@ -504,8 +510,8 @@ "component": "trigger", "tab": "1615551125555", "name": "start import", - "x": 235, - "y": 1032, + "x": 330, + "y": 1503, "connections": { "0": [ { @@ -534,8 +540,8 @@ "component": "csv_import", "tab": "1615551125555", "name": "CsvImport", - "x": 414, - "y": 1013, + "x": 509, + "y": 1484, "connections": { "0": [ { @@ -563,8 +569,8 @@ "component": "comment", "tab": "1615551125555", "name": "import data from csv", - "x": 401, - "y": 946, + "x": 496, + "y": 1417, "connections": {}, "disabledio": { "input": [], @@ -583,12 +589,12 @@ "component": "trigger", "tab": "1615551125555", "name": "update profile / node", - "x": 80, - "y": 13, + "x": 122, + "y": 209, "connections": { "0": [ { - "index": "0", + "index": "1", "id": "1619515097737" } ] @@ -613,12 +619,12 @@ "component": "trigger", "tab": "1615551125555", "name": "tun tasks", - "x": 77, - "y": 84, + "x": 119, + "y": 280, "connections": { "0": [ { - "index": "0", + "index": "1", "id": "1619515097737" } ] @@ -642,8 +648,8 @@ "component": "debug", "tab": "1612772287426", "name": "wsmqtt-exit1", - "x": 610.8833312988281, - "y": 199, + "x": 742.8833312988281, + "y": 153, "connections": {}, "disabledio": { "input": [], @@ -666,8 +672,8 @@ "component": "debug", "tab": "1612772287426", "name": "wsmqtt-exit2", - "x": 611.8833312988281, - "y": 374, + "x": 1064.8833312988281, + "y": 292, "connections": {}, "disabledio": { "input": [ @@ -692,8 +698,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "to-cmd-manager", - "x": 740.8833312988281, - "y": 828, + "x": 668.8833312988281, + "y": 1269, "connections": {}, "disabledio": { "input": [], @@ -714,12 +720,12 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "platform-rpc-call", - "x": 77.88333129882812, - "y": 173, + "x": 119.88333129882812, + "y": 369, "connections": { "0": [ { - "index": "0", + "index": "1", "id": "1619515097737" } ] @@ -743,8 +749,8 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "cmd_to_dido", - "x": 76.88333129882812, - "y": 678, + "x": 93.88333129882812, + "y": 1007, "connections": { "0": [ { @@ -776,8 +782,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "cmd_to_dido", - "x": 748.8833312988281, - "y": 373, + "x": 779.8833312988281, + "y": 552, "connections": {}, "disabledio": { "input": [], @@ -798,8 +804,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "platform-rpc-call", - "x": 611.8833312988281, - "y": 287, + "x": 739.8833312988281, + "y": 246, "connections": {}, "disabledio": { "input": [], @@ -820,8 +826,8 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOn line", - "x": 72, - "y": 756, + "x": 89, + "y": 1085, "connections": { "0": [ { @@ -850,8 +856,8 @@ "component": "cmd_manager", "tab": "1615551125555", "name": "CMD Manager", - "x": 420, - "y": 156, + "x": 448.1091003417969, + "y": 351.05455017089844, "connections": { "0": [ { @@ -905,17 +911,17 @@ "component": "httproute", "tab": "1615551125555", "name": "GET db", - "x": 73, - "y": 455, + "x": 115, + "y": 651, "connections": { "0": [ { "index": "0", - "id": "1619515097737" + "id": "1684060205000" }, { - "index": "0", - "id": "1684060205000" + "index": "1", + "id": "1619515097737" } ] }, @@ -950,8 +956,8 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOnAlarm", - "x": 68, - "y": 902, + "x": 85, + "y": 1231, "connections": { "0": [ { @@ -980,8 +986,8 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOffAlarm", - "x": 67, - "y": 975, + "x": 84, + "y": 1304, "connections": { "0": [ { @@ -1010,8 +1016,8 @@ "component": "virtualwireout", "tab": "1611921777196", "name": "modbus_to_dido", - "x": 399, - "y": 433, + "x": 596, + "y": 462, "connections": {}, "disabledio": { "input": [], @@ -1121,8 +1127,8 @@ "component": "monitormemory", "tab": "1612772287426", "name": "RAM", - "x": 71.88333129882812, - "y": 609.5, + "x": 77.88333129882812, + "y": 703.5, "connections": { "0": [ { @@ -1136,7 +1142,7 @@ "output": [] }, "state": { - "text": "834.19 MB / 985.68 MB", + "text": "847.42 MB / 985.68 MB", "color": "gray" }, "options": { @@ -1151,8 +1157,8 @@ "component": "monitordisk", "tab": "1612772287426", "name": "disk", - "x": 72.88333129882812, - "y": 706.5, + "x": 78.88333129882812, + "y": 800.5, "connections": { "0": [ { @@ -1166,7 +1172,7 @@ "output": [] }, "state": { - "text": "5.84 GB / 7.26 GB", + "text": "5.79 GB / 7.26 GB", "color": "gray" }, "options": { @@ -1182,8 +1188,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "send-to-services", - "x": 5.883331298828125, - "y": 1069.5, + "x": 38.883331298828125, + "y": 1220.5, "connections": { "0": [ { @@ -1191,7 +1197,7 @@ "id": "1634463186563" }, { - "index": "0", + "index": "1", "id": "1634488120710" } ] @@ -1215,8 +1221,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "send-to-services", - "x": 428.8833312988281, - "y": 602.5, + "x": 434.8833312988281, + "y": 696.5, "connections": {}, "disabledio": { "input": [], @@ -1237,8 +1243,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "send-to-services", - "x": 612.8833312988281, - "y": 462.5, + "x": 740.8833312988281, + "y": 341.5, "connections": {}, "disabledio": { "input": [], @@ -1258,10 +1264,10 @@ "id": "1634303743260", "component": "httprequest", "tab": "1612772287426", - "name": "http://192.168.252.2:8004/sentmessage", + "name": "192.168.252.2:8004/sentmessage", "reference": "", - "x": 439.8833312988281, - "y": 1076.7333374023438, + "x": 467.8833312988281, + "y": 1154.7333374023438, "connections": { "0": [ { @@ -1291,8 +1297,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 234.75, - "y": 1024, + "x": 267.75, + "y": 1266, "connections": {}, "disabledio": { "input": [ @@ -1317,8 +1323,8 @@ "component": "code", "tab": "1612772287426", "name": "Code", - "x": 255, - "y": 512, + "x": 261, + "y": 606, "connections": { "0": [ { @@ -1352,8 +1358,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 430, - "y": 508, + "x": 436, + "y": 602, "connections": {}, "disabledio": { "input": [ @@ -1378,8 +1384,8 @@ "component": "code", "tab": "1612772287426", "name": "Code", - "x": 244, - "y": 608, + "x": 250, + "y": 702, "connections": { "0": [ { @@ -1413,8 +1419,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 431, - "y": 700, + "x": 437, + "y": 794, "connections": {}, "disabledio": { "input": [ @@ -1439,8 +1445,8 @@ "component": "code", "tab": "1612772287426", "name": "Code", - "x": 247, - "y": 702, + "x": 253, + "y": 796, "connections": { "0": [ { @@ -1474,8 +1480,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 434, - "y": 792, + "x": 440, + "y": 886, "connections": {}, "disabledio": { "input": [ @@ -1500,8 +1506,8 @@ "component": "debug", "tab": "1612772287426", "name": "Send info", - "x": 438, - "y": 1185, + "x": 467, + "y": 1261, "connections": {}, "disabledio": { "input": [ @@ -1526,8 +1532,8 @@ "component": "infosender", "tab": "1612772287426", "name": "Info sender", - "x": 233, - "y": 1122, + "x": 272, + "y": 1158, "connections": { "0": [ { @@ -1559,8 +1565,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 811.8833312988281, - "y": 1070.5, + "x": 782.8833312988281, + "y": 1149.5, "connections": {}, "disabledio": { "input": [ @@ -1585,8 +1591,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "send-to-services", - "x": 748, - "y": 464, + "x": 778, + "y": 656, "connections": {}, "disabledio": { "input": [], @@ -1607,8 +1613,8 @@ "component": "monitorconsumption", "tab": "1612772287426", "name": "CPU", - "x": 71, - "y": 515, + "x": 77, + "y": 609, "connections": { "0": [ { @@ -1622,7 +1628,7 @@ "output": [] }, "state": { - "text": "1.9% / 86.94 MB", + "text": "1.3% / 74.34 MB", "color": "gray" }, "options": { @@ -1641,8 +1647,8 @@ "component": "debug", "tab": "1615551125555", "name": "CMDtoDIDO", - "x": 413, - "y": 654, + "x": 392, + "y": 1012, "connections": {}, "disabledio": { "input": [ @@ -1667,17 +1673,17 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "from-dido-controller", - "x": 71, - "y": 260, + "x": 113, + "y": 456, "connections": { "0": [ { "index": "0", - "id": "1619515097737" + "id": "1684055037116" }, { - "index": "0", - "id": "1684055037116" + "index": "1", + "id": "1619515097737" } ] }, @@ -1700,8 +1706,8 @@ "component": "debug", "tab": "1615551125555", "name": "from dido to cmd", - "x": 423, - "y": 331, + "x": 448, + "y": 519, "connections": {}, "disabledio": { "input": [ @@ -1726,8 +1732,8 @@ "component": "debug", "tab": "1615551125555", "name": "HTTP routes", - "x": 423, - "y": 422, + "x": 447, + "y": 620, "connections": {}, "disabledio": { "input": [ @@ -1752,8 +1758,8 @@ "component": "debug", "tab": "1611921777196", "name": "MDBToDido", - "x": 401, - "y": 118, + "x": 598, + "y": 147, "connections": {}, "disabledio": { "input": [], @@ -1776,8 +1782,8 @@ "component": "dido_controller", "tab": "1615551125555", "name": "DIDO_Controller", - "x": 402, - "y": 736, + "x": 397, + "y": 1131, "connections": { "0": [ { @@ -1821,8 +1827,8 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "modbus_to_dido", - "x": 79, - "y": 595, + "x": 96, + "y": 924, "connections": { "0": [ { @@ -1854,8 +1860,8 @@ "component": "debug", "tab": "1615551125555", "name": "modbusToDido", - "x": 411, - "y": 561, + "x": 388, + "y": 920, "connections": {}, "disabledio": { "input": [ @@ -1880,7 +1886,7 @@ "component": "modbus_reader", "tab": "1611921777196", "name": "Modbus reader", - "x": 102, + "x": 232, "y": 175, "connections": { "0": [ @@ -1931,8 +1937,8 @@ "component": "thermometer", "tab": "1611921777196", "name": "Thermometer", - "x": 107.75, - "y": 449, + "x": 234.75, + "y": 444, "connections": { "0": [ { @@ -1974,8 +1980,8 @@ "component": "debug", "tab": "1611921777196", "name": "MDBToTb", - "x": 402, - "y": 228, + "x": 599, + "y": 257, "connections": {}, "disabledio": { "input": [], @@ -1998,8 +2004,8 @@ "component": "code", "tab": "1611921777196", "name": "device-status", - "x": 588.0833282470703, - "y": 177, + "x": 755.0833282470703, + "y": 209, "connections": { "0": [ { @@ -2033,8 +2039,8 @@ "component": "debug", "tab": "1611921777196", "name": "modbus service", - "x": 802.0833282470703, - "y": 139, + "x": 966.0833282470703, + "y": 152, "connections": {}, "disabledio": { "input": [ @@ -2059,8 +2065,8 @@ "component": "virtualwireout", "tab": "1611921777196", "name": "send-to-services", - "x": 801.0833282470703, - "y": 236, + "x": 968.0833282470703, + "y": 268, "connections": {}, "disabledio": { "input": [], @@ -2081,8 +2087,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "tb-push", - "x": 84.75, - "y": 1300, + "x": 64.75, + "y": 1450, "connections": { "0": [ { @@ -2110,8 +2116,8 @@ "component": "slack_filter", "tab": "1612772287426", "name": "Slack Filter", - "x": 278, - "y": 1297, + "x": 283, + "y": 1491, "connections": { "0": [ { @@ -2137,7 +2143,7 @@ "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "RVO16 Senica - 10.0.0.131" + "name": "rvo_senica_1_10.0.0.141" }, "color": "#30E193", "notes": "" @@ -2147,8 +2153,8 @@ "component": "httprequest", "tab": "1612772287426", "name": "http://192.168.252.2:8004/slack", - "x": 471, - "y": 1354, + "x": 482, + "y": 1573, "connections": { "0": [ { @@ -2178,11 +2184,13 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 808, - "y": 1302, + "x": 819, + "y": 1484, "connections": {}, "disabledio": { - "input": [], + "input": [ + 0 + ], "output": [] }, "state": { @@ -2202,8 +2210,8 @@ "component": "trigger", "tab": "1612772287426", "name": "Trigger", - "x": 73, - "y": 1388, + "x": 66, + "y": 1543, "connections": { "0": [ { @@ -2226,7 +2234,553 @@ }, "color": "#F6BB42", "notes": "" + }, + { + "id": "1729855334955", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "platform-rpc-call", + "x": 1058.933334350586, + "y": 488.3500061035156, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "platform-rpc-call", + "color": "gray" + }, + "options": { + "wirename": "platform-rpc-call" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1729855371093", + "component": "debug", + "tab": "1612772287426", + "name": "rpc cloud", + "x": 1066.933334350586, + "y": 393.3500061035156, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "options": { + "type": "data", + "repository": false, + "enabled": true + }, + "color": "#967ADC", + "notes": "" + }, + { + "id": "1731068658334", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 90.75, + "y": 247, + "connections": { + "0": [ + { + "index": "0", + "id": "1612776786008" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731068754606", + "component": "cloudmqttconnect", + "tab": "1612772287426", + "name": "MQTT client - to senica-prod01", + "x": 739.75, + "y": 434, + "connections": { + "1": [ + { + "index": "0", + "id": "1729855371093" + }, + { + "index": "0", + "id": "1729855334955" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Connected", + "color": "green" + }, + "options": { + "username": "", + "clientid": "", + "port": "2764", + "host": "192.168.252.2", + "topic": "u118" + }, + "color": "#888600", + "notes": "" + }, + { + "id": "1731069001548", + "component": "db_init", + "tab": "1612772287426", + "name": "DB Initialization", + "x": 91.75, + "y": 55.25, + "connections": { + "0": [ + { + "index": "0", + "id": "1731069033416" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#888600", + "notes": "" + }, + { + "id": "1731069033416", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "db-init", + "x": 343.75, + "y": 50.25, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731069059135", + "component": "showdb", + "tab": "1612772287426", + "name": "Show db data", + "x": 1076.75, + "y": 745.25, + "connections": { + "0": [ + { + "index": "0", + "id": "1731069079243" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#888600", + "notes": "" + }, + { + "id": "1731069079243", + "component": "debug", + "tab": "1612772287426", + "name": "dbData", + "x": 1270.75, + "y": 784.25, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "options": { + "type": "data", + "repository": false, + "enabled": true + }, + "color": "#967ADC", + "notes": "" + }, + { + "id": "1731069116691", + "component": "trigger", + "tab": "1612772287426", + "name": "settings", + "x": 863.75, + "y": 622.75, + "connections": { + "0": [ + { + "index": "0", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, + { + "id": "1731069131637", + "component": "trigger", + "tab": "1612772287426", + "name": "relaysData", + "x": 800.75, + "y": 684.75, + "connections": { + "0": [ + { + "index": "1", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, + { + "id": "1731069137374", + "component": "trigger", + "tab": "1612772287426", + "name": "nodesData", + "x": 693.75, + "y": 749.75, + "connections": { + "0": [ + { + "index": "2", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, + { + "id": "1731069179846", + "component": "trigger", + "tab": "1612772287426", + "name": "pinsData", + "x": 755.75, + "y": 802.75, + "connections": { + "0": [ + { + "index": "3", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, + { + "id": "1731069192937", + "component": "trigger", + "tab": "1612772287426", + "name": "sample data", + "x": 812.75, + "y": 856.75, + "connections": { + "0": [ + { + "index": "4", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, + { + "id": "1731069264443", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 50.75, + "y": 1099, + "connections": { + "0": [ + { + "index": "0", + "id": "1634488120710" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731069334626", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "db-init", + "x": 184.88333129882812, + "y": 129, + "connections": { + "0": [ + { + "index": "0", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731069548145", + "component": "virtualwirein", + "tab": "1611921777196", + "name": "db-init", + "x": 46.75, + "y": 192, + "connections": { + "0": [ + { + "index": "0", + "id": "1699965957410" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731069567152", + "component": "virtualwirein", + "tab": "1611921777196", + "name": "db-init", + "x": 44.75, + "y": 465, + "connections": { + "0": [ + { + "index": "0", + "id": "1700411878636" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731070156936", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "db-init", + "x": 89.88333129882812, + "y": 1381, + "connections": { + "0": [ + { + "index": "2", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1731234189516", + "component": "trigger", + "tab": "1612772287426", + "name": "monitor.txt", + "x": 865.75, + "y": 911.75, + "connections": { + "0": [ + { + "index": "5", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, + { + "id": "1731234189551", + "component": "trigger", + "tab": "1612772287426", + "name": "err.txt", + "x": 921.75, + "y": 968.75, + "connections": { + "0": [ + { + "index": "6", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" } ], "version": 615 -} \ No newline at end of file +} diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 24f3269..11e7ae3 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -3,8 +3,8 @@ exports.title = 'DIDO_Controller'; exports.version = '2.0.0'; exports.group = 'Worksys'; exports.color = '#2134B0'; -exports.input = 2; -exports.output = ["red", "white", "yellow"]; +exports.input = 3; +exports.output = ["red", "white", "yellow", "green"]; exports.click = false; exports.icon = 'bolt'; exports.options = { edge: "undefined" }; @@ -56,25 +56,31 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme */ +const dbStatus = TABLE("status"); +const { errLogger, logger, monitor } = require('./helper/logger'); +const SerialPort = require('serialport'); +const WebSocket = require('ws'); +//const { exec } = require('child_process'); +const { runSyncExec } = require('./helper/serialport_helper'); +const { bytesToInt, resizeArray } = require('./helper/utils'); +const { sendNotification } = require('./helper/notification_reporter'); +const bitwise = require('bitwise'); -//globals -//FIRMWARE version -//TODO remove FLOW.OMS_edgeName variable, as we have FLOW.OMS_rvo_tbname -FLOW.OMS_edge_fw_version = "2024-09-23";//rok-mesiac-den -FLOW.OMS_edgeName = ""; -FLOW.OMS_maintenance_mode = false; +const DataToTbHandler = require('./helper/DataToTbHandler'); +let tbHandler; -//dynamic values -FLOW.OMS_masterNodeIsResponding = true; //cmd_manager -//FLOW.OMS_brokerready = false //wsmqttpublish -FLOW.OMS_no_voltage = new Set();//modbus_citysys - elektromer +const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler'); +const errorHandler = new ErrorToServiceHandler(); -//see loadSettings() in cmd_manager -FLOW.OMS_language = "en";//cmd_manager -FLOW.OMS_rvo_name = "";//cmd_manager -FLOW.OMS_rvo_tbname = "";//relaysData -FLOW.OMS_temperature_adress = "";//cmd_manager -//----------------------------------------------- +let ws = null; +let rsPort = null; + +let pinsData; +let relaysData; +let rvoTbName; +let GLOBALS; //FLOW global GLOBALS +let SETTINGS; // GLOBALS.settings +let controller_type; let alarmStatus = "OFF"; @@ -87,43 +93,6 @@ const SEND_TO = { const TIME_AFTER_TEMPERATURE_NOK_STATUS = 3600; //seconds const DIFFERENCE_TO_SEND_TEMPERATURE = 0.31; -var log4js = require("log4js"); -var path = require('path'); - -log4js.configure({ - appenders: { - errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } -}); - -const errLogger = log4js.getLogger("errLogs"); -const logger = log4js.getLogger(); -const monitor = log4js.getLogger("monitorLogs"); - -//console.log(path.join(__dirname, 'err.txt', "-----------------------------")); - -/* -process.on('uncaughtException', function (err) { - - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); - - //process.exit(1); -}) -*/ - -//USAGE -//logger.debug("text") -//monitor.info('info'); -//errLogger.error("some error"); exports.install = function(instance) { @@ -149,8 +118,6 @@ exports.install = function(instance) { const twilight_sensor_array = []; let twilightError = false; - let edgeName = ""; - monitor.info("DIDO_Relay_Controller installed"); //key is PIN number , line: 0 = RVO @@ -172,15 +139,6 @@ exports.install = function(instance) { }; */ - const dbPins = TABLE("pins"); - let pinsData = {};//key is pin - - const dbRelays = TABLE("relays"); - let relaysData = {};//key is line - - const dbStatus = TABLE("status"); - let statusData = null; - //status for calculating Statecodes let deviceStatus = { //key is device name: temperature,.... "state_of_main_switch": "Off", //Hlavný istič @@ -190,53 +148,32 @@ exports.install = function(instance) { "temperature": "OK", //templomer "battery": "OK", //Batéria "power_supply": "OK", //Zdroj - "master_node": "OK", //MN - FLOW.OMS_masterNodeIsResponding - "no_voltage": "OK", //FLOW.OMS_no_voltage - výpadok napätia na fáze + "master_node": "OK", //MN - GLOBALS.settings.masterNodeIsResponding + "no_voltage": "OK", //GLOBALS.settings.no_voltage - výpadok napätia na fáze "state_of_breaker": {}, //"Off",//Istič "state_of_contactor": {}, //"Off",//Stykač "twilight_sensor": "OK" //lux sensor }; - const SerialPort = require('serialport'); - const WebSocket = require('ws'); - let ws = null; - let rsPort = null; - - const { runSyncExec } = require('./helper/serialport_helper.js'); - const { bytesToInt, resizeArray } = require('./helper/utils'); - const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); - const { sendNotification } = require('./helper/notification_reporter.js'); - const bitwise = require('bitwise'); - - const DataToTbHandler = require('./helper/DataToTbHandler.js'); - const tbHandler = new DataToTbHandler(SEND_TO.tb); - tbHandler.setSender(exports.title); + function main() { - const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); - const errorHandler = new ErrorToServiceHandler(); + GLOBALS = FLOW.GLOBALS; + SETTINGS = FLOW.GLOBALS.settings; + rvoTbName = SETTINGS.rvoTbName; + pinsData = GLOBALS.pinsData; + relaysData = GLOBALS.relaysData; + statusData = GLOBALS.statusData; - //let useTurnOffCounter = false; - //let turnOffCounter = 0; - let controller_type = FLOW.OMS_controller_type //"lm" or "unipi" //logicMachine - if(controller_type == "") controller_type = "lm"; + tbHandler = new DataToTbHandler(SEND_TO.tb) + tbHandler.setSender(exports.title); - console.log(exports.title, "controller type: ", controller_type); - - - async function loadAllDb() - { - let responsePins = await promisifyBuilder(dbPins.find()); - pinsData = makeMapFromDbResult(responsePins, "pin"); - - let responseRelays = await promisifyBuilder(dbRelays.find()); - relaysData = makeMapFromDbResult(responseRelays, "line"); - - let responseStatus = await promisifyBuilder(dbStatus.find()); - statusData = responseStatus[0]; // {thermometer: 'OK', em: 'OK', twilight_sensor: 'OK'} + controller_type = SETTINGS.controller_type //"lm" or "unipi" //logicMachine + if(controller_type == "") controller_type = "lm"; + deviceStatus["temperature"] = statusData.thermometer; - FLOW.OMS_rvo_tbname = relaysData[0].tbname; + console.log(exports.title, "controller type: ", controller_type); if(controller_type === "lm") { @@ -249,12 +186,13 @@ exports.install = function(instance) { else { errLogger.debug("UNKNOWN controller_type:", controller_type); } - } + } function initialSetting() { - //force turn off relays & set tbname + //force turn off relays + let keys = Object.keys(pinsData); for(let i = 0; i < keys.length; i++) { @@ -266,15 +204,12 @@ exports.install = function(instance) { if(relaysData[line] != undefined) { pinsData[key].tbname = relaysData[line].tbname; - - relaysData[line].contactor = 0; + //relaysData[line].contactor = 0; } else { errLogger.error("CRITICAL!!! undefined relay", relaysData[line], line); - - //sendNotification("set port ", edgeName, ERRWEIGHT.CRITICAL, "local database is corrupted", "", SEND_TO.tb, instance, null ); - sendNotification("set port ", edgeName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance ); + sendNotification("set port ", rvoTbName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance ); } } @@ -285,36 +220,23 @@ exports.install = function(instance) { //this will modify database let forceTurnOff = true; - turnOffLine(line, pin, forceTurnOff, "turn off on startup"); + turnLine("off", line, pin, forceTurnOff, "turn off on startup"); } } //report RVO version relaysData[0].tbname; let values = {}; - values["edge_fw_version"] = FLOW.OMS_edge_fw_version; - values["maintenance_mode"] = FLOW.OMS_maintenance_mode; + values["edge_fw_version"] = SETTINGS.edge_fw_version; + values["maintenance_mode"] = SETTINGS.maintenance_mode; - edgeName = relaysData[0].tbname; - FLOW.OMS_edgeName = edgeName; - - dataToTb = { - [edgeName]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - instance.send(SEND_TO.tb, dataToTb); + sendTelemetry(values, rvoTbName); let time = 5*1000; setTimeout(function(){ instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"}); - sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", SEND_TO.tb, instance ); - monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version); - + sendNotification("rsPort.open()", rvoTbName, "flow_start", {}, "", SEND_TO.tb, instance); + monitor.info("-->FLOW bol spustený", rvoTbName, SETTINGS.edge_fw_version); }, time); } @@ -339,27 +261,23 @@ exports.install = function(instance) { //set port rsPort.write(Buffer.from(setRSPortData), function(err) { - monitor.info(exports.title + "--->Digital in_out has been set (runSyncExec was sucessfull)"); + if(!err) { + monitor.info(exports.title + "--->Digital in_out has been set (runSyncExec was sucessfull)"); - turnOffAlarm(); - - //useTurnOffCounter = true; - //turnOffCounter = relaysData.length - 1; - - initialSetting(); + turnAlarm("off"); + initialSetting(); + } }) - }).catch(function (reason) { - //instance.send(SEND_TO.debug, exports.title + " runSyncExec - promise rejected:" + reason); - errLogger.error( exports.title + " runSyncExec - promise rejected:" + reason); - - errorHandler.sendMessageToService( exports.title + " runSyncExec - promise rejected:" + reason); + }).catch(function(reason) { + errLogger.error(exports.title + " runSyncExec - promise rejected:" + reason); + errorHandler.sendMessageToService(exports.title + " runSyncExec - promise rejected:" + reason); }); }); - rsPort.on('data', function (data){ + rsPort.on('data', function(data) { rsPortReceivedData = [...rsPortReceivedData, ...data]; @@ -402,7 +320,6 @@ exports.install = function(instance) { }) rsPort.open(); - } @@ -417,7 +334,7 @@ exports.install = function(instance) { ws.onopen = function open() { instance.send(0, exports.title + " running"); - turnOffAlarm(); + turnAlarm("off"); // useTurnOffCounter = true; // turnOffCounter = relaysData.length - 1; @@ -480,7 +397,7 @@ exports.install = function(instance) { { previousValues["temperature"]["value"] = value; values['temperature'] = value; - sendTelemetry(values, FLOW.OMS_rvo_tbname); + sendTelemetry(values, rvoTbName); } return; } @@ -490,7 +407,7 @@ exports.install = function(instance) { let value = item['value']; let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01" - if (pin == undefined) return; + if(pin == undefined) return; switchLogic(pin, value); }) } @@ -516,92 +433,59 @@ exports.install = function(instance) { } } - // ! do we need requests every minute ??? - // const startRequests = () => { - // console.log("startRequest function called"); - // start = setInterval(() => { - // // console.log("data from evok requested"); - // ws.send(JSON.stringify({"cmd":"filter", "devices": "neuron"})); - // // ws.send(JSON.stringify({"cmd":"filter", "devices":["input", "relay"]})); - // }, 150000) - // } - instance.on("close", () => { if(rsPort) rsPort.close(); if(ws) ws.close(); - clearInterval(sendRebuildTasksAt11); }) - loadAllDb(); - function getPin(line) { - //conversionTable - let keys = Object.keys(pinsData); - for(let i = 0; i < keys.length; i++) + //conversionTable + let keys = Object.keys(pinsData); + for(let i = 0; i < keys.length; i++) + { + let key = keys[i]; + + if(pinsData[key].type == "state_of_contactor" && pinsData[key].line == line) { - let key = keys[i]; - - if(pinsData[key].type == "state_of_contactor" && pinsData[key].line == line) - { - if(rsPort) return key - 1; - if(ws) return key; - } + if(rsPort) return key - 1; + if(ws) return key; } + } - logger.debug("no pin detected"); + logger.debug("no pin detected"); - return null; + return null; } - function turnOnAlarm() + function turnAlarm(onOrOff) { - if(FLOW.OMS_maintenance_mode) return; + let value = 0; + if(onOrOff == "on") value = 1; + + if(value == 1 && SETTINGS.maintenance_mode) return; - alarmStatus = "ON"; - - if(rsPort) - { - let arr = [0x55]; - arr.push( 13 ); - arr.push( 0 ); - arr.push( 1 ); - - rsPort.write(Buffer.from(arr), function(err) { - logger.debug("sirena zapnuta"); - }); - } - else if(ws) - { - let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": 1}; - ws.send(JSON.stringify(cmd)); - logger.debug("sirena zapnuta"); - } - } - - - function turnOffAlarm() - { alarmStatus = "OFF"; - + if(value == 1) alarmStatus = "ON"; + if(rsPort) { let arr = [0x55]; - arr.push( 13 ); - arr.push( 0 ); - arr.push( 0 ); - + arr.push(13); + arr.push(0); + arr.push(value); + rsPort.write(Buffer.from(arr), function(err) { - logger.debug("sirena vypnuta"); + logger.debug(`sirena - ${onOrOff}`); }); } else if(ws) { - let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": 0}; + let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": value}; ws.send(JSON.stringify(cmd)); - logger.debug("sirena vypnuta"); + logger.debug(`sirena - ${onOrOff}`); } } @@ -609,9 +493,7 @@ exports.install = function(instance) { function reportLineStatus(line) { //Tá hodnota by mala fungovať tak že LSB bit číslo je stav ističa (1 - On, 0 - Off) a druhý bit je stav stýkača (1 - true, 0 - false). - let tbname = relaysData[line].tbname; - let bits = []; if(deviceStatus["state_of_breaker"][line] == "On") @@ -631,143 +513,47 @@ exports.install = function(instance) { let byte = bitwise.byte.write(bits.reverse()); //console.log("line", line, bits, byte); - - let values = { - statecode: byte - } - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //console.log(values); - - instance.send(SEND_TO.tb, dataToTb); + sendTelemetry({statecode: byte}, tbname); } - function turnOnLine(line, pin, force, info) + // turn line on or off + function turnLine(onOrOff, line, pin, force, info) { - - instance.send(SEND_TO.debug, "turn on line " + line ); + //onOrOff => "on" or "off" + let value = 0; + if(onOrOff == "on") value = 1; + if(force == undefined) force = false; if(line == 0) { - if(alarmStatus == "ON") turnOffAlarm(); - FLOW.OMS_maintenance_mode = true; + if(value == 1 && alarmStatus == "ON") turnAlarm("off"); + SETTINGS.maintenance_mode = value? true: false; let values = {}; values["statecode"] = calculateStateCode(); - values["power_mode"] = "maintenance"; - let tbname = relaysData[line].tbname; - sendTelemetry(values, tbname); - - monitor.info("turnOnLine (line, FLOW.OMS_maintenance_mode)", line, FLOW.OMS_maintenance_mode, info); - - return; - } - - if( pin === undefined) pin = getPin(line); - - monitor.info("turnOnLine (line, pin, force)", line, pin, force, info); - - if( pin === undefined) - { - monitor.info("pin is undefined!", line); - return; - } - + values["power_mode"] = value ? "maintenance": "Automatic"; + sendTelemetry(values, rvoTbName); - if(!force) - { - if(relaysData[line].contactor == 1) - { - instance.send(SEND_TO.debug, "line is already on " + line ); - logger.debug("turnOnLine: line is already on: ", line); - - return; - } - } - - // if(!rsPort.isOpen && !ws) - if(!rsPort && !ws) - { - errLogger.error("dido controller - port or websocket is not opened"); + monitor.info(`turnLine ${onOrOff} - (line, SETTINGS.maintenance_mode)`, line, SETTINGS.maintenance_mode, info); return; } - if(rsPort) - { - let arr = [0x55]; - arr.push( pin ); - arr.push( 0 ); - arr.push( 1 ); - - rsPort.write(Buffer.from(arr), function(err) { - if(err === undefined) - { - console.log("turnONLine zapisal do rsPortu", line, arr); - switchLogic(arr); - } - else - { - monitor.info("turnOnLine WRITE error", err); - } - - }); + if(pin === undefined) pin = getPin(line); - } - else if(ws) - { - console.log("turnONLine pin (relay)", pin); - //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method - let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1}; - ws.send(JSON.stringify(cmd)); - switchLogic(pin, 1) - } - } - - - function turnOffLine(line, pin, force, info) - { - if(force == undefined) force = false; - - if(line == 0) - { - FLOW.OMS_maintenance_mode = false; - - let values = {}; - values["statecode"] = calculateStateCode(); - values["power_mode"] = "automatic"; - let tbname = relaysData[line].tbname; - sendTelemetry(values, tbname); - - return; - } - - if( pin === undefined) pin = getPin(line); - - monitor.info("turnOffLine (line, pin, force)", line, pin, force, info); - - if( pin === undefined) + if(pin === undefined) { errLogger.error("pin is undefined!", line); return; } - + if(!force) { - if(relaysData[line].contactor == 0) + if(relaysData[line].contactor == value) { - instance.send(SEND_TO.debug, "line is already off " + line ); - logger.debug("turnOffLine: line already off:", line); - + instance.send(SEND_TO.debug, `line is already ${onOrOff} ` + line ); + logger.debug(`turnLine: line is already ${onOrOff} `, line); return; } } @@ -782,35 +568,40 @@ exports.install = function(instance) { if(rsPort) { let arr = [0x55]; - arr.push( pin ); - arr.push( 0 ); - arr.push( 0 ); + arr.push(pin); + arr.push(0); + arr.push(value); rsPort.write(Buffer.from(arr), function(err) { if(err === undefined) { - console.log("turnOffLine zapisal do rsPort-u", line, arr); + monitor.info(`turnLine ${onOrOff} zapisal do rsPort-u`, line, pin, arr, info); switchLogic(arr); } else { - monitor.info("turnOffLine WRITE error", err); - } - + monitor.info(`turnLine ${onOrOff} WRITE error`, err); + } }); } else if(ws) { //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method - //monitor.info("turnOffLine pin (relay)", pin); - let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0}; + monitor.info(`turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); + let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value}; ws.send(JSON.stringify(cmd)); - switchLogic(pin, 0) + switchLogic(pin, value) } } + // main opening + instance.on("2", _ => { + main(); + }) + + //data from modbus_reader or temperature sensor or twilight sensor or other modbus device instance.on("0", flowdata => { @@ -820,7 +611,7 @@ exports.install = function(instance) { instance.send(SEND_TO.debug, flowdata.data); // we handle nok status from modbus_reader component and thermometer - if(flowdata.data?.status) + if("status" in flowdata.data) { const status = flowdata.data.status; if(status == "NOK-twilight_sensor") @@ -838,7 +629,7 @@ exports.install = function(instance) { deviceStatus["temperature"] = "NOK"; } } - else if(flowdata.data?.values) + else if("values" in flowdata.data) { const values = flowdata.data.values; if(values.hasOwnProperty("twilight_sensor")) @@ -856,10 +647,10 @@ exports.install = function(instance) { else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current")) { deviceStatus["em"] = "OK"; - FLOW.OMS_no_voltage.size > 0 ? deviceStatus["no_voltage"] = "NOK": deviceStatus["no_voltage"] = "OK"; + SETTINGS.no_voltage.size > 0 ? deviceStatus["no_voltage"] = "NOK": deviceStatus["no_voltage"] = "OK"; } - sendTelemetry(values, FLOW.OMS_rvo_tbname); + sendTelemetry(values, rvoTbName); } sendRvoStatus(); @@ -878,10 +669,10 @@ exports.install = function(instance) { let force = obj.force; let info = obj.info; - if(obj.command == "turnOn") turnOnLine(line, undefined, force, info); - else if(obj.command == "turnOff") turnOffLine(line, undefined, force, info); - else if(obj.command == "turnOnAlarm") turnOnAlarm(); - else if(obj.command == "turnOffAlarm") turnOffAlarm(); + if(obj.command == "turnOn") turnLine("on", line, undefined, force, info); + else if(obj.command == "turnOff") turnLine("off", line, undefined, force, info); + else if(obj.command == "turnOnAlarm") turnAlarm("on"); + else if(obj.command == "turnOffAlarm") turnAlarm("off"); //! ake data prichadzaju z cmd_manager.js ??? //TODO transform to websocket @@ -899,11 +690,9 @@ exports.install = function(instance) { function calculateStateCode() { - let bytes = []; let bits = []; - //Hlavný istič - state_of_main_switch - //TODO state_of main_switch is door contact in senica rvo - values should be "open" and "closed" + //Hlavný istič - state_of_main_switch => v rvo senica je to druhy door pre silovu cast (EM) if(deviceStatus["state_of_main_switch"] == "closed") { bits.push(0); @@ -914,7 +703,7 @@ exports.install = function(instance) { } //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY - if(!FLOW.OMS_maintenance_mode) + if(!SETTINGS.maintenance_mode) { if(deviceStatus["rotary_switch_state"] == "Manual") { @@ -1040,6 +829,10 @@ exports.install = function(instance) { async function sendRvoStatus() { + // test if dbLoaded is ok to check + //if(!FLOW.dbLoaded) return; + if(SETTINGS === undefined) return; + const table = { "OK": 1, "NOK": 0 @@ -1055,7 +848,7 @@ exports.install = function(instance) { "master_node_status": table[deviceStatus["master_node"]] }; - for (const phase of FLOW.OMS_no_voltage) dataToTb[`phase_${phase}_status`] = 0; + for (const phase of SETTINGS.no_voltage) dataToTb[`phase_${phase}_status`] = 0; //thermometer did not send data for more than a hour. Time in seconds if(deviceStatus["temperature"] === "OK") @@ -1071,7 +864,7 @@ exports.install = function(instance) { dataToTb["status"] = checkRvoStatus(); dataToTb["statecode"] = calculateStateCode(); - sendTelemetry(dataToTb, FLOW.OMS_rvo_tbname); + sendTelemetry(dataToTb, rvoTbName); } @@ -1083,7 +876,8 @@ exports.install = function(instance) { let status = "OK"; for (const [key, value] of Object.entries(deviceStatus)) { - if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; + //if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; + if(["em", "twilight_sensor"].includes(key) && value == "NOK") status = "NOK"; } if(status == "OK") @@ -1093,7 +887,7 @@ exports.install = function(instance) { for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { - if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue; + if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && SETTINGS.maintenance_mode) continue; status = "NOK"; break; } @@ -1102,8 +896,8 @@ exports.install = function(instance) { // battery status. If value is 1 - battery is NOK if (previousValues[5] === 1) status = "NOK"; - if(!FLOW.OMS_masterNodeIsResponding) status = "NOK"; - if(FLOW.OMS_no_voltage.size > 0) status = "NOK"; + if(!SETTINGS.masterNodeIsResponding) status = "NOK"; + if(SETTINGS.no_voltage.size > 0) status = "NOK"; return status; } @@ -1135,7 +929,7 @@ exports.install = function(instance) { if(obj == undefined) { previousValues[pinIndex] = newPinValue; - logger.debug("dido-switchLogic ==> no pinIndex", pinIndex); + //logger.debug("dido-switchLogic ==> no pinIndex", pinIndex); return; } @@ -1155,14 +949,14 @@ exports.install = function(instance) { // { // if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) // { - // sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); + // sendNotification("switchLogic", rvoTbName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); // values["status"] = "NOK"; // deviceStatus["state_of_main_switch"] = "Off"; // } // else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) // { - // sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); + // sendNotification("switchLogic", rvoTbName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); // deviceStatus["state_of_main_switch"] = "On"; // } @@ -1211,73 +1005,71 @@ exports.install = function(instance) { //console.log("rotary_switch_state pin", pin2, pin3, value); } + //Zdroj - pin 4 else if (type === "power_supply") { if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", SEND_TO.tb, instance); - sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply"); + sendNotification("switchLogic", rvoTbName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply"); deviceStatus["power_supply"] = "NOK"; } else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", SEND_TO.tb, instance); - sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply"); + sendNotification("switchLogic", rvoTbName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply"); deviceStatus["power_supply"] = "OK"; } } + //Batéria - pin 5 else if (type === "battery") { if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", SEND_TO.tb, instance); - sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level"); + sendNotification("switchLogic", rvoTbName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level"); deviceStatus["battery"] = "NOK"; } else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", SEND_TO.tb, instance); - sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level"); + sendNotification("switchLogic", rvoTbName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level"); deviceStatus["battery"] = "OK"; } } + //Dverový kontakt - pin 6 //! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch" //! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition' else if(type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; - if (value === "open" && FLOW.OMS_maintenance_mode) + + if (value === "open" && SETTINGS.maintenance_mode) { - sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", SEND_TO.tb, instance, "rvo_door"); + sendNotification("switchLogic", rvoTbName, "door_opened", {}, "", SEND_TO.tb, instance, "rvo_door"); } - if (value === "open" && !FLOW.OMS_maintenance_mode) + if (value === "open" && !SETTINGS.maintenance_mode) { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.WARNING, "RVO open door out of maintenance mode", "", SEND_TO.tb, instance); - sendNotification("switchLogic", edgeName, "door_has_been_open_without_permision_alarm_is_on", {}, "", SEND_TO.tb, instance, "rvo_door"); + sendNotification("switchLogic", rvoTbName, "door_opened_without_permission", {}, "", SEND_TO.tb, instance, "rvo_door"); // zapneme sirenu // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition") - if(type === "door_condition") turnOnAlarm(); + if(type === "door_condition") turnAlarm("on"); } if (value === "closed") { - if(alarmStatus == "ON") turnOffAlarm(); - //turnOffAlarm(); - - sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", SEND_TO.tb, instance, "rvo_door"); + if(alarmStatus == "ON") turnAlarm("off"); + sendNotification("switchLogic", rvoTbName, "door_closed", {}, "", SEND_TO.tb, instance, "rvo_door"); } deviceStatus[type] = value; } + //lux sensor else if(type == "twilight_sensor") { @@ -1309,14 +1101,15 @@ exports.install = function(instance) { { twilightError = true; let value = twilight_sensor_array.shift(); - //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, SEND_TO.tb, instance ); + newPinValue = 0; } else if (set.size !== 1 && twilightError) { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance ); + //sendNotification("switchLogic", rvoTbName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance ); twilightError = false; twilight_sensor_array.shift(); + newPinValue = value; } else if (set.size === 1 && twilightError) @@ -1339,12 +1132,12 @@ exports.install = function(instance) { //else console.log("lux_sensor", value, diff); } } + else if(type == "state_of_contactor") { - //sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance ); if(!(deviceStatus["state_of_contactor"][line] == value)) { - sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", SEND_TO.tb, instance ); + sendNotification("switchLogic", rvoTbName, "state_of_contactor_for_line", {line: line, value: value}, "", SEND_TO.tb, instance ); } deviceStatus["state_of_contactor"][line] = value; @@ -1353,34 +1146,45 @@ exports.install = function(instance) { if(value === "On") value = true; else if(value === "Off") value = false; + //TODO do we need to modify relays table with contactor value, if we do not use it on startup ?? + let dataChanged = false; + if(relaysData[line].contactor !== newPinValue) { + dataChanged = true; + relaysData[line].contactor = newPinValue; + } + + instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, value: value, dataChanged: dataChanged}); + reportLineStatus(line); + //modify table relays - dbRelays.modify({ contactor: newPinValue }).where("line", line).make(function(builder) { - builder.callback(function(err, response) { - if(!err) - { - let time = 0; - if(value) time = 1000 * 10;//10 sekund + // dbRelays.modify({ contactor: newPinValue }).where("line", line).make(function(builder) { + // builder.callback(function(err, response) { + // if(!err) + // { + // let time = 0; + // if(value) time = 1000 * 10;//10 sekund - let dataChanged = false; - if(relaysData[line].contactor != value) dataChanged = true; - relaysData[line].contactor = value; + // let dataChanged = false; + // if(relaysData[line].contactor != newPinValue) dataChanged = true; + // relaysData[line].contactor = newPinValue; // 0,1 - //ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles - //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor - setTimeout(function(){ - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); - }, time); + // //ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles + // //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor + // setTimeout(function(){ + // instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); + // }, time); - reportLineStatus(line); - } - else - { - errLogger.error("modify table relays failed", err); - } + // reportLineStatus(line); + // } + // else + // { + // errLogger.error("modify table relays failed", err); + // } - }); - }); + // }); + // }); } + else if(type === "state_of_breaker") { @@ -1448,28 +1252,27 @@ exports.install = function(instance) { if(type == "rotary_switch_state") { - if(FLOW.OMS_maintenance_mode) value = "maintenance"; + if(SETTINGS.maintenance_mode) value = "maintenance"; value = value.toLowerCase(); values["power_mode"] = value; } if(newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue; - if(FLOW.OMS_rvo_tbname == tbname) sendRvoStatus(); + if(rvoTbName == tbname) sendRvoStatus(); if(Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname); } - function sendTelemetry(values, tbname) { + function sendTelemetry(values, tbname, date=Date.now()) { let dataToTb = { [tbname]: [ { - "ts": Date.now(), + "ts": date, "values": values } ] }; - // instance.send(SEND_TO.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); } @@ -1496,8 +1299,6 @@ exports.install = function(instance) { return (typeof item === "object" && !Array.isArray(item) && item !== null); } - - } //end of instance diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index f60c296..a89b75b 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -49,13 +49,8 @@ class DataToTbHandler { for(let i = 0; i < arrayOfValues.length; i++) { ts = arrayOfValues[i].ts; - - //console.log("sendToTb------------>before", arrayOfValues[i].values, tbname); - let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); - //console.log("sendToTb------------>after", values); - if(!this.isEmptyObject(values)) { arrayOfValuesToSend.push({ts: ts, values: values}); @@ -68,17 +63,6 @@ class DataToTbHandler { return; } - /* - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - */ - this.messageCounter++; let dataToTbModified = { diff --git a/flow/helper/ErrorToServiceHandler.js b/flow/helper/ErrorToServiceHandler.js index dc60446..97ee9d4 100644 --- a/flow/helper/ErrorToServiceHandler.js +++ b/flow/helper/ErrorToServiceHandler.js @@ -97,7 +97,7 @@ class ErrorToServiceHandler console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender); //TODO UGLY!!! - if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id; + if(this.projects_id === undefined) this.projects_id = FLOW.GLOBALS.settings.project_id; /* if(this.projects_id === undefined) diff --git a/flow/helper/error_reporter.js b/flow/helper/error_reporter.js deleted file mode 100644 index 2b84369..0000000 --- a/flow/helper/error_reporter.js +++ /dev/null @@ -1,72 +0,0 @@ - -//key is device, value = str -let sentValues= {}; - -function sendError(func, device, weight, str, extra, tb_output, instance, type) { - // if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){ - // return; // Allow debug messages only if CONFIG.debug is active - // } - - let key = device; - if(type != undefined) key = type; - - if(sentValues.hasOwnProperty(key)) - { - if(sentValues[key] == str) - { - return; - } - } - - sentValues[key] = str; - - let content = { - "type": weight, - "status": "new", - "source": { - "func":func, - "component":instance.id, - "component_name":instance.name, - "edge":device - }, - "message":str, - "message_data": extra - }; - - let msg = {}; - msg[device] = [ - { - "ts": Date.now(), - "values": { - "_event":content - } - } - ]; - - // Msg can be outputted from components only after configuration - /*if (canSendErrData()){ - sendBufferedErrors(); - } else { - bufferError(msg); - }*/ - instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections - -} - - -let ERRWEIGHT = { - EMERGENCY: "emergency", // System unusable - ALERT: "alert", // Action must be taken immidiately - CRITICAL: "critical", // Component unable to function - ERROR: "error", // Error, but component able to recover from it - WARNING: "warning", // Possibility of error, system running futher - NOTICE: "notice", // Significant message but not an error, things user might want to know about - INFO: "informational", // Info - DEBUG: "debug" // Debug - only if CONFIG.debug is enabled -}; - - -module.exports = { - sendError, - ERRWEIGHT -} \ No newline at end of file diff --git a/flow/helper/error_reporting.js b/flow/helper/error_reporting.js deleted file mode 100644 index 62dace5..0000000 --- a/flow/helper/error_reporting.js +++ /dev/null @@ -1,56 +0,0 @@ -const ERRWEIGHT = { - EMERGENCY: "emergency", // System unusable - ALERT: "alert", // Action must be taken immidiately - CRITICAL: "critical", // Component unable to function - ERROR: "error", // Error, but component able to recover from it - WARNING: "warning", // Possibility of error, system running futher - NOTICE: "notice", // Significant message but not an error, things user might want to know about - INFO: "informational", // Info - DEBUG: "debug" // Debug - only if CONFIG.debug is enabled -}; - - - - -function sendError(func, device, weight, str, extra){ - if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){ - return; // Allow debug messages only if CONFIG.debug is active - } - - let content = { - "type": weight, - "status": "new", - "source": { - "func":func, - "component":instance.id, - "component_name":instance.name, - "edge":myEdge - }, - "message":str, - "message_data": extra - }; - let msg = {}; - msg[device] = [ - { - "ts": Date.now(), - "values": { - "_event":content - } - } - ]; - - // Msg can be outputted from components only after configuration - /*if (canSendErrData()){ - sendBufferedErrors(); - } else { - bufferError(msg); - }*/ - instance.send(0, msg); // Even if error server is unavailable, send this message to output, for other possible component connections - -} - - -module.exports = { - sendError, - ERRWEIGHT, -} \ No newline at end of file diff --git a/flow/helper/logger.js b/flow/helper/logger.js new file mode 100644 index 0000000..2585639 --- /dev/null +++ b/flow/helper/logger.js @@ -0,0 +1,30 @@ +//https://github.com/log4js-node/log4js-node/blob/master/examples/example.js +//file: { type: 'file', filename: path.join(__dirname, 'log/file.log') } + +var log4js = require("log4js"); +var path = require('path'); + +log4js.configure({ +appenders: { + errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../../", 'err.txt') }, + monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../../", 'monitor.txt') }, + console: { type: 'console' } +}, +categories: { + errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, + monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, + //another: { appenders: ['console'], level: 'trace' }, + default: { appenders: ['console'], level: 'trace' } +} +}); + +const errLogger = log4js.getLogger("errLogs"); +const logger = log4js.getLogger(); +const monitor = log4js.getLogger("monitorLogs"); + +//USAGE +//logger.debug("text") +//monitor.info('info'); +//errLogger.error("some error"); + +module.exports = { errLogger, logger, monitor }; \ No newline at end of file diff --git a/flow/helper/notification_reporter.js b/flow/helper/notification_reporter.js index 84b2345..61c0aef 100644 --- a/flow/helper/notification_reporter.js +++ b/flow/helper/notification_reporter.js @@ -1,10 +1,6 @@ - -const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js'); -const dbNotifications = TABLE("notifications"); - //key is device, value = str let sentValues= {}; -let notificationsData = {}; +let notificationsData = null; let ERRWEIGHT = { EMERGENCY: "emergency", // System unusable @@ -24,12 +20,9 @@ function getKey(map, val) { //https://stackoverflow.com/questions/41117799/string-interpolation-on-variable var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]); -async function initNotifications() -{ - let response = await promisifyBuilder(dbNotifications.find()); - notificationsData = makeMapFromDbResult(response, "key"); - console.log("initNotifications done" ); +function initNotification() { + notificationsData = FLOW.GLOBALS.notificationsData; } function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) { @@ -39,7 +32,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance, let storeToSendValues = true; if(saveKey == undefined) storeToSendValues = false; - let lang = FLOW.OMS_language; + let lang = FLOW.GLOBALS.settings.language; if(lang != "en" || lang != "sk") lang = "en"; let tpl = key; @@ -55,7 +48,8 @@ function sendNotification(func, device, key, params, extra, tb_output, instance, } else { - console.error("sendNotification: Notifications: undefined key", key, func, notificationsData); + //console.error("sendNotification: Notifications: undefined key", key, func, notificationsData); + console.error("sendNotification: Notifications: undefined key", key, func ); return false; } @@ -91,7 +85,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance, if(storeToSendValues) sentValues[saveKey] = tpl; - let str = FLOW.OMS_rvo_name; + let str = FLOW.GLOBALS.settings.rvo_name; if(str != "") str = str + ": "; str = str + tpl; @@ -132,6 +126,6 @@ function sendNotification(func, device, key, params, extra, tb_output, instance, module.exports = { sendNotification, - initNotifications, - ERRWEIGHT -} \ No newline at end of file + ERRWEIGHT, + initNotification +} diff --git a/flow/helper/serialport_helper.js b/flow/helper/serialport_helper.js index 98aa235..4a0edd5 100644 --- a/flow/helper/serialport_helper.js +++ b/flow/helper/serialport_helper.js @@ -40,18 +40,17 @@ async function writeData(port, data, readbytes, timeout){ return new Promise((resolve, reject) => { // If first item in data array is 255, we just write broadcast command to rsPort - // We wait 3 seconds and resolve([ "b", "r", "o", "a", "d", "c", "a", "s", "t" ]) + // We wait 3 seconds and resolve(["broadcast"]) // It is important to resolve with array if(data[0] == 255) { port.write(Buffer.from(data), function(err) { if (err) { - port.removeListener('data', callback); reject(err.message); } }); - setTimeout(resolve, 3000, [ "b", "r", "o", "a", "d", "c", "a", "s", "t" ]); + setTimeout(resolve, 3000, ["broadcast"]); return; } diff --git a/flow/infosender.js b/flow/infosender.js index a83499c..c7afd83 100644 --- a/flow/infosender.js +++ b/flow/infosender.js @@ -3,12 +3,9 @@ exports.title = 'Info sender'; exports.version = '1.0.0'; exports.group = 'Worksys'; exports.color = '#2134B0'; -exports.input = 1; +exports.input = 2; exports.output = 1 -exports.click = false; -exports.author = 'oms-is'; exports.icon = 'bolt'; -exports.options = { edge: "undefined" }; const { networkInterfaces } = require('os'); @@ -22,13 +19,12 @@ exports.html = `
exports.readme = `# send all data to projects.worksys.io, required to monitor status of controller(unipi)`; -const fs = require('fs'); -var path = require('path'); +exports.install = function(instance) { -exports.install = async function(instance) { - + let id; let allValues = {}; let sendAllValuesInterval; + let configured = false; let now = new Date(); console.log(exports.title, "INSTALLED", now.toLocaleString("sk-SK")); @@ -47,23 +43,19 @@ exports.install = async function(instance) { } } } - function sendValues() { - const id = FLOW.OMS_projects_id; + if(!configured) return; if(Object.keys(allValues).length > 0) { - if(id !== undefined) + if(id) { delete allValues.__force__; let dataToSend = {...allValues}; dataToSend.id = id; dataToSend.ipAddresses = ipAddresses; - //dataToSend.notify_date = new Date().toISOString().slice(0, 19).replace('T', ' '); - - //console.log(exports.title, "------------>sendValues", dataToSend); instance.send(0, dataToSend); @@ -71,7 +63,7 @@ exports.install = async function(instance) { } else { - console.log(exports.title, "unable to send data, id is undefined"); + console.log(exports.title, "unable to send data, no id"); } } @@ -81,10 +73,14 @@ exports.install = async function(instance) { clearInterval(sendAllValuesInterval); }) - instance.on("data", (flowdata) => { + instance.on("0", _ => { + id = FLOW.GLOBALS.settings.project_id; + configured = true; + }) + + instance.on("1", flowdata => { allValues = { ...allValues, ...flowdata.data}; - //console.log("DATA RECEIVED", flowdata.data); //__force__ diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index 24a9580..b0908ae 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -3,6 +3,7 @@ exports.title = 'Modbus reader'; exports.version = '2.0.0'; exports.group = 'Worksys'; exports.color = '#2134B0'; +exports.input = 1; exports.output = ["red", "white", "yellow"]; exports.click = false; exports.author = 'Rastislav Kovac'; @@ -31,7 +32,10 @@ const SEND_TO = { const numberOfNotResponding = {}; let tbName = null; let mainSocket; - +//number of phases inRVO +let phases; +//phases where voltage is 0 (set) +let noVoltage; exports.install = function(instance) { @@ -55,10 +59,19 @@ exports.install = function(instance) { // lampSwitchNotification helper variables this.onNotificationSent = false; this.offNotificationSent = false; + this.phases = this.buildPhases(); this.startSocket(); } + buildPhases = () => { + let a = []; + for (let i = 1; i<= phases; i++) { + a.push(`Phase_${i}_voltage`) + } + return a; + } + startSocket = () => { let obj = this; @@ -228,9 +241,7 @@ exports.install = function(instance) { this.allValues = {}; break; } - } - } setNewStream = () => @@ -282,7 +293,7 @@ exports.install = function(instance) { if(!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return; Object.keys(values).map(singleValue => { - if (["Phase_1_voltage", "Phase_2_voltage", "Phase_3_voltage"].includes(singleValue)) + if (this.phases.includes(singleValue)) { let l = singleValue.split("_"); let phase = parseInt(l[1]); @@ -291,13 +302,13 @@ exports.install = function(instance) { if(values[singleValue] == 0) { - FLOW.OMS_no_voltage.add(phase); + noVoltage.add(phase); sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase ); // console.log('no voltage') } else { - FLOW.OMS_no_voltage.delete(phase); + noVoltage.delete(phase); // console.log('voltage detected') sendNotification("modbus_reader: checkNullVoltage", tbName, "voltage_on_phase_restored", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase); } @@ -330,18 +341,23 @@ exports.install = function(instance) { } const isObjectEmpty = (objectName) => { - return Object.keys(objectName).length === 0 && objectName.constructor === Object; + return Object.keys(objectName).length === 0 && objectName.constructor === Object; } - setTimeout(() => { - + function main() { + + phases = FLOW.GLOBALS.settings.phases; + tbName = FLOW.GLOBALS.settings.rvoTbName; + noVoltage = FLOW.GLOBALS.settings.no_voltage; mainSocket = new SocketWithClients(); - tbName = FLOW.OMS_rvo_tbname; // this notification is to show, that flow (unipi) has been restarted sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance); + } - }, 25000); + instance.on("0", function(_) { + main(); + }) } diff --git a/flow/show_dbdata.js b/flow/show_dbdata.js new file mode 100644 index 0000000..d01eecd --- /dev/null +++ b/flow/show_dbdata.js @@ -0,0 +1,241 @@ +exports.id = 'showdb'; +exports.title = 'Show db data'; +exports.group = 'Worksys'; +exports.color = '#888600'; +exports.version = '1.0.2'; +exports.icon = 'sign-out'; +exports.input = 7; +exports.output = 1; + +const { exec } = require('child_process'); + +exports.install = function(instance) { + + instance.on("0", _ => { + instance.send(0, FLOW.GLOBALS.settings); + }) + instance.on("1", _ => { + instance.send(0, FLOW.GLOBALS.relaysData); + }) + instance.on("2", _ => { + instance.send(0, FLOW.GLOBALS.nodesData); + }) + instance.on("3", _ => { + instance.send(0, FLOW.GLOBALS.pinsData); + }) + instance.on("4", _ => { + instance.send(0, {rpcSwitchOffLine, rpcSetNodeDimming, rpcLineProfile, rpcNodeProfile, sunCalcExample, dataFromTerminalBroadcast}) + }) + instance.on("5", _ => { + exec("sudo tail -n 25 monitor.txt" , (err, stdout, stderr) => { + if (err || stderr) instance.send(0,{err, stderr}); + else instance.send(0,stdout); + }) + }) + instance.on("6", _ => { + exec("sudo tail -n 25 err.txt" , (err, stdout, stderr) => { + if (err || stderr) instance.send(0,{err, stderr}); + else instance.send(0,stdout); + }) + }) +}; + + + +const rpcSwitchOffLine = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 8, + "method": "set_command", + "params": { + "entities": [ + { + "entity_type": "edb_line", + "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" + } + ], + "command": "switch", + "payload": { + "value": false + } + } + } + } +} + +const rpcSetNodeDimming = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 10, + "method": "set_command", + "params": { + "entities": [ + { + "entity_type": "street_luminaire", + "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" + } + ], + "command": "dimming", + "payload": { + "value": 5 + } + } + } + } +} + +const rpcLineProfile = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 9, + "method": "set_profile", + "params": { + "entities": [ + { + "entity_type": "edb_line", + "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" + } + ], + "payload": { + "intervals": [ + { + "value": 0, + "end_time": "20:00", + "start_time": "13:00" + }, + { + "value": 1, + "end_time": "05:30", + "start_time": "20:00" + }, + { + "value": 0, + "end_time": "13:00", + "start_time": "05:30" + } + ], + "astro_clock": true, + "dawn_lux_sensor": false, + "dusk_lux_sensor": false, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 0, + "dusk_astro_clock_offset": 0, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + } + } + } + } +} + + +const rpcNodeProfile = +{ + "topic": "v1/gateway/rpc", + "content": { + "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", + "data": { + "id": 11, + "method": "set_profile", + "params": { + "entities": [ + { + "entity_type": "street_luminaire", + "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" + } + ], + "payload": { + "intervals": [ + { + "cct": 3000, + "value": 0, + "end_time": "17:50", + "start_time": "13:00" + }, + { + "cct": 3000, + "value": 100, + "end_time": "21:30", + "start_time": "17:50" + }, + { + "cct": 3000, + "value": 0, + "end_time": "13:00", + "start_time": "07:10" + }, + { + "cct": 3000, + "value": 50, + "end_time": "00:00", + "start_time": "21:30" + }, + { + "cct": 3000, + "value": 10, + "end_time": "04:30", + "start_time": "00:00" + }, + { + "cct": 3000, + "value": 100, + "end_time": "07:10", + "start_time": "04:30" + } + ], + "astro_clock": true, + "dawn_lux_sensor": false, + "dusk_lux_sensor": false, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 30, + "dusk_astro_clock_offset": 20, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + } + } + } + } +} + + const sunCalcExample = { + dusk_no_offset: '20:18', + dawn_no_offset: '05:19', + dusk: '20:18', + dusk_hours: 20, + dusk_minutes: 18, + dawn: '05:19', + dawn_hours: 5, + dawn_minutes: 19, + dusk_time: 1715278688962, + dawn_time: 1715224744357, + dusk_astro_clock_offset: 0, + dawn_astro_clock_offset: 0 +} + + +const dataFromTerminalBroadcast = { + address: 4294967295, + byte1: 0, + byte2: 0, + byte3: 0, + byte4: 96, + name: "Time Schedule settings", + recipient: 2, + register: 8, + rw: 1 +} diff --git a/flow/slack_filter.js b/flow/slack_filter.js index a27c9e1..753c24a 100644 --- a/flow/slack_filter.js +++ b/flow/slack_filter.js @@ -11,9 +11,6 @@ exports.options = { 'name':'', 'types': '["emergency", "critical", "error", "ale exports.html = `
-
-
@(Name of this server)
-
@(Slack channel to receive the alerts)
@@ -170,11 +167,12 @@ exports.install = function(instance) { FLOW["savedSlackMessages"] = []; } + instance.options.name = FLOW.GLOBALS.settings.rvo_name; if (instance.options.name) { instance.status('Running'); running = true; } else { - instance.status('Please enter name', 'red'); + instance.status('Please run options again', 'red'); running = false; } } catch (e) { @@ -183,5 +181,7 @@ exports.install = function(instance) { }; instance.on('options', instance.reconfigure); - instance.reconfigure(); -}; \ No newline at end of file + setTimeout(instance.reconfigure, 10000); + + +}; diff --git a/flow/thermometer.js b/flow/thermometer.js index b5ee420..b3bb350 100644 --- a/flow/thermometer.js +++ b/flow/thermometer.js @@ -2,6 +2,7 @@ exports.id = 'thermometer'; exports.title = 'Thermometer'; exports.group = 'Worksys'; exports.color = '#5CB36D'; +exports.input = 1; exports.version = '1.0.3'; exports.output = ["red", "white", "blue"]; exports.author = 'Rastislav Kovac'; @@ -9,7 +10,9 @@ exports.icon = 'thermometer-three-quarters'; exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`; -const instanceSendTo = { +const { errLogger, logger, monitor } = require('./helper/logger'); + +const SEND_TO = { debug: 0, tb: 1, dido_controller: 2 @@ -18,56 +21,16 @@ const instanceSendTo = { //read temperature - frequency let timeoutMin = 5;//minutes -var path = require('path'); -var log4js = require("log4js"); - -log4js.configure({ - appenders: { - errLogs: { type: 'file', filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } -}); - -const errLogger = log4js.getLogger("errLogs"); -const logger = log4js.getLogger(); -const monitor = log4js.getLogger("monitorLogs"); - -//logger.debug("text") -//monitor.info('info'); -//errLogger.error("some error"); - -const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper'); -const dbSettings = TABLE("settings"); -let temperatureAddress = ""; - -async function loadSettings() -{ - //todo global FLOW.OMS_edgeName is making problem, so we load it here as well, it should not be - let responseSettings = await promisifyBuilder(dbSettings.find()); - temperatureAddress = responseSettings[0]["temperature_adress"]; -} - -loadSettings(); - exports.install = function(instance) { const { exec } = require('child_process'); - const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter'); + const { sendNotification } = require('./helper/notification_reporter'); let startRead; - let dataToTb; let counter = 0; - - let edgeName = ""; - + let rvoTbName = ""; + let temperatureAddress = ""; logger.debug(exports.title, "installed"); @@ -76,11 +39,11 @@ exports.install = function(instance) { }) - const start = function() { + const main = function() { try { - if(FLOW.OMS_controller_type === "unipi") + if(FLOW.GLOBALS.settings.controller_type === "unipi") { clearInterval(startRead); return; @@ -88,71 +51,23 @@ exports.install = function(instance) { if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined"; - logger.debug("FLOW.OMS_temperature_adress", FLOW.OMS_temperature_adress); - exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => { - edgeName = FLOW.OMS_edgeName; - - if(edgeName !== "") + if(!error) { - if(error) - { - - if(FLOW.OMS_brokerready == undefined) - { - logger.debug("gettemparature - FLOW.OMS_brokerready is undefined"); - - setTimeout(function(){ - start(); - }, 3000); - - return; - } - - if(FLOW.OMS_brokerready) - { - //sendNotification("start", edgeName, ERRWEIGHT.WARNING, "Thermometer is not responding", {"Error": error}, instanceSendTo.tb, instance, "thermometer"); - sendNotification("start", edgeName, "thermometer_is_not_responding", {}, {"Error": error}, instanceSendTo.tb, instance, "thermometer"); - } - - let status = "NOK"; - dataToTb = { - [edgeName]: [ - { - "ts": Date.now(), - "values": { - "status": status - } - } - ] - } - - monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready); - - // instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ?? - instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"}); - } - else parseData(stdout); - } - else - { - monitor.info("gettemperature: edgeName is not defined", FLOW.OMS_edgeName); - - setTimeout(function(){ - start(); - }, 3000); - + parseData(stdout) return; } - - //instance.send({"Temp":stdout,"stderr":stderr,"err":error}); + sendNotification("main", rvoTbName, "thermometer_is_not_responding", {}, {"Error": error}, SEND_TO.tb, instance, "thermometer"); + monitor.info("Thermometer is not responding", error); + instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"}); }); } catch(err) { errLogger.error(exports.title, err); + clearInterval(startRead); } } @@ -166,30 +81,17 @@ exports.install = function(instance) { if(counter > 290) { - instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); - - //sendNotification("parseData", edgeName, ERRWEIGHT.NOTICE, "Thermometer is working again", "", instanceSendTo.tb, instance, "thermometer"); - if(FLOW.OMS_brokerready) sendNotification("parseData", edgeName, "thermometer_is_responding_again", {}, "", instanceSendTo.tb, instance, "thermometer"); + instance.send(SEND_TO.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); + sendNotification("parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer"); } logger.debug("gettemperature", data); + const values = { "temperature": Number(data.toFixed(2)), - "status": "OK" } - dataToTb = { - [edgeName]: [ - { - "ts": Date.now(), - "values":values - } - ] - } - - instance.send(instanceSendTo.tb, dataToTb); - instance.send(instanceSendTo.dido_controller, values); - + instance.send(SEND_TO.dido_controller, {values: values}); counter = 0; } else { @@ -200,24 +102,21 @@ exports.install = function(instance) { //ked je problem 1 den let day = 24 * 60 / timeoutMin; if ( counter > day && counter < day + 2 ) { - //sendNotification("parseData", edgeName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", instanceSendTo.tb, instance, "thermometer"); - sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer"); + //sendNotification("parseData", rvoTbName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", SEND_TO.tb, instance, "thermometer"); + sendNotification("parseData", rvoTbName, "thermometer_sends_invalid_data", {}, "", SEND_TO.tb, instance, "thermometer"); - instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); - instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"}); + instance.send(SEND_TO.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); + instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"}); } } } - setTimeout(function(){ - start(); - }, 10000); - - startRead = setInterval(start, timeoutMin * 1000 * 60); - - //testing - //setInterval(() => {instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"})}, 180000); - + instance.on("data", _ => { + temperatureAddress = FLOW.GLOBALS.settings.temperature_address; + rvoTbName = FLOW.GLOBALS.settings.rvoTbName; + startRead = setInterval(main, timeoutMin * 1000 * 60); + main(); + }) }; \ No newline at end of file diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index 20f64fa..e11d9eb 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -4,30 +4,27 @@ exports.group = 'MQTT'; exports.color = '#888600'; exports.version = '1.0.2'; exports.icon = 'sign-out'; -exports.input = 1; -exports.output = ["red", "white", "blue"]; -exports.author = 'Daniel Segeš'; +exports.input = 2; +exports.output = 4; exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" }; -exports.npm = ['mqtt']; - exports.html = `
-
-
-
Hostname or IP address (if not empty - setting will override db setting)
-
-
-
Port
-
-
-
-
-
@(Client id)
-
-
-
@(Username)
-
-
+
+
+
Hostname or IP address (if not empty - setting will override db setting)
+
+
+
Port
+
+
+
+
+
@(Client id)
+
+
+
@(Username)
+
+
`; @@ -41,21 +38,22 @@ Added: - rpc response `; -const instanceSendTo = { +const { promisifyBuilder } = require('./helper/db_helper'); +const { errLogger, monitor } = require('./helper/logger'); +const fs = require('fs'); +const mqtt = require('mqtt'); + +const SEND_TO = { debug: 0, rpcCall: 1, services: 2 } -const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); - //CONFIG -let useLog4js = true; let createTelemetryBackup = true; let saveTelemetryOnError = true;//backup_on_failure overrides this value //------------------------ -var fs = require('fs'); let rollers; if(createTelemetryBackup) rollers = require('streamroller'); @@ -64,56 +62,18 @@ let insertNoSqlCounter = 0; let insertBackupNoSqlCounter = 0; let processingData = false; -let backup_on_failure = false;//== saveTelemetryOnError - create backup broker send failure +let backup_on_failure = false;//== saveTelemetryOnError - create backup client send failure let restore_from_backup = 0; //how many rows process at once? let restore_backup_wait = 0;//wait seconds let lastRestoreTime = 0; -let errLogger; -let logger; -let monitor; - -//TODO brokerready and sendBrokerError seems to be the same. Moreover, we use FLOW_OMS_brokerready variable!! -// -// if there is an error in broker connection, flow logs to monitor.txt. Not to log messages every second, we use sendBrokerError variable -let sendBrokerError = true; - -if(useLog4js) -{ - var path = require('path'); - var log4js = require("log4js"); - - log4js.configure({ - appenders: { - errLogs: { type: 'file', filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } - }); - - errLogger = log4js.getLogger("errLogs"); - logger = log4js.getLogger(); - monitor = log4js.getLogger("monitorLogs"); - - //USAGE - //logger.debug("text"); - //monitor.info('info'); - //errLogger.error("some error"); -} +// if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable +let sendClientError = true; process.on('uncaughtException', function (err) { - if(errLogger) - { - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); - } + errLogger.error('uncaughtException:', err.message) + errLogger.error(err.stack); //TODO //send to service @@ -127,13 +87,9 @@ const nosqlBackup = NOSQL('/backup/tbdata'); exports.install = function(instance) { - var broker; + var client; var opts; - var brokerready = false; - - instance.on('options', loadSettings); - - mqtt = require('mqtt'); + var clientReady = false; // wsmqtt status for notification purposes on projects.worksys.io database let wsmqttName = null; @@ -142,21 +98,28 @@ exports.install = function(instance) { function getWsmqttName(host) { - if(host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; - else if(host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; - else if(host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; + if(host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; + else if(host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; + else if(host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; } function sendWsStatus() { - instance.send(instanceSendTo.services, {[wsmqttName]: wsmqtt_status}); + instance.send(SEND_TO.services, {[wsmqttName]: wsmqtt_status}); } - sendWsStatusVar = setInterval(sendWsStatus, 180000); + function main() + { + if(!FLOW.dbLoaded) return; + + loadSettings(); + clearInterval(sendWsStatus); + sendWsStatusVar = setInterval(sendWsStatus, 180000); + } //set opts according to db settings - async function loadSettings() + function loadSettings() { if(instance.options.host !== "") @@ -179,21 +142,17 @@ exports.install = function(instance) { else { - const dbSettings = TABLE("settings"); - let responseSettings = await promisifyBuilder(dbSettings.find()); - - backup_on_failure = responseSettings[0]["backup_on_failure"]; + const SETTINGS = FLOW.GLOBALS.settings; + backup_on_failure = SETTINGS.backup_on_failure; saveTelemetryOnError = backup_on_failure; - restore_from_backup = responseSettings[0]["restore_from_backup"]; - restore_backup_wait = responseSettings[0]["restore_backup_wait"]; + restore_from_backup = SETTINGS.restore_from_backup; + restore_backup_wait = SETTINGS.restore_backup_wait; - let mqtt_host = responseSettings[0]["mqtt_host"]; - let mqtt_clientid = responseSettings[0]["mqtt_clientid"]; - let mqtt_username = responseSettings[0]["mqtt_username"]; - let mqtt_port = responseSettings[0]["mqtt_port"]; - - console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]); + let mqtt_host = SETTINGS.mqtt_host; + let mqtt_clientid = SETTINGS.mqtt_clientid; + let mqtt_username = SETTINGS.mqtt_username; + let mqtt_port = SETTINGS.mqtt_port; opts = { host: mqtt_host, @@ -216,27 +175,23 @@ exports.install = function(instance) { var url = "mqtt://" + opts.host + ":" + opts.port; console.log("MQTT URL: ", url); - broker = mqtt.connect(url, opts); + client = mqtt.connect(url, opts); - broker.on('connect', function() { + client.on('connect', function() { instance.status("Connected", "green"); - monitor.info("MQTT broker connected"); + monitor.info("MQTT client connected"); - sendBrokerError = true; - - brokerready = true; - FLOW.OMS_brokerready = brokerready; + sendClientError = true; + clientReady = true; wsmqtt_status = 'connected'; }); - broker.on('reconnect', function() { + client.on('reconnect', function() { instance.status("Reconnecting", "yellow"); - brokerready = false; - - FLOW.OMS_brokerready = brokerready; + clientReady = false; }); - broker.on('message', function(topic, message) { + client.on('message', function(topic, message) { // message is type of buffer message = message.toString(); if (message[0] === '{') { @@ -244,50 +199,53 @@ exports.install = function(instance) { message = JSON.parse(message); if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { - broker.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, {qos:1}); - instance.send(instanceSendTo.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}}); + client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, {qos:1}); + instance.send(SEND_TO.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}}); } }, () => instance.debug('MQTT: Error parsing data', message)); } - instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message }); + instance.send(SEND_TO.rpcCall, {"topic":topic, "content":message }); }); - broker.on('close', function(err) { - brokerready = false; - FLOW.OMS_brokerready = brokerready; + client.on('close', function(err) { + clientReady = false; wsmqtt_status = 'disconnected'; if (err && err.toString().indexOf('Error')) { instance.status("Err: "+err.code, "red"); - instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts }); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); } else { instance.status("Disconnected", "red"); - instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts }); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); } - broker.reconnect(); + client.reconnect(); }); - broker.on('error', function(err) { + client.on('error', function(err) { instance.status("Err: "+ err.code, "red"); - instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts }); - if(sendBrokerError) { - monitor.info('MQTT broker error', err); - sendBrokerError = false; + instance.send(SEND_TO.debug, {"message":"Client ERROR signal received !", "error":err, "opt":opts }); + if(sendClientError) { + monitor.info('MQTT client error', err); + sendClientError = false; } - brokerready = false; - FLOW.OMS_brokerready = brokerready; + clientReady = false; wsmqtt_status = 'disconnected'; - }); } - instance.on('data', function(data) { - if (brokerready) + instance.on("0", _ => { + main(); + }) + + + instance.on('1', function(data) { + + if(clientReady) { //do we have some data in backup file? //if any, process data from database @@ -296,13 +254,14 @@ exports.install = function(instance) { //read telemetry data and send back to server if(!processingData) processDataFromDatabase(); } - } - if (brokerready) + if(clientReady) { let stringifiedJson = JSON.stringify(data.data); - broker.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1}); + client.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1}); + + instance.send(3, stringifiedJson); //backup telemetry if(createTelemetryBackup) @@ -327,8 +286,8 @@ exports.install = function(instance) { else { - if(logger) logger.debug("Broker unavailable. Data not sent !", JSON.stringify(data.data)); - instance.send(instanceSendTo.debug, {"message":"Broker unavailable. Data not sent !", "data": data.data }); + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); if(saveTelemetryOnError) { @@ -344,9 +303,9 @@ exports.install = function(instance) { }); - instance.close = function(done) { - if (brokerready){ - broker.end(); + instance.close = function(done) { + if(clientReady){ + client.end(); clearInterval(sendWsStatusVar); } }; @@ -373,7 +332,7 @@ exports.install = function(instance) { let firstDigit = files[i].slice(0, pos); fileCounter = parseInt(firstDigit); - if (isNaN(fileCounter)) fileCounter = 0; + if(isNaN(fileCounter)) fileCounter = 0; //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); if(type == "max") @@ -443,10 +402,7 @@ exports.install = function(instance) { const processDataFromDatabase = async () => { - if(restore_from_backup <= 0) - { - return; - } + if(restore_from_backup <= 0) return; //calculate diff const now = new Date(); @@ -478,7 +434,7 @@ exports.install = function(instance) { for(let i = 0; i < records.length; i++) { - if (brokerready) { + if(clientReady) { let item = records[i]; let id = item.id; @@ -487,18 +443,19 @@ exports.install = function(instance) { { //console.log("------------processDataFromDatabase - remove", id, dataBase, i); - try{ + try { let o = JSON.parse(JSON.stringify(item)); delete o.id; let message = JSON.stringify(o); - broker.publish("v1/gateway/telemetry", message, {qos:1}); + client.publish("v1/gateway/telemetry", message, {qos:1}); + instance.send(3, message); //remove from database await promisifyBuilder(nosql.remove().where("id", id)); - } catch (error) { + } catch(error) { //process error console.log("processDataFromDatabase", error); } @@ -533,8 +490,6 @@ exports.install = function(instance) { } - loadSettings(); - - //instance.on('options', instance.reconfigure); - //instance.reconfigure(); + instance.on('options', main); + //instance.reconfigure(); }; From 5270a898a3dc3fd3bc18a2d7f97e67ced61f556a Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 2 Dec 2024 17:06:49 +0100 Subject: [PATCH 07/25] Compare nodesDb; thermometer instead evok; no status.table --- config | 3 +- createNode.py | 43 + databases/nodes_original/nodes_original.table | 1 + databases/notifications.table | 3 +- databases/relays.table | 6 +- databases/status.table | 2 - flow/cmd_manager.js | 2013 ++++----- flow/cmd_manager131.js | 3603 ----------------- flow/db_init.js | 3 - flow/designer.json | 162 +- flow/dido_controller.js | 95 +- flow/nodesdb_changecheck.js | 70 + flow/thermometer.js | 220 +- 13 files changed, 1255 insertions(+), 4969 deletions(-) create mode 100644 createNode.py create mode 100644 databases/nodes_original/nodes_original.table delete mode 100644 databases/status.table delete mode 100644 flow/cmd_manager131.js create mode 100644 flow/nodesdb_changecheck.js diff --git a/config b/config index 287769b..25ee651 100644 --- a/config +++ b/config @@ -7,7 +7,6 @@ package#flow (Object) : { url: '/' } table.relays : line:number|tbname:string|contactor:number|profile:string table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number -table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number +table.settings : rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number table.pins : pin:string|type:string|line:number table.notifications : key:string|weight:string|sk:string|en:string -table.status : thermometer:string|em:string|twilight_sensor:string diff --git a/createNode.py b/createNode.py new file mode 100644 index 0000000..21406cc --- /dev/null +++ b/createNode.py @@ -0,0 +1,43 @@ +print("zaciname") +import re, json + +search_str = '|' +final = [] +counter = 1 +with open("/home/unipi/flowserver/databases/nodes.table", 'r') as file: +# with open("/home/rasta5man/dev/oms/flowserver/databases/nodes.table", 'r') as file: + # Read each line in the file + for line in file: + # Print each line + line = line.strip() + print(line) + if counter != 1: + i = [m.start() for m in re.finditer(re.escape(search_str), line)] + node = line[ i[0] + 1 : i[1] ] + tbname = line[ i[1] + 1 : i[2] ] + final.append({node:tbname}) + counter += 1 +print(json.dumps(final)) +f = open("/home/unipi/flowserver/databases/nodes_original/nodes_original.table", "w") +f.write(json.dumps(final)) +f.close() + + +# +# # ``d`` has to be replaced with a different character +# old_character = "'" +# +# # ``t`` will replace ``d` +# new_character = '"' +# resultant_string = 0; +# with open("/home/unipi/flowserver/databases/nodes_original/nodes_original.table", 'r') as file: +# for line in file: +# resultant_string = re.sub("'", '"', line) +# +# resultant_string = re.sub(" ", "", resultant_string) +# print(resultant_string) +# +# f = open("/home/unipi/flowserver/databases/nodes_original/nodes_original.table", "w") +# f.write(str(resultant_string)) +# f.close() +# diff --git a/databases/nodes_original/nodes_original.table b/databases/nodes_original/nodes_original.table new file mode 100644 index 0000000..d8670f9 --- /dev/null +++ b/databases/nodes_original/nodes_original.table @@ -0,0 +1 @@ +[{"3815": "B5EoxeMVp4zwr8nqW0GjjoARjvD1PNamOGbLg63Z"}, {"3799": "roKgWqY95V3mXMRzyAjmmj7bLjexpJPvaGDBw826"}] diff --git a/databases/notifications.table b/databases/notifications.table index 77f9e7a..8e3788f 100644 --- a/databases/notifications.table +++ b/databases/notifications.table @@ -34,4 +34,5 @@ key:string|weight:string|sk:string|en:string +|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|............... +|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|............... +|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|............... -+|flow_restart|NOTICE|Restart flowu|Flow has been restarted|............... \ No newline at end of file ++|flow_restart|NOTICE|Restart flowu|Flow has been restarted|............... ++|nodes_db_changed|NOTICE|Zmena v node databaze|Node db has changed|............... diff --git a/databases/relays.table b/databases/relays.table index 68e4f06..40843b1 100644 --- a/databases/relays.table +++ b/databases/relays.table @@ -1,5 +1,5 @@ line:number|tbname:string|contactor:number|profile:string +|0|6lQGaY9RDywdVzObj0PadOkPg4NBn3exEK51LWZq|1||........... -+|1|JzwxZXOvDj1bVrN4nkWw9Qk8qdyBl3MRKLpGPgaQ|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|........... -+|2|g9OxBZ5KRwNznlY6pAp6mxkWXvjdEL4eGQobMDy2|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|........... -+|3|OzNMgZ9n43qPbjXmy7zWMJA2DKdYvW5e6pxGRrVa|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|........... ++|1|JzwxZXOvDj1bVrN4nkWw9Qk8qdyBl3MRKLpGPgaQ|9|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|........... ++|2|g9OxBZ5KRwNznlY6pAp6mxkWXvjdEL4eGQobMDy2|9|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|........... ++|3|OzNMgZ9n43qPbjXmy7zWMJA2DKdYvW5e6pxGRrVa|9|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|........... diff --git a/databases/status.table b/databases/status.table deleted file mode 100644 index 6d4f686..0000000 --- a/databases/status.table +++ /dev/null @@ -1,2 +0,0 @@ -thermometer:string|em:string|twilight_sensor:string -+|OK|OK|OK|............. diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index d84daf3..ed4a928 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -113,7 +113,7 @@ exports.install = function(instance) { priorities["79"] = minutes; priorities["84"] = minutes; - minutes = 10; + minutes = 10; priorities["87"] = minutes; priorities["6"] = minutes; priorities["7"] = minutes; @@ -123,7 +123,7 @@ exports.install = function(instance) { priorities["89"] = minutes; //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) - let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89]; + let listOfCommands = [0, 1, 3, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 84, 87, 89]; const errorHandler = new ErrorToServiceHandler(); @@ -146,8 +146,7 @@ exports.install = function(instance) { //-------------------------------- - function main() - { + function main() { GLOBALS = FLOW.GLOBALS; SETTINGS = FLOW.GLOBALS.settings; relaysData = GLOBALS.relaysData; @@ -166,7 +165,7 @@ exports.install = function(instance) { let now = new Date(); console.log("CMD Manager installed", now.toLocaleString("sk-SK")); - + sunCalcResult = calculateDuskDawn(); reportDuskDawn = { @@ -180,7 +179,7 @@ exports.install = function(instance) { //to ensure, edgeDateTime will be send to tb at full minute customTasksInterval = setInterval(function() { - if(new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); + if (new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); }, 1000); setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour @@ -188,22 +187,19 @@ exports.install = function(instance) { } - function cmdCounterResolve(address) - { - if(cmdCounter.hasOwnProperty(address)) - { + function cmdCounterResolve(address) { + if (cmdCounter.hasOwnProperty(address)) { cmdCounter[address] = cmdCounter[address] - 1; - + let result = cmdCounter[address]; - if(result == 0) delete cmdCounter[address]; + if (result == 0) delete cmdCounter[address]; return result; } return -1; } - function getParams(priority) - { + function getParams(priority) { let params = {}; //core rpc values @@ -221,12 +217,11 @@ exports.install = function(instance) { //params.tbname = tbname; params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed - if(priority != undefined ) - { + if (priority != undefined) { params.timestamp = priority; params.priority = priority; } - + params.addMinutesToTimestamp = 0;//repeat task if value is > 0, //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" @@ -238,10 +233,8 @@ exports.install = function(instance) { //nastav profil nodu - function processNodeProfile(node) - { - if(rotary_switch_state != "Automatic") - { + function processNodeProfile(node) { + if (rotary_switch_state != "Automatic") { logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); return; } @@ -249,21 +242,19 @@ exports.install = function(instance) { let nodeObj = nodesData[node]; let line = nodeObj.line; - if(relaysData[line].contactor == 0) - { + if (relaysData[line].contactor == 0) { logger.debug("line line is off", line, node); return; } - if(nodeObj.processed == 1) - { + if (nodeObj.processed == 1) { //logger.debug("node was already processed", node); return; } let nodeProfile = nodeObj.profile; logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); - if(nodeProfile) { + if (nodeProfile) { try { nodeProfile = JSON.parse(nodeProfile); @@ -277,10 +268,9 @@ exports.install = function(instance) { let timestamp = PRIORITY_TYPES.node_cmd; - removeTask({type: "set_node_profile", address: node}); + removeTask({ type: "set_node_profile", address: node }); - if(nodeProfile === "") - { + if (nodeProfile === "") { //vypneme profil nodu, posleme cmd //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia @@ -305,8 +295,7 @@ exports.install = function(instance) { tasks.push(params); } - else - { + else { let tasksProfile = []; //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu @@ -332,11 +321,10 @@ exports.install = function(instance) { //TS1 Time point a TS1 Time Point Levels let register = 9; - for(let i = 0; i < nodeProfile.intervals.length; i++) - { + for (let i = 0; i < nodeProfile.intervals.length; i++) { let obj = nodeProfile.intervals[i]; //let timePoint = obj.time_point; - let dim_value = obj.value; + let dim_value = obj.value; //Reg 9 až Reg 40 @@ -350,12 +338,12 @@ exports.install = function(instance) { Register úrovne má rovnaký formát ako dimming register (Reg 1). */ - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //params.byte1 = 0;//msb, podla dokumentacie data3 + //params.byte2 = 0;//podla dokumentacie data2 + //params.byte3 = 0;//podla dokumentacie data1 + //params.byte4 = 0;//lsb, podla dokumentacie data0 + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! let start_time = obj.start_time; let t = start_time.split(":"); @@ -428,8 +416,7 @@ exports.install = function(instance) { params.addMinutesToTimestamp = 0; params.info = "Threshold lux level for DUSK/DAWN"; - if(nodeProfile.dusk_lux_sensor) - { + if (nodeProfile.dusk_lux_sensor) { let v = nodeProfile.dusk_lux_sensor_value; let ba = longToByteArray(v); @@ -437,8 +424,7 @@ exports.install = function(instance) { params.byte2 = ba[0]; } - if(nodeProfile.dawn_lux_sensor) - { + if (nodeProfile.dawn_lux_sensor) { let v = nodeProfile.dawn_lux_sensor_value; let ba = longToByteArray(v); @@ -466,8 +452,7 @@ exports.install = function(instance) { params.addMinutesToTimestamp = 0; params.info = "DUSK/DAWN max. adjust period"; - if(nodeProfile.astro_clock) - { + if (nodeProfile.astro_clock) { let v = nodeProfile.dusk_lux_sensor_time_window; let ba = longToByteArray(v); @@ -475,8 +460,7 @@ exports.install = function(instance) { params.byte2 = ba[0]; } - if(nodeProfile.astro_clock) - { + if (nodeProfile.astro_clock) { let v = nodeProfile.dawn_lux_sensor_time_window; let ba = longToByteArray(v); @@ -495,7 +479,7 @@ exports.install = function(instance) { //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: //Bity 0 – 6: hodnota v minútach //Bit 7: znamienko (1 – mínus) - + logger.debug("processNodeProfile: Static offset", node); let params = getParams(PRIORITY_TYPES.node_cmd); @@ -508,26 +492,21 @@ exports.install = function(instance) { params.addMinutesToTimestamp = 0; params.info = "Static offset"; - if(nodeProfile.astro_clock) - { + if (nodeProfile.astro_clock) { let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); - if(dusk_astro_clock_offset < 0) - { + if (dusk_astro_clock_offset < 0) { params.byte3 = (dusk_astro_clock_offset * -1) + 128; } - else - { + else { params.byte3 = dusk_astro_clock_offset; } - if(dawn_astro_clock_offset < 0) - { + if (dawn_astro_clock_offset < 0) { params.byte4 = (dawn_astro_clock_offset * -1) + 128; } - else - { + else { params.byte4 = dawn_astro_clock_offset; } } @@ -555,8 +534,7 @@ exports.install = function(instance) { bits.push(0); bits.push(0); bits.push(0); - if(nodeProfile.astro_clock == true) - { + if (nodeProfile.astro_clock == true) { //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý bits.push(1); } @@ -575,14 +553,14 @@ exports.install = function(instance) { bits = []; //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if(nodeProfile.dusk_lux_sensor == true)//sumrak + if (nodeProfile.dusk_lux_sensor == true)//sumrak { bits.push(1); } else bits.push(0); //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if(nodeProfile.dawn_lux_sensor == true)//usvit + if (nodeProfile.dawn_lux_sensor == true)//usvit { bits.push(1); } @@ -608,7 +586,7 @@ exports.install = function(instance) { cmdCounter[node] = tasksProfile.length; //tasks.push(tasksProfile); - tasks = tasks.concat(tasksProfile); + tasks = tasks.concat(tasksProfile); } @@ -618,100 +596,88 @@ exports.install = function(instance) { } - function cleanUpRefFlowdataObj() - { + function cleanUpRefFlowdataObj() { let now = new Date(); let timestamp = now.getTime(); //clear old refFlowdata references let keys = Object.keys(refFlowdataObj); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let timestampKey = keys[i]; - if((timestamp - timestampKey) > 60*1000 ) - { + if ((timestamp - timestampKey) > 60 * 1000) { console.log("cleanUpRefFlowdataObj delete", timestampKey); - delete refFlowdataObj[ timestampKey ]; + delete refFlowdataObj[timestampKey]; } } } - function removeTask(obj) - { + function removeTask(obj) { let keys = Object.keys(obj); tasks = tasks.filter((task) => { let counter = 0; - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; - if(task.hasOwnProperty(key) && obj.hasOwnProperty(key)) - { - if(task[key] == obj[key]) counter++; + if (task.hasOwnProperty(key) && obj.hasOwnProperty(key)) { + if (task[key] == obj[key]) counter++; } } - - if(counter == keys.length) return false; + + if (counter == keys.length) return false; return true; }); } - process.on('uncaughtException', function (err) { + process.on('uncaughtException', function(err) { //TODO send to service - + errLogger.error('uncaughtException:', err.message) errLogger.error(err.stack); errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); //process.exit(1); }) - + //te();//force error - function processAllNodeProfilesOnLine(line) - { + function processAllNodeProfilesOnLine(line) { for (let k in nodesData) { - if(line == nodesData[k].line) - { + if (line == nodesData[k].line) { let node = nodesData[k].node; let processed = nodesData[k].processed; - if(!processed) processNodeProfile(node); + if (!processed) processNodeProfile(node); //else logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); } - } - } - - - function loadRelaysData(line) - { - for (const [key, value] of Object.entries(relaysData)) - { - if(key == "0") continue; - if(line != undefined) - { - //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData - if(line != value.line) continue; - } - - if(value.contactor == 1) processAllNodeProfilesOnLine(value.line); } } - function reportOnlineNodeStatus(line) - { + function loadRelaysData(line) { + for (const [key, value] of Object.entries(relaysData)) { + if (key == "0") continue; + if (line != undefined) { + //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData + if (line != value.line) continue; + } + + if (value.contactor == 1) processAllNodeProfilesOnLine(value.line); + } + } + + + function reportOnlineNodeStatus(line) { //broadcast cas, o 3 sek neskor - status, brightness //Po zapnutí línie broadcastovo aktualizovať predtým čas. logger.debug("--->reportOnlineNodeStatus for line", line); //return; - + //run broadcast //Actual time addMinutesToTimestamp = 0; @@ -729,41 +695,40 @@ exports.install = function(instance) { params.byte2 = minutes;//m params.byte3 = seconds;//s params.byte4 = 0; - params.recipient = 2;//2 broadcast, address = 0 + params.recipient = 2;//2 broadcast, address = 0 params.register = 87;//Actual time params.rw = 1;//write //other values params.type = "cmd"; - params.timestamp = Date.now() + 60000; + params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; tasks.push(params); let sec = 3; - setTimeout(function(){ + setTimeout(function() { //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel for (let k in nodesData) { - + //potrebujem nody k danej linii - if(line == nodesData[k].line || line == undefined) - { + if (line == nodesData[k].line || line == undefined) { let tbname = nodesData[k].tbname; let node = nodesData[k].node; let status = "NOK"; // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. - if(nodesData[k].status) { + if (nodesData[k].status) { status = "OK"; nodesData[k].time_of_last_communication = time; } - - sendTelemetry({status: status}, tbname, time); + + sendTelemetry({ status: status }, tbname, time); //prud, vykon - current, input power pre liniu pre vsetky nody - + //a pridame aj vyreportovanie dimmingu { let params = getParams(PRIORITY_TYPES.high_priority); @@ -816,12 +781,11 @@ exports.install = function(instance) { } } } - },sec*1000); + }, sec * 1000); } - function reportOfflineNodeStatus(line) - { + function reportOfflineNodeStatus(line) { logger.debug("--->reportOfflineNodeStatus for line", line); values = {}; @@ -829,45 +793,31 @@ exports.install = function(instance) { values["power"] = 0;//výkon values["current"] = 0;//prúd values["status"] = "OFFLINE"; - + const date = Date.now(); // it happens, that some data did not get to tb after sending // we setTimeout to make more time for db to process telemetry (eg 150 messages at once) Object.keys(nodesData).forEach((node, index) => { - + setTimeout(function() { - - //potrebujem nody k danej linii - if(line == nodesData[node].line || line == undefined) - { - let tbname = nodesData[node].tbname; - sendTelemetry(values, tbname, date) - } - },(index+1) * 300); + //potrebujem nody k danej linii + if (line == nodesData[node].line || line == undefined) { + let tbname = nodesData[node].tbname; + sendTelemetry(values, tbname, date) + } + + }, (index + 1) * 1000); }) - + } - function turnOnLine(line, info) - { + function turnLine(onOrOff, line, info) { let obj = { line: line, - command: "turnOn", - info: info - }; - - logger.debug("linia", line, obj); - instance.send(SEND_TO.dido_controller, obj); - } - - function turnOffLine(line, info) - { - let obj = { - line: line, - command: "turnOff", + command: onOrOff, info: info }; @@ -876,43 +826,40 @@ exports.install = function(instance) { } - function detectIfResponseIsValid(bytes) - { + + function detectIfResponseIsValid(bytes) { //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let type = "RESPONSE"; - if(bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] - else if(bytes[4] == 0) type = "RESPONSE"; - else if(bytes[4] == 1) type = "ERROR"; - else if(bytes[4] == 2) type = "EVENT"; + if (bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] + else if (bytes[4] == 0) type = "RESPONSE"; + else if (bytes[4] == 1) type = "ERROR"; + else if (bytes[4] == 2) type = "EVENT"; else type = "UNKNOWN"; - + let message = "OK"; let error = ""; - if(type == "BROADCAST") return {message, type, error}; + if (type == "BROADCAST") return { message, type, error }; let crc = crc16('ARC', bytes.slice(0, 9)); let c1 = (crc >> 8) & 0xFF; let c2 = crc & 0xFF; - - if(c1 != bytes[9]) - { + + if (c1 != bytes[9]) { //CRC_ERROR message = "NOK"; error = "CRC_ERROR c1"; instance.send(SEND_TO.debug, "CRC_ERROR c1"); } - - if(c2 != bytes[10]) - { + + if (c2 != bytes[10]) { //CRC_ERROR message = "NOK"; error = "CRC_ERROR c2"; instance.send(SEND_TO.debug, "CRC_ERROR c2"); } - + //crc error - if(type != "RESPONSE") - { + if (type != "RESPONSE") { instance.send(SEND_TO.debug, bytes); instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); @@ -922,14 +869,13 @@ exports.install = function(instance) { message = "NOK"; } - - return {message, type, error}; + + return { message, type, error }; } //BUILD TASKS// - function buildTasks(params) - { + function buildTasks(params) { //report SETTINGS.edge_fw_version as fw_version //report date as startdate @@ -944,14 +890,12 @@ exports.install = function(instance) { let processBroadcast = true; let processNodes = true; - if(params == undefined) - { + if (params == undefined) { init = true; tasks = []; logger.debug("-->buildTasks clear tasks"); } - else - { + else { processLineProfiles = false; processBroadcast = false; processNodes = false; @@ -960,23 +904,18 @@ exports.install = function(instance) { processLine = params.line; } - //load profiles pre linie - //relaysData[ record["line"] ] - + //load profiles pre vsetky linie: let now = new Date(); - if(processLineProfiles) - { + if (processLineProfiles) { //process line profiles let keys = Object.keys(relaysData); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let line = parseInt(keys[i]); //line is turned off by default let profilestr = relaysData[line].profile; - if(processLine != undefined) - { - if(processLine != line) continue; + if (processLine != undefined) { + if (processLine != line) continue; } try { @@ -984,14 +923,14 @@ exports.install = function(instance) { /** * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... */ - if(profilestr === "") throw ("Profile is not defined"); + if (profilestr === "") throw ("Profile is not defined"); let profile = JSON.parse(profilestr); - if(Object.keys(profile).length === 0) throw ("Profile is empty"); + if (Object.keys(profile).length === 0) throw ("Profile is empty"); monitor.info("buildTasks: profile for line", line); monitor.info("profile:", profile); - let time_points= profile.intervals; + let time_points = profile.intervals; // add name to regular profile timepoint and delete unused end_time key: time_points.forEach(point => { @@ -1001,17 +940,13 @@ exports.install = function(instance) { //monitor.info("buildTasks: time_points", time_points); - let currentValue = 0; - if(time_points.length > 0) currentValue = time_points[time_points.length - 1].value; - /** * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching */ - if(profile.astro_clock == true) - { + if (profile.astro_clock == true) { // if astro clock true, we remove all regular profile points time_points = []; @@ -1019,74 +954,60 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDawn(new Date(), line); // adding dusk dawn to timpoints - if(profile.dawn_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dawn"], "value": 0, "name":"dawn"}); - if(profile.dusk_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dusk"], "value": 1, "name":"dusk"}); + if (profile.dawn_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dawn"], "value": 0, "name": "dawn" }); + if (profile.dusk_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dusk"], "value": 1, "name": "dusk" }); //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window - if(profile.dawn_lux_sensor == true) - { - let [ahours, aminutes, aseconds] = sunCalcResult["dawn"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours)); - ad.setMinutes(parseInt(aminutes) + profile.dawn_lux_sensor_time_window); - ad.setSeconds(0); + if (profile.dawn_lux_sensor == true) { + let [ahours, aminutes] = sunCalcResult["dawn"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dawn_lux_sensor_time_window, 0); - let strDate = ad.getHours() + ":" + ad.getMinutes(); - - time_points.push({"value": 0, "start_time": strDate, "name": "luxOff"}); + let strDate = ad.getHours() + ":" + ad.getMinutes(); + time_points.push({ "value": 0, "start_time": strDate, "name": "luxOff" }); } - if(profile.dusk_lux_sensor == true) - { - let [ahours, aminutes, aseconds] = sunCalcResult["dusk"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours)); - ad.setMinutes(parseInt(aminutes) + profile.dusk_lux_sensor_time_window); - ad.setSeconds(0); + if (profile.dusk_lux_sensor == true) { + let [ahours, aminutes] = sunCalcResult["dusk"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dusk_lux_sensor_time_window, 0); - let strDate = ad.getHours() + ":" + ad.getMinutes(); - - time_points.push({"value": 1, "start_time": strDate, "name": "luxOn"}); - //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing + let strDate = ad.getHours() + ":" + ad.getMinutes(); + time_points.push({ "value": 1, "start_time": strDate, "name": "luxOn" }); + //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing } } //sort time_points - time_points.sort(function (a, b) { + time_points.sort(function(a, b) { + + let [ahours, aminutes] = a.start_time.split(':'); + let [bhours, bminutes] = b.start_time.split(':'); - let [ahours, aminutes, aseconds] = a.start_time.split(':'); - let [bhours, bminutes, bseconds] = b.start_time.split(':'); - let ad = new Date(); - ad.setHours( parseInt(ahours) ); - ad.setMinutes( parseInt(aminutes) ); - ad.setSeconds(0); - + ad.setHours(parseInt(ahours), parseInt(aminutes), 0); + let bd = new Date(); - bd.setHours( parseInt(bhours) ); - bd.setMinutes( parseInt(bminutes) ); - ad.setSeconds(0); - + bd.setHours(parseInt(bhours), parseInt(bminutes), 0); + return ad.getTime() - bd.getTime(); }); console.log("line timepoints ........", time_points); - monitor.info("-->comming events turn on/off lines:"); - for(let t = 0; t < time_points.length; t++) - { - - let start_time = new Date(); - let [hours, minutes, seconds] = time_points[t].start_time.split(':'); + let currentValue = 0; + if (time_points.length > 0) currentValue = time_points[time_points.length - 1].value; - start_time.setHours( parseInt(hours) ); - start_time.setMinutes( parseInt(minutes) ); - start_time.setSeconds(0); - - //task is the past - if(now.getTime() > start_time.getTime()) - { + monitor.info("-->comming events turn on/off lines:"); + for (let t = 0; t < time_points.length; t++) { + + let start_time = new Date(); + let [hours, minutes] = time_points[t].start_time.split(':'); + start_time.setHours(parseInt(hours), parseInt(minutes), 0); + + //task is in the past + if (now.getTime() > start_time.getTime()) { currentValue = time_points[t].value; //timepoint is in past, we add 24 hours @@ -1103,32 +1024,30 @@ exports.install = function(instance) { params.addMinutesToTimestamp = 0; // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day - if(time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24*60; + if (time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24 * 60; //astro timepoints will be recalculated dynamically: params.timePointName = time_points[t].name; // if astro timepoint, we save time window: - if(['luxOn', 'luxOff', 'dusk','dawn'].includes(params.timePointName)) - { + if (['luxOn', 'luxOff', 'dusk', 'dawn'].includes(params.timePointName)) { params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; } - if(params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; - else if(params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; + if (params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; + else if (params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; params.debug = true; //turn on/off line tasks.push(params); - monitor.info(params.info, start_time); - + monitor.info("TimePoint params: ", params.info, start_time); } monitor.info("-->time_points final", line, time_points); - //ensure to turn on/off according to calculated value + //ensure to turn on/off according to calculated currentValue let params = getParams(PRIORITY_TYPES.terminal); params.type = "relay"; params.line = parseInt(line); @@ -1143,14 +1062,13 @@ exports.install = function(instance) { monitor.info("-->currentValue for relay", line, currentValue); //turn on/off line - if(params.value == 0) params.info = "turn off line on startup: " + line; - else if(params.value == 1) params.info = "turn on line on startup: " + line; + if (params.value == 0) params.info = "turn off line on startup: " + line; + else if (params.value == 1) params.info = "turn on line on startup: " + line; tasks.push(params); } catch (error) { - if(profilestr !=="" ) - { + if (profilestr !== "") { //errLogger.error(profilestr, error); errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); } @@ -1166,29 +1084,28 @@ exports.install = function(instance) { //PROCESS DEFAULT BROADCASTS //Time of dusk, Time of dawn, Actual Time - if(processBroadcast) - { + if (processBroadcast) { let addMinutesToTimestamp = 5; { //run broadcast Time of dusk addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk - + let params = getParams(PRIORITY_TYPES.node_broadcast); - + let sunCalcResult = calculateDuskDawn(); let dusk_hours = sunCalcResult["dusk_hours"]; let dusk_minutes = sunCalcResult["dusk_minutes"]; - + params.address = 0xffffffff;//broadcast params.byte1 = dusk_hours;//h params.byte2 = dusk_minutes;//m params.byte3 = 0;//s params.byte4 = 0; - params.recipient = 2;//2 broadcast, + params.recipient = 2;//2 broadcast, params.register = 6;//Time of dusk - Reg 6 params.rw = 1;//write - + //other values params.type = "cmd"; params.timestamp = Date.now() + 60000; @@ -1197,18 +1114,18 @@ exports.install = function(instance) { tasks.push(params); } - + { - + //run broadcast Time of dawn addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn - + let params = getParams(PRIORITY_TYPES.node_broadcast); - + let sunCalcResult = calculateDuskDawn(); let dawn_hours = sunCalcResult["dawn_hours"]; let dawn_minutes = sunCalcResult["dawn_minutes"]; - + params.address = 0xffffffff;//broadcast params.byte1 = dawn_hours;//h params.byte2 = dawn_minutes;//m @@ -1217,27 +1134,27 @@ exports.install = function(instance) { params.recipient = 2; //2 broadcast params.register = 7;//Time of dawn - Reg 6 params.rw = 1;//write - + //other values params.type = "cmd"; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "Broadcast-dawnTime"; - + tasks.push(params); } - + { //run broadcast Actual time addMinutesToTimestamp = 5; - + let params = getParams(PRIORITY_TYPES.node_broadcast); - + var d = new Date(); let hours = d.getHours(); let minutes = d.getMinutes(); let seconds = d.getSeconds(); - + params.address = 0xffffffff;//broadcast params.byte1 = hours;//h params.byte2 = minutes;//m @@ -1246,13 +1163,13 @@ exports.install = function(instance) { params.recipient = 2; //2 broadcast params.register = 87;//Actual time params.rw = 1;//write - + //other values params.type = "cmd"; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; - + tasks.push(params); } @@ -1260,22 +1177,20 @@ exports.install = function(instance) { //process nodes & tasks //reportovanie pre platformu - if(processNodes) - { + if (processNodes) { for (let k in nodesData) { let address = parseInt(k); let tbname = nodesData[k].tbname; let register = 0; - + //logger.debug("generated cmd - buildTasks for node:", address); - + //listOfCommands - READ - for(let i = 0; i < listOfCommands.length; i++) - { + for (let i = 0; i < listOfCommands.length; i++) { register = listOfCommands[i]; - + let params = getParams(PRIORITY_TYPES.node_cmd); - + //core rpc values params.address = address; params.byte1 = 0; @@ -1285,31 +1200,30 @@ exports.install = function(instance) { params.recipient = 1; params.register = register; params.rw = 0; - + let addMinutesToTimestamp = priorities[register]; - + let timestampStart = PRIORITY_TYPES.node_cmd; //run imediatelly in function runTasks - if(addMinutesToTimestamp > 1) - { + if (addMinutesToTimestamp > 1) { timestampStart = timestampStart + addMinutesToTimestamp * 60000; } - + //other values params.type = "cmd"; params.tbname = tbname; params.timestamp = timestampStart; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "generated cmd - buildTasks (node)"; - + tasks.push(params); - + } } } //niektore ulohy sa vygeneruju iba 1x pri starte!!! - if(!init) return; + if (!init) return; //Priebežne (raz za cca 5 minút) je potrebné vyčítať z Master nodu verziu jeho FW. @@ -1328,7 +1242,7 @@ exports.install = function(instance) { //params.debug = true; //this will set SETTINGS.masterNodeIsResponding - + tasks.push(params); } @@ -1340,7 +1254,7 @@ exports.install = function(instance) { params.addMinutesToTimestamp = 60;//60 = every hour params.info = "detekcia nespracovaných profilov linie a nodov"; //params.debug = true; - + tasks.push(params); } @@ -1369,15 +1283,13 @@ exports.install = function(instance) { * dawn: usvit - lux je nad hranicou - vypnem * dusk: sumrak - lux je pod hranicou - zapnem */ - function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) - { + function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) { let now = new Date(); let currentTimestamp = now.getTime(); let keys = Object.keys(relaysData); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let line = keys[i]; //line is turned off by default let profilestr = relaysData[line].profile; @@ -1386,38 +1298,31 @@ exports.install = function(instance) { try { let profile = JSON.parse(profilestr); - if(Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); + if (Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); - if(profile.astro_clock == true) - { + if (profile.astro_clock == true) { let sunCalcResult = calculateDuskDawn(now, line); //usvit - if(profile.dawn_lux_sensor == true) - { - let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut - let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); + if (profile.dawn_lux_sensor == true) { + let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut + let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); - if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) - { - if(lux_sensor_value > profile.dawn_lux_sensor_value) - { - if(contactor) turnOffLine(line, "Profile: dawn - turnOff line according to lux sensor"); + if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { + if (lux_sensor_value > profile.dawn_lux_sensor_value) { + if (contactor) turnLine("off", line, "Profile: dawn - turnOff line according to lux sensor"); } } } //sumrak - if(profile.dusk_lux_sensor == true) - { - let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60); - let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60); + if (profile.dusk_lux_sensor == true) { + let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); + let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); - if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) - { - if(lux_sensor_value < profile.dusk_lux_sensor_value) - { - if(!contactor) turnOnLine(line, "Profile: dusk - turnOn line according to lux sensor"); + if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { + if (lux_sensor_value < profile.dusk_lux_sensor_value) { + if (!contactor) turnLine("on", line, "Profile: dusk - turnOn line according to lux sensor"); } } } @@ -1425,7 +1330,7 @@ exports.install = function(instance) { } } catch (error) { - if(profilestr !== "" ) monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); + if (profilestr !== "") monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); } } @@ -1439,39 +1344,35 @@ exports.install = function(instance) { * if newStatus of node is always receiving false, and it is already for more than SETTINGS.node_status_nok_time value, we update status to "NOK" in tb * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). */ - function updateNodeStatus(node, newStatus) - { + function updateNodeStatus(node, newStatus) { //MASTER - if(node == 0) return; + if (node == 0) return; let nodeObj = nodesData[node]; - if(nodeObj == undefined) return; + if (nodeObj == undefined) return; let nodeCurrentStatus = nodeObj.status; const now = Date.now(); let data = null; - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; - else if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) - { - data = {time_of_last_communication: now}; + if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; + else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { + data = { time_of_last_communication: now }; nodeDbStatusModify(node, data); return; } - else if(newStatus == false && nodeCurrentStatus == false) return true; - else if(newStatus == false && nodeCurrentStatus == true) - { - if(nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; + else if (newStatus == false && nodeCurrentStatus == false) return true; + else if (newStatus == false && nodeCurrentStatus == true) { + if (nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; else { - data = {status: newStatus}; + data = { status: newStatus }; nodeDbStatusModify(node, data); return true; } } - else if(newStatus == true && nodeCurrentStatus == false) - { - data = {status: newStatus, time_of_last_communication: now}; + else if (newStatus == true && nodeCurrentStatus == false) { + data = { status: newStatus, time_of_last_communication: now }; nodeDbStatusModify(node, data); return; } @@ -1479,33 +1380,30 @@ exports.install = function(instance) { } - function nodeDbStatusModify(node, data){ + function nodeDbStatusModify(node, data) { dbNodes.modify(data).where("node", node).make(function(builder) { builder.callback(function(err, response) { - if(!err) { - nodesData[node] = {...nodesData[node], ...data}; + if (!err) { + nodesData[node] = { ...nodesData[node], ...data }; } }); }); } - async function runTasks(){ + async function runTasks() { clearInterval(interval); let currentTimestamp = Date.now(); //report dusk, dawn--------------------------------- - if(reportDuskDawn.dusk_time < currentTimestamp) - { + if (reportDuskDawn.dusk_time < currentTimestamp) { //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if( (currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) - { + if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { //reportovali sme? - if(reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) - { - sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", SEND_TO.tb, instance); + if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { + sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; } } @@ -1517,15 +1415,12 @@ exports.install = function(instance) { reportDuskDawn.dusk_time = sunCalcResult.dusk_time; } - if(reportDuskDawn.dawn_time < currentTimestamp) - { + if (reportDuskDawn.dawn_time < currentTimestamp) { //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if( (currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) - { + if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { //reportovali sme? - if(reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) - { - sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", SEND_TO.tb, instance); + if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { + sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; } } @@ -1540,23 +1435,20 @@ exports.install = function(instance) { //-------------------------------------------------------- //sort tasks based on timestamp - tasks.sort(function (a, b) { - if(a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) - { + tasks.sort(function(a, b) { + if (a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) { return a.priority - b.priority; } return a.timestamp - b.timestamp; }); - if(tasks.length == 0 ) - { + if (tasks.length == 0) { instance.send(SEND_TO.debug, "no tasks created"); interval = setInterval(runTasks, LONG_INTERVAL); return; } - if(!rsPort.isOpen) - { + if (!rsPort.isOpen) { instance.send(SEND_TO.debug, "!rsPort.isOpen"); //await rsPort.open(); console.log("Cmd_manager - !rsPort.isOpen"); @@ -1564,18 +1456,15 @@ exports.install = function(instance) { let currentTask = tasks[0]; - if(currentTask.debug) - { + if (currentTask.debug) { //logger.debug("--->task to process", currentTask); } - if(currentTask.timestamp <= currentTimestamp) - { - let params = {...tasks[0]}; + if (currentTask.timestamp <= currentTimestamp) { + let params = { ...tasks[0] }; //allow terminal commands - if(SETTINGS.maintenance_mode && params.type !== "cmd-terminal") - { + if (SETTINGS.maintenance_mode && params.type !== "cmd-terminal") { interval = setInterval(runTasks, LONG_INTERVAL); return; } @@ -1587,28 +1476,24 @@ exports.install = function(instance) { let line = null; //rpc related - if(nodesData[nodeAddress] !== undefined) line = nodesData[nodeAddress].line; - if(params.line !== undefined) line = params.line; + if (nodesData[nodeAddress] !== undefined) line = nodesData[nodeAddress].line; + if (params.line !== undefined) line = params.line; let repeatTask = false; - if(params.addMinutesToTimestamp > 0 || params.timePointName) repeatTask = true; + if (params.addMinutesToTimestamp > 0 || params.timePointName) repeatTask = true; - if(repeatTask) - { - if(type === "cmd" || type === "cmd-master") - { + if (repeatTask) { + if (type === "cmd" || type === "cmd-master") { //set next start time automatically tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; } } - else - { + else { tasks.shift(); } //kontrola nespracovanych profilov nodov - if(type == "process_profiles") - { + if (type == "process_profiles") { tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; //vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov @@ -1619,8 +1504,7 @@ exports.install = function(instance) { } //relay - if(type == "relay") - { + if (type == "relay") { const timePointName = params.timePointName; const value = params.value; @@ -1629,43 +1513,31 @@ exports.install = function(instance) { date.setDate(date.getDate() + 1);//next day let sunCalcResult; - sunCalcResult = calculateDuskDawn(date, params.line); + if (timePointName) sunCalcResult = calculateDuskDawn(date, params.line); - if(timePointName == "dawn") - { + if (timePointName == "dawn") { tasks[0].timestamp = sunCalcResult.dawn_time; } - else if(timePointName == "dusk") - { + else if (timePointName == "dusk") { tasks[0].timestamp = sunCalcResult.dusk_time; } - else if(timePointName == "luxOn") - { + else if (timePointName == "luxOn") { tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; } - else if(timePointName == "luxOff") - { + else if (timePointName == "luxOff") { tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; } - else if(timePointName == "profileTimepoint") - { + else if (timePointName == "profileTimepoint") { tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; } let info = "aplikovany bod profilu"; let message = ""; - if(value == 1) - { - turnOnLine(params.line, info); - message = "on"; - } - else if(value == 0) - { - turnOffLine(params.line, info); - message = "off"; - } + value == 1 ? message = "on" : message = "off"; - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance ); + turnLine(message, params.line, info); + + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", { line: params.line, value: message }, "", SEND_TO.tb, instance); interval = setInterval(runTasks, SHORT_INTERVAL); return; } @@ -1675,25 +1547,22 @@ exports.install = function(instance) { //if(rotary_switch_state == "Off") disconnected = true; //state_of_breaker[line] - alebo istic linie - if(state_of_breaker.hasOwnProperty(line)) - { + if (state_of_breaker.hasOwnProperty(line)) { //if(state_of_breaker[line] == "Off") disconnected = true; } //toto sa reportuje po prijati dat z dido_controlera - if(disconnected) - { - let values = {"status": "OFFLINE"}; + if (disconnected) { + let values = { "status": "OFFLINE" }; logger.debug("disconnected", values); logger.debug("rotary_switch_state", rotary_switch_state); logger.debug("state_of_breaker", state_of_breaker[line]); //report only once! - if(!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; + if (!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; - if(!disconnectedReport[tbname]) - { + if (!disconnectedReport[tbname]) { sendTelemetry(values, tbname) } @@ -1707,54 +1576,48 @@ exports.install = function(instance) { const register = params.register; //high_priority - if(!SETTINGS.masterNodeIsResponding) - { + if (!SETTINGS.masterNodeIsResponding) { //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version errorHandler.sendMessageToService("Master node is not responding"); let stop = true; //fw version - register == 4 - if(type == "cmd-terminal" || register == 4) stop = false; - if(stop) - { + if (type == "cmd-terminal" || register == 4) stop = false; + if (stop) { interval = setInterval(runTasks, LONG_INTERVAL); return; } } let relayStatus = 1; - if(relaysData[line] != undefined) - { + if (relaysData[line] != undefined) { relayStatus = relaysData[line].contactor; } - if(line == 0) relayStatus = 0; - if(type == "cmd-terminal") relayStatus = 1; + if (line == 0) relayStatus = 0; + if (type == "cmd-terminal") relayStatus = 1; //check if rotary_switch_state == "Off" - if(relayStatus == 0) - { + if (relayStatus == 0) { //console.log("------------------------------------relayStatus", relayStatus, line); - let values = {"status": "OFFLINE"}; + let values = { "status": "OFFLINE" }; sendTelemetry(values, tbname) - + interval = setInterval(runTasks, SHORT_INTERVAL); return; } - if(!rsPort.isOpen) - { + if (!rsPort.isOpen) { interval = setInterval(runTasks, LONG_INTERVAL); return; } - + //RE-CALCULATE VALUES //set actual time for broadcast - if(register == 87 && params.recipient === 2) - { + if (register == 87 && params.recipient === 2) { var d = new Date(); let hours = d.getHours(); let minutes = d.getMinutes(); @@ -1768,15 +1631,13 @@ exports.install = function(instance) { //SET DUSK/DAWN FOR BROADCAST //Time of dusk - if(register == 6 && params.recipient === 2) - { + if (register == 6 && params.recipient === 2) { - if(type != "cmd-terminal") - { + if (type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); let dusk_hours = sunCalcResult["dusk_hours"]; let dusk_minutes = sunCalcResult["dusk_minutes"]; - + params.byte1 = dusk_hours;//h params.byte2 = dusk_minutes;//m params.byte3 = 0;//s @@ -1788,14 +1649,12 @@ exports.install = function(instance) { } //Time of dawn - if(register == 7 && params.recipient === 2) - { - if(type != "cmd-terminal") - { + if (register == 7 && params.recipient === 2) { + if (type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); let dawn_hours = sunCalcResult["dawn_hours"]; let dawn_minutes = sunCalcResult["dawn_minutes"]; - + params.byte1 = dawn_hours;//h params.byte2 = dawn_minutes;//m params.byte3 = 0;//s @@ -1804,7 +1663,7 @@ exports.install = function(instance) { //TODO astrohodiny let dawn = "Time of dawn: " + sunCalcResult["dawn"]; } - + } //----------------------- @@ -1814,7 +1673,7 @@ exports.install = function(instance) { startTime = new Date(); let saveToTb = true; - if(!tbname) saveToTb = false; + if (!tbname) saveToTb = false; let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); @@ -1822,22 +1681,21 @@ exports.install = function(instance) { let timeout = 4000; // await keyword is important, otherwise incorrect data is returned! - await writeData(rsPort, resp, readBytes, timeout).then(function (data) { + await writeData(rsPort, resp, readBytes, timeout).then(function(data) { endTime = new Date(); var timeDiff = endTime - startTime; //data je array z 11 bytov: 1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data a 2 byty CRC - let dataBytes = data.slice(5,9); + let dataBytes = data.slice(5, 9); let result = detectIfResponseIsValid(data); - + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let message = result.message; // OK, NOK let message_type = result.type; let error = result.error; - if(params.debug != "generated cmd") - { + if (params.debug != "generated cmd") { //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug, params); } @@ -1857,23 +1715,20 @@ exports.install = function(instance) { let values = {}; //CMD FINISHED - if(message == "OK") - { + if (message == "OK") { updateNodeStatus(nodeAddress, true); //write - if(type == "set_node_profile") - { + if (type == "set_node_profile") { let result = cmdCounterResolve(nodeAddress); - if(result == 0) - { + if (result == 0) { dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { builder.callback(function(err, response) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); - - logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: nodeAddress }, "", SEND_TO.tb, instance); + + logger.debug("--> profil úspešne odoslaný na node č. " + nodeAddress); nodesData[nodeAddress].processed = true; }); }); @@ -1881,59 +1736,48 @@ exports.install = function(instance) { } //parse read response - if(params.rw == 0) - { + if (params.rw == 0) { values = processResponse(register, dataBytes); //read } - if(itIsNodeCommand) - { + if (itIsNodeCommand) { values.comm_status = "OK"; values.status = "OK"; } //master node - if(nodeAddress == 0) - { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); + if (nodeAddress == 0) { + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); SETTINGS.masterNodeIsResponding = true; - if(register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; + if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; } //odoslanie príkazu z terminálu - dáta - if(type == "cmd-terminal") - { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); + if (type == "cmd-terminal") { + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance); } - if(params.debug) - { + if (params.debug) { //logger.debug("saveToTb", saveToTb, tbname, values); } - if(saveToTb) - { + if (saveToTb) { sendTelemetry(values, tbname) } - else - { - if(type == "cmd-terminal") - { + else { + if (type == "cmd-terminal") { terminalCommandResponse(params, "SUCCESS", data); } } } - else - { + else { terminalCommandResponse(params, "ERROR", data) handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); - - if(params.hasOwnProperty("debug")) - { - if(params.debug) - { + + if (params.hasOwnProperty("debug")) { + if (params.debug) { //logger.debug("writeData err: ", error, result, params); logger.debug("writeData err: ", tbname, nodeAddress, register, values); } @@ -1949,19 +1793,16 @@ exports.install = function(instance) { terminalCommandResponse(params, "FAILURE", null, reason); handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); - if(params.hasOwnProperty("debug")) - { - if(params.debug) - { + if (params.hasOwnProperty("debug")) { + if (params.debug) { logger.debug("-->WRITE FAILED: " + reason, params.debug, params); } } }); - + } - else - { + else { // if(currentTask.debug) // { // //currentTask.timestamp <= currentTimestamp @@ -1970,20 +1811,20 @@ exports.install = function(instance) { interval = setInterval(runTasks, LONG_INTERVAL); return; - } + } //console.log("----->runTasks - setInterval", new Date()); interval = setInterval(runTasks, SHORT_INTERVAL); - } + } - function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb){ + function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb) { let node = params.address; let register = params.register; let type = params.type; let tbName = params.tbname; - if(!tbName) return; + if (!tbName) return; let values = {}; @@ -1991,55 +1832,48 @@ exports.install = function(instance) { let updateStatus = updateNodeStatus(node, false); //master node - if(node == 0) - { + if (node == 0) { sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); logger.debug("master_node_is_not_responding", params); SETTINGS.masterNodeIsResponding = false; - if(register == 4) values["master_node_version"] = "NOK"; + if (register == 4) values["master_node_version"] = "NOK"; } - if(type == "set_node_profile") - { + if (type == "set_node_profile") { delete cmdCounter[node]; - logger.debug( "profil nebol úspešne odoslaný na node č. ", params); - sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", {node: node}, "", SEND_TO.tb, instance ); + logger.debug("profil nebol úspešne odoslaný na node č. ", params); + sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); } - if(itIsNodeCommand) - { + if (itIsNodeCommand) { values.comm_status = "NOK"; } - if(updateStatus) - { + if (updateStatus) { values.status = "NOK"; } // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); - if(saveToTb && Object.keys(values).length > 0) - { + if (saveToTb && Object.keys(values).length > 0) { sendTelemetry(values, tbName) } } - - + + /** * function handles requests from terminal * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. * FAILURE means, that we got into catch block of writeData function. */ - function terminalCommandResponse(params, responseType, data=null, reason="") { //success, error, failure - - if(params.refFlowdataKey == undefined) - { + function terminalCommandResponse(params, responseType, data = null, reason = "") { //success, error, failure + + if (params.refFlowdataKey == undefined) { //console.log("params.refFlowdataKey is undefined", params); return; } - else - { + else { console.log("params.refFlowdataKey: ", params); } @@ -2070,12 +1904,11 @@ exports.install = function(instance) { let responseObj = {} responseObj["type"] = type; - if(responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; + if (responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; else responseObj["bytes"] = data; let refFlowdata = refFlowdataObj[params.refFlowdataKey]; //holds reference to httprequest flowdata - if(refFlowdata) - { + if (refFlowdata) { refFlowdata.data = responseObj; instance.send(SEND_TO.http_response, refFlowdata); } @@ -2085,7 +1918,7 @@ exports.install = function(instance) { /** * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function */ - function reportEdgeDateTimeAndNumberOfLuminaires(){ + function reportEdgeDateTimeAndNumberOfLuminaires() { //Number of ok and nok nodes on platform does not equals to total number of nodes. //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: @@ -2098,16 +1931,15 @@ exports.install = function(instance) { let number_of_ok_luminaires = 0; let number_of_nok_luminaires = 0; - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; let nodeObj = nodesData_clone[key]; - if(nodeObj.tbname == undefined) continue; + if (nodeObj.tbname == undefined) continue; - if(nodeObj.status) number_of_ok_luminaires++; + if (nodeObj.status) number_of_ok_luminaires++; else number_of_nok_luminaires++; } - + const values = { "number_of_luminaires": number_of_luminaires, "number_of_ok_luminaires": number_of_ok_luminaires, @@ -2125,7 +1957,7 @@ exports.install = function(instance) { // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI - if(SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; + if (SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; rsPort = new SerialPort(`/dev/${SETTINGS.serial_port}`, { autoOpen: false }); //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit //rsPort.setMaxListeners(0); @@ -2136,33 +1968,33 @@ exports.install = function(instance) { //loadRelaysData(); - await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) { + await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); //APP START - let dataToInfoSender = {id: SETTINGS.project_id, name: SETTINGS.rvo_name}; + let dataToInfoSender = { id: SETTINGS.project_id, name: SETTINGS.rvo_name }; dataToInfoSender.fw_version = SETTINGS.edge_fw_version; dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); dataToInfoSender.__force__ = true; - + instance.send(SEND_TO.infoSender, dataToInfoSender); logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); - }).catch(function (reason) { + }).catch(function(reason) { instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); }); }); rsPort.on('error', function(err) { - - //TODO report to service!!! - //errLogger.error(exports.title, "unable to open port", SETTINGS.serial_port, err.message); - errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); - instance.send(SEND_TO.debug, err.message); + //TODO report to service!!! + //errLogger.error(exports.title, "unable to open port", SETTINGS.serial_port, err.message); + errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); + + instance.send(SEND_TO.debug, err.message); }); rsPort.on("close", () => { @@ -2171,7 +2003,7 @@ exports.install = function(instance) { rsPort.open(); } - + instance.on("close", () => { clearInterval(interval); @@ -2189,69 +2021,55 @@ exports.install = function(instance) { //instance.send(SEND_TO.debug, "on Data"); //instance.send(SEND_TO.debug, flowdata); - + //logger.debug(flowdata.data); //just testing functions - if(flowdata.data == "open") - { - if(!rsPort.isOpen) rsPort.open(); + if (flowdata.data == "open") { + if (!rsPort.isOpen) rsPort.open(); return; } - else if(flowdata.data == "close") - { + else if (flowdata.data == "close") { rsPort.close(); return; } - else if(flowdata.data == "clean") - { + else if (flowdata.data == "clean") { tasks = []; return; } - else if(flowdata.data == "buildtasks") - { + else if (flowdata.data == "buildtasks") { //build & run return; } - else if(flowdata.data == "run") - { + else if (flowdata.data == "run") { //durations = []; - if(tasks.length == 0) - { + if (tasks.length == 0) { buildTasks(); - if(rsPort.isOpen) - { - interval = setInterval(runTasks, 100); + if (rsPort.isOpen) { + interval = setInterval(runTasks, 100); } - else - { - instance.send(SEND_TO.debug, "port is not opened!!!"); + else { + instance.send(SEND_TO.debug, "port is not opened!!!"); } } } - else - { + else { //terminal data - object //logger.debug("flowdata", flowdata.data); - if(typeof flowdata.data === 'object') - { + if (typeof flowdata.data === 'object') { //logger.debug("dido", flowdata.data); - if(flowdata.data.hasOwnProperty("sender")) - { + if (flowdata.data.hasOwnProperty("sender")) { //data from dido_controller - if(flowdata.data.sender == "dido_controller") - { + if (flowdata.data.sender == "dido_controller") { - if(flowdata.data.hasOwnProperty("cmd")) - { + if (flowdata.data.hasOwnProperty("cmd")) { let cmd = flowdata.data.cmd; - - if(cmd == "buildTasks") - { + + if (cmd == "buildTasks") { clearInterval(interval); logger.debug("-->CMD MANAGER - BUILD TASKS"); @@ -2263,32 +2081,25 @@ exports.install = function(instance) { logger.debug("-->CMD MANAGER - RUN TASKS"); interval = setInterval(runTasks, LONG_INTERVAL); } - else if(cmd == "reload_relays") - { + else if (cmd == "reload_relays") { loadRelaysData(flowdata.data.line); - if(flowdata.data.dataChanged) - { - if(!flowdata.data.value) - { + if (flowdata.data.dataChanged) { + if (!flowdata.data.value) { reportOfflineNodeStatus(flowdata.data.line); } - else - { + else { reportOnlineNodeStatus(flowdata.data.line); } } - + } - else if(cmd == "rotary_switch_state") - { + else if (cmd == "rotary_switch_state") { let value = flowdata.data.value; //state was changed - if(rotary_switch_state != value) - { - if(value == "Off") - { + if (rotary_switch_state != value) { + if (value == "Off") { //vyreportovat vsetky svietdla reportOfflineNodeStatus(); } @@ -2296,13 +2107,11 @@ exports.install = function(instance) { rotary_switch_state = value; } } - else if(cmd == "lux_sensor") - { + else if (cmd == "lux_sensor") { lux_sensor = parseInt(flowdata.data.value); // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? - if(lux_sensor < 100) - { + if (lux_sensor < 100) { // we send lux_sensor value to all nodes: let params = getParams(PRIORITY_TYPES.node_broadcast); @@ -2325,39 +2134,37 @@ exports.install = function(instance) { turnOnOffLinesAccordingToLuxSensor(lux_sensor); } } - else if(cmd == "state_of_breaker") - { + else if (cmd == "state_of_breaker") { //istic linie let value = flowdata.data.value; let line = parseInt(flowdata.data.line); let dataChanged = false; - if(state_of_breaker[line] != value) dataChanged = true; + if (state_of_breaker[line] != value) dataChanged = true; state_of_breaker[line] = value; let status = "OK"; - if(value == "Off") status = "NOK"; + if (value == "Off") status = "NOK"; - if(dataChanged) { + if (dataChanged) { - if(relaysData.hasOwnProperty(line)) - { + if (relaysData.hasOwnProperty(line)) { let tbname = relaysData[line].tbname; - if(value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); - else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); + if (value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); //report status liniu - sendTelemetry({status: status}, tbname) + sendTelemetry({ status: status }, tbname) //current value - if(value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii + if (value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii } } } - else{ + else { logger.debug("undefined cmd", cmd); } } @@ -2367,19 +2174,18 @@ exports.install = function(instance) { } //data from worksys - if(flowdata.data.hasOwnProperty("topic")) - { + if (flowdata.data.hasOwnProperty("topic")) { let data = getNested(flowdata.data, "content", "data"); - if(data == undefined) { - console.log("Invalid rpc command came from platform"); - return; + if (data == undefined) { + console.log("Invalid rpc command came from platform"); + return; } let command = data.params.command; let method = data.method; let profile = data.params.payload; - if(profile == undefined) profile = ""; + if (profile == undefined) profile = ""; let entity = data.params.entities[0]; let entity_type = entity.entity_type; let tbname = entity.tb_name; @@ -2388,33 +2194,28 @@ exports.install = function(instance) { logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); logger.debug("----------------------------"); - if(entity_type == "street_luminaire"|| entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") - { - if(method == "set_command") - { + if (entity_type == "street_luminaire" || entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") { + if (method == "set_command") { //let command = data.params.command; let value = data.params.payload.value; - - if(command == "dimming") - { + + if (command == "dimming") { let nodeWasFound = false; let keys = Object.keys(nodesData); //logger.debug("-----", keys); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let node = keys[i]; //logger.debug( node, nodesData[node], tbname); - if(tbname == nodesData[node].tbname) - { + if (tbname == nodesData[node].tbname) { let params = getParams(PRIORITY_TYPES.high_priority); value = parseInt(value); - if(value > 0) value = value + 128; + if (value > 0) value = value + 128; //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime params.type = "cmd"; @@ -2435,7 +2236,7 @@ exports.install = function(instance) { tasks.push(params); - setTimeout(function(){ + setTimeout(function() { //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority //a pridame aj vyreportovanie dimmingu @@ -2506,8 +2307,8 @@ exports.install = function(instance) { tasks.push(params); } - },4000); - + }, 4000); + nodeWasFound = true; @@ -2515,55 +2316,49 @@ exports.install = function(instance) { } } - if(!nodeWasFound) - { + if (!nodeWasFound) { logger.debug("set dimming from platform", "unable to find tbname", tbname); } } - else - { + else { instance.send(SEND_TO.debug, "undefined command " + command); logger.debug("undefined command", command); } return; } - else if(method == "set_profile") - { + else if (method == "set_profile") { //nastav profil nodu logger.debug("-->set_profile for node", data.params); logger.debug("------profile data", profile); //instance.send(SEND_TO.debug, "set_profile" + command); let keys = Object.keys(nodesData); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let node = keys[i]; - if(tbname == nodesData[node].tbname) - { + if (tbname == nodesData[node].tbname) { - if(profile != "") profile = JSON.stringify(profile); + if (profile != "") profile = JSON.stringify(profile); dbNodes.modify({ processed: false, profile: profile }).where("node", node).make(function(builder) { builder.callback(function(err, response) { - logger.debug("worksys - update node profile done", profile); - if(profile === "") logger.debug("worksys - update node profile done - profile is empty"); + logger.debug("worksys - update node profile done", profile); + if (profile === "") logger.debug("worksys - update node profile done - profile is empty"); - //profil úspešne prijatý pre node č. xx - sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, SEND_TO.tb, instance ); + //profil úspešne prijatý pre node č. xx + sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); - nodesData[node].processed = false; - nodesData[node].profile = profile; + nodesData[node].processed = false; + nodesData[node].profile = profile; - processNodeProfile(node); + processNodeProfile(node); }); }); } } } - else - { + else { instance.send(SEND_TO.debug, "unknown method " + method); logger.debug("unknown method", method); @@ -2573,28 +2368,24 @@ exports.install = function(instance) { } //nastav profil linie z platformy - else if(entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") - { + else if (entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") { //profil linie //relays.table line:number|tbname:string|contactor:number|profile:string //najdeme line relaysData - if(method == "set_profile") - { + if (method == "set_profile") { logger.debug("-->set_profile for line", data.params); logger.debug("profile data:", profile); let keys = Object.keys(relaysData); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let line = keys[i]; - if(tbname == relaysData[line].tbname) - { + if (tbname == relaysData[line].tbname) { //zmazeme tasky - removeTask({type: "relay", line: line}); - - if(profile != "") profile = JSON.stringify(profile); + removeTask({ type: "relay", line: line }); + + if (profile != "") profile = JSON.stringify(profile); dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) { builder.callback(function(err, response) { @@ -2606,15 +2397,15 @@ exports.install = function(instance) { relaysData[line].profile = profile; loadRelaysData(line) - + //TODO build tasks by mala bezat az ked je vsetko loadRelaysData //spracovane, pravdepodobne treba spravit promisy logger.debug("loadRelaysData DONE for line", line); - console.log("zacina buildTasks po loadRelaysData.........") + console.log("zacina buildTasks po loadRelaysData.........") - buildTasks({processLineProfiles: true, line: line}); - - sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", {line: line}, profile, SEND_TO.tb, instance ); + buildTasks({ processLineProfiles: true, line: line }); + + sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); }); }); @@ -2622,35 +2413,31 @@ exports.install = function(instance) { } } } - else if(method == "set_command") - { + else if (method == "set_command") { let value = data.params.payload.value; - if(command === "switch") - { + if (command === "switch") { // if we receive rpc from platform, to switch maintenance mode, we set SETTINGS.maintenance_mode flow variable to value; - if(entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; + if (entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; const relayObject = getObjectByTbValue(relaysData, tbname); let line = 0; - if(isObject(relayObject)) line = relayObject.line; - + if (isObject(relayObject)) line = relayObject.line; + // v relaysData je contactor bud 0 alebo 1, ale z platformy prichadza true, false; - if(value == false) turnOffLine(line, "command received from platform"); - else turnOnLine(line, "command received from platform"); + if (value == false) turnLine("off", line, "command received from platform"); + else turnLine("on", line, "command received from platform"); } } - else - { + else { instance.send(SEND_TO.debug, "undefined method " + method); logger.debug("undefined method", method); } return; } - else - { + else { instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); logger.debug("UNKNOW entity_type", entity_type); } @@ -2658,11 +2445,10 @@ exports.install = function(instance) { } //terminal - if(!rsPort.isOpen) await rsPort.open(); + if (!rsPort.isOpen) await rsPort.open(); let params = flowdata.data.body; - if(params == undefined) - { + if (params == undefined) { //logger.debug("CMD manager flowdata.data.body is undefined"); return; } @@ -2683,7 +2469,7 @@ exports.install = function(instance) { cleanUpRefFlowdataObj(); - refFlowdataObj[ timestamp ] = flowdata; + refFlowdataObj[timestamp] = flowdata; //fix //params.address = params.adress; @@ -2701,546 +2487,497 @@ exports.install = function(instance) { }) -//function gets value of a nested property in an object and returns undefined if it does not exists: -function getNested(obj, ...args) { - return args.reduce((obj, level) => obj && obj[level], obj) -} + //function gets value of a nested property in an object and returns undefined if it does not exists: + function getNested(obj, ...args) { + return args.reduce((obj, level) => obj && obj[level], obj) + } -/** - * setCorrectTime function runs once per hour - * If it is 3 o'clock, it sets actual time, which is got from services - * https://service-prod01.worksys.io/gettime - * If also detects Read Only Filesystem once a day - */ -function setCorrectPlcTimeOnceADay() -{ + /** + * setCorrectTime function runs once per hour + * If it is 3 o'clock, it sets actual time, which is got from services + * https://service-prod01.worksys.io/gettime + * If also detects Read Only Filesystem once a day + */ + function setCorrectPlcTimeOnceADay() { - const currentTime = new Date(); - if(currentTime.getHours() != 3) return; + const currentTime = new Date(); + if (currentTime.getHours() != 3) return; - RESTBuilder.make(function(builder) { + RESTBuilder.make(function(builder) { - if(!builder) return; + if (!builder) return; - builder.method('GET'); - builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); + builder.method('GET'); + builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); - builder.callback(function(err, response, output) { + builder.callback(function(err, response, output) { - if (err) { - console.log(err); - return; - } - - const res = output.response; - - try { - - const obj = JSON.parse(res); - let d = new Date(obj.date); - - const now = new Date(); - - let diffInMinutes = now.getTimezoneOffset(); - console.log("---->TimezoneOffset", diffInMinutes); - - if(d instanceof Date) - { - - // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); - - let year = d.getFullYear(); - let month = addZeroBefore(d.getMonth() + 1); - let day = addZeroBefore(d.getDate()); - - let hours = addZeroBefore(d.getHours()); - let minutes = addZeroBefore(d.getMinutes() ); - let seconds = addZeroBefore(d.getSeconds()); - - let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); - console.log(dateStr); - - monitor.info("failed timedatectl set-time", err, stderr); - } - else - { - monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); - } - - }); + if (err) { + console.log(err); + return; } - } catch (error) { - logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); - monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); - } + const res = output.response; - // we detect readOnlyFileSystem once an hour as well - detectReadOnlyFilesystem(); + try { + const obj = JSON.parse(res); + let d = new Date(obj.date); + + const now = new Date(); + + let diffInMinutes = now.getTimezoneOffset(); + console.log("---->TimezoneOffset", diffInMinutes); + + if (d instanceof Date) { + + // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); + + let year = d.getFullYear(); + let month = addZeroBefore(d.getMonth() + 1); + let day = addZeroBefore(d.getDate()); + + let hours = addZeroBefore(d.getHours()); + let minutes = addZeroBefore(d.getMinutes()); + let seconds = addZeroBefore(d.getSeconds()); + + let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); + console.log(dateStr); + + monitor.info("failed timedatectl set-time", err, stderr); + } + else { + monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); + } + + }); + } + + } catch (error) { + logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); + monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); + } + + // we detect readOnlyFileSystem once an hour as well + detectReadOnlyFilesystem(); + + }); }); - }); -} + } -function detectReadOnlyFilesystem() -{ - exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); + function detectReadOnlyFilesystem() { + exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); - } else { - //console.log("Read-only", stdout); - - let lines = stdout + ""; - lines = lines.split("\n"); + } else { + //console.log("Read-only", stdout); - let readOnlyDetected = ""; - for(let i = 0; i < lines.length; i++) - { - if(lines[i].startsWith("/dev/mmcblk0p2")) - { - readOnlyDetected = lines[i]; + let lines = stdout + ""; + lines = lines.split("\n"); + + let readOnlyDetected = ""; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith("/dev/mmcblk0p2")) { + readOnlyDetected = lines[i]; + } } + + if (readOnlyDetected !== "") { + errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); + monitor.info("Read only filesystem detected"); + } + } - - if(readOnlyDetected !== "") - { - errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); - monitor.info("Read only filesystem detected"); - } - - } - }); -} - - - - - - - - -///helper functions -function sendTelemetry(values, tbname, date=Date.now()) -{ - const dataToTb = { - [tbname]: [ - { - "ts": date, - "values": values - } - ] + }); } - tbHandler.sendToTb(dataToTb, instance); -} - -function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) -{ - - if(date === undefined) date = new Date(); - //if(duskOffset === undefined) duskOffset = 0; - //if(dawnOffset === undefined) dawnOffset = 0; - - //let line = keys[i]; - let profilestr = ""; - if(relaysData[line] != undefined) profilestr = relaysData[line].profile; - - let result = {}; - - var times = SunCalc.getTimes(date, latitude, longitude); - let dawn = new Date(times.sunrise);//usvit - let dusk = new Date(times.sunset);//sumrak - - - //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 - //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 - let dusk_astro_clock_offset = duskOffset;//minutes - let dawn_astro_clock_offset = dawnOffset;//minutes - try { - let profile = JSON.parse(profilestr); - if(Object.keys(profile).length === 0) throw ("profile is not defined"); - //Jednoduchý režim - if(profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) - { + + ///helper functions + function sendTelemetry(values, tbname, date = Date.now()) { + const dataToTb = { + [tbname]: [ + { + "ts": date, + "values": values + } + ] } - //Režim astrohodín - if(profile.astro_clock == true) - { - //if(profile.dusk_lux_sensor == false) - { - if(profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt( profile.dusk_astro_clock_offset ); + tbHandler.sendToTb(dataToTb, instance); + } + + function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) { + + if (date === undefined) date = new Date(); + //if(duskOffset === undefined) duskOffset = 0; + //if(dawnOffset === undefined) dawnOffset = 0; + + //let line = keys[i]; + let profilestr = ""; + if (relaysData[line] != undefined) profilestr = relaysData[line].profile; + + let result = {}; + + var times = SunCalc.getTimes(date, latitude, longitude); + let dawn = new Date(times.sunrise);//usvit + let dusk = new Date(times.sunset);//sumrak + + + //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 + //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 + + + let dusk_astro_clock_offset = duskOffset;//minutes + let dawn_astro_clock_offset = dawnOffset;//minutes + + try { + + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("profile is not defined"); + + //Jednoduchý režim + if (profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) { + } - //if(profile.dawn_lux_sensor == false) - { - if(profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt( profile.dawn_astro_clock_offset ); + //Režim astrohodín + if (profile.astro_clock == true) { + //if(profile.dusk_lux_sensor == false) + { + if (profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt(profile.dusk_astro_clock_offset); + } + + //if(profile.dawn_lux_sensor == false) + { + if (profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt(profile.dawn_astro_clock_offset); + } + + } + + //dusk - súmrak + //down, sunrise - svitanie + + } catch (error) { + if (profilestr != "") { + logger.debug(profilestr); + logger.debug(error); } - } - //dusk - súmrak - //down, sunrise - svitanie - - } catch (error) { - if(profilestr != "") - { - logger.debug(profilestr); - logger.debug(error); + result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + + dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset * 60000); + dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset * 60000); + + result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dusk_hours = dusk.getHours(); + result.dusk_minutes = dusk.getMinutes(); + + result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + result.dawn_hours = dawn.getHours(); + result.dawn_minutes = dawn.getMinutes(); + + result.dusk_time = dusk.getTime(); + result.dawn_time = dawn.getTime(); + + result.dusk_astro_clock_offset = dusk_astro_clock_offset; + result.dawn_astro_clock_offset = dawn_astro_clock_offset; + + return result; + } + + + function processResponse(register, bytes) { + + let values = {}; + + let byte3 = bytes[0]; + let byte2 = bytes[1]; + let byte1 = bytes[2]; + let byte0 = bytes[3]; + + //status + if (register == 0) { + let statecode = bytesToInt(bytes); + values = { "statecode": statecode }; + return values; } - } - result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + //Dimming, CCT + if (register == 1) { + let brightness = 0; + let dimming = byte0; + if (dimming > 128) { + //dimming = -128; + brightness = dimming - 128; + } - dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset*60000); - dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset*60000); + //cct + //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 + let cct; + if (byte3 == 1) cct = byte2 * 256 + byte1; + else cct = bytesToInt(bytes.slice(0, 3)); - result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dusk_hours = dusk.getHours(); - result.dusk_minutes = dusk.getMinutes(); + //cct podla auditu - result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); - result.dawn_hours = dawn.getHours(); - result.dawn_minutes = dawn.getMinutes(); + values["dimming"] = brightness; + return values; + } - result.dusk_time = dusk.getTime(); - result.dawn_time = dawn.getTime(); + // + if (register == 4) { + values["master_node_version"] = bytes[1] + "." + bytes[2]; + //logger.debug("FW Version", register, bytes); + } - result.dusk_astro_clock_offset = dusk_astro_clock_offset; - result.dawn_astro_clock_offset = dawn_astro_clock_offset; + //Napätie + if (register == 74) { + let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); + values["voltage"] = Number(voltage); + } - return result; -} + //Prúd + if (register == 75) { + let current = bytesToInt(bytes); + values["current"] = current; + } + //výkon + if (register == 76) { + let power = (bytesToInt(bytes) * 0.1).toFixed(2); + values["power"] = Number(power); + } -function processResponse(register, bytes) -{ + //účinník + if (register == 77) { + let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); + values["power_factor"] = Number(power_factor); + } - let values = {}; + //frekvencia + if (register == 78) { + let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); + values["frequency"] = Number(frequency); + } - let byte3 = bytes[0]; - let byte2 = bytes[1]; - let byte1 = bytes[2]; - let byte0 = bytes[3]; + //energia + if (register == 79) { + let energy = bytesToInt(bytes); + + //Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal. + + values["energy"] = energy / 1000; + } + + //doba života + if (register == 80) { + let lifetime = (bytesToInt(bytes) / 60).toFixed(2); + values["lifetime"] = Number(lifetime); + } + + //nastavenie profilu + if (register == 8) { + let time_schedule_settings = bytesToInt(bytes); + values["time_schedule_settings"] = time_schedule_settings; + } + + //skupinová adresa 1 + if (register == 3) { + let gr_add_1 = bytesToInt(byte0); + values["gr_add_1"] = gr_add_1; + + let gr_add_2 = bytesToInt(byte1); + values["gr_add_2"] = gr_add_2; + + let gr_add_3 = bytesToInt(byte2); + values["gr_add_3"] = gr_add_3; + + let gr_add_4 = bytesToInt(byte3); + values["gr_add_4"] = gr_add_4; + } + + //naklon + if (register == 84) { + let temp; + if (byte3 >= 128) { + temp = (byte3 - 128) * (-1); + } + else { + temp = byte3; + } + + let inclination_x; + if (byte2 >= 128) { + inclination_x = (byte2 - 128) * (-1); + } + else { + inclination_x = byte2; + } + + let inclination_y; + if (byte1 >= 128) { + inclination_y = (byte1 - 128) * (-1); + } + else { + inclination_y = byte1; + } + + let inclination_z; + if (byte0 >= 128) { + inclination_z = (byte0 - 128) * (-1); + } + else { + inclination_z = byte0; + } + + values["temperature"] = temp; + + //náklon x + values["inclination_x"] = inclination_x; + + //náklon y + values["inclination_y"] = inclination_y; + + //náklon z + values["inclination_z"] = inclination_z; + } + + let h = byte3; + let m = byte2; + + let timestamp; + + if (register == 87 || register == 6 || register == 7) { + //if(byte3 < 10) h = "0" + byte3; + //if(byte2 < 10) m = "0" + byte2; + //if(byte1 < 10) s = "0" + byte1; + + var d = new Date(); + d.setHours(h, m, 0); + timestamp = d.getTime(); + } + + //aktuálny čas + if (register == 87) { + //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. + //values["actual_time"] = h + ":" + m + ":" + s; + + values["actual_time"] = timestamp; + } + + //čas súmraku + if (register == 6) { + //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. + //values["dusk_time"] = h + ":" + m + ":" + s; + + values["dusk_time"] = timestamp; + } + + //čas úsvitu + if (register == 7) { + //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. + //values["dawn_time"] = h + ":" + m + ":" + s; + + values["dawn_time"] = timestamp; + } + + //FW verzia + if (register == 89) { + //formát: "Byte3: Byte2.Byte1 (Byte0)" + values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; + } - //status - if(register == 0) - { - let statecode = bytesToInt(bytes); - values = {"statecode": statecode}; return values; } - //Dimming, CCT - if(register == 1) - { - let brightness = 0; - let dimming = byte0; - if(dimming > 128) { - //dimming = -128; - brightness = dimming - 128; + + //byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB + function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { + let resp = []; + + let cmd = register; + + if (typeof adresa === 'string') adresa = parseInt(adresa); + if (typeof byte1 === 'string') byte1 = parseInt(byte1); + if (typeof byte2 === 'string') byte2 = parseInt(byte2); + if (typeof byte3 === 'string') byte3 = parseInt(byte3); + if (typeof byte4 === 'string') byte4 = parseInt(byte4); + + if (rw === 0) { + cmd = cmd + 0x8000; } - //cct - //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 - let cct; - if(byte3 == 1) cct = byte2*256 + byte1; - else cct = bytesToInt(bytes.slice(0, 3)); + //master + if (rec === 0) adresa = 0; - //cct podla auditu - - values["dimming"] = brightness; - return values; - } - - // - if(register == 4) - { - values["master_node_version"] = bytes[1] + "." + bytes[2]; - //logger.debug("FW Version", register, bytes); - } - - //Napätie - if(register == 74) - { - let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); - values["voltage"] = Number(voltage); - } - - //Prúd - if(register == 75) - { - let current = bytesToInt(bytes); - values["current"] = current; - } - - //výkon - if(register == 76) - { - let power = (bytesToInt(bytes) * 0.1).toFixed(2); - values["power"] = Number(power); - } - - //účinník - if(register == 77) - { - let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); - values["power_factor"] = Number(power_factor); - } - - //frekvencia - if(register == 78) - { - let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); - values["frequency"] = Number(frequency); - } - - //energia - if(register == 79) - { - let energy = bytesToInt(bytes); - - //Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal. - - values["energy"] = energy / 1000; - } - - //doba života - if(register == 80) - { - let lifetime = ( bytesToInt(bytes) / 60).toFixed(2); - values["lifetime"] = Number(lifetime); - } - - //nastavenie profilu - if(register == 8) - { - let time_schedule_settings = bytesToInt(bytes); - values["time_schedule_settings"] = time_schedule_settings; - } - - //skupinová adresa 1 - if(register == 3) - { - let gr_add_1 = bytesToInt(byte0); - values["gr_add_1"] = gr_add_1; - - let gr_add_2 = bytesToInt(byte1); - values["gr_add_2"] = gr_add_2; - - let gr_add_3 = bytesToInt(byte2); - values["gr_add_3"] = gr_add_3; - - let gr_add_4 = bytesToInt(byte3); - values["gr_add_4"] = gr_add_4; - } - - //naklon - if(register == 84) - { - let temp; - if(byte3 >= 128) - { - temp = (byte3 - 128) * (-1); - } - else - { - temp = byte3; + if (rec === 2) { + adresa = 0xffffffff;//Broadcast } - let inclination_x; - if(byte2 >= 128) - { - inclination_x = (byte2 - 128) * (-1); - } - else - { - inclination_x = byte2; + //recipient + if (rec === 3) { + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(adresa & 0xFF);//band } + else { + resp.push((adresa >> 24) & 0xFF);//rshift + resp.push((adresa >> 16) & 0xFF); + resp.push((adresa >> 8) & 0xFF); + resp.push(adresa & 0xFF); - let inclination_y; - if(byte1 >= 128) - { - inclination_y = (byte1 - 128) * (-1); - } - else - { - inclination_y = byte1; - } - - let inclination_z; - if(byte0 >= 128) - { - inclination_z = (byte0 - 128) * (-1); - } - else - { - inclination_z = byte0; - } - - values["temperature"] = temp; - - //náklon x - values["inclination_x"] = inclination_x; - - //náklon y - values["inclination_y"] = inclination_y; - - //náklon z - values["inclination_z"] = inclination_z; - } - - let h = byte3; - let m = byte2; - let s = byte1; - - let timestamp; - - if(register == 87 || register == 6 || register == 7 ) - { - //if(byte3 < 10) h = "0" + byte3; - //if(byte2 < 10) m = "0" + byte2; - //if(byte1 < 10) s = "0" + byte1; - - var d = new Date(); - d.setHours(h); - d.setMinutes(m); - d.setSeconds(0); - d.setMilliseconds(0); - - timestamp = d.getTime(); - } - - //aktuálny čas - if(register == 87) - { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["actual_time"] = h + ":" + m + ":" + s; - - values["actual_time"] = timestamp; - } - - //čas súmraku - if(register == 6) - { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["dusk_time"] = h + ":" + m + ":" + s; - - values["dusk_time"] = timestamp; - } - - //čas úsvitu - if(register == 7) - { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["dawn_time"] = h + ":" + m + ":" + s; - - values["dawn_time"] = timestamp; - } - - //FW verzia - if(register == 89) - { - //formát: "Byte3: Byte2.Byte1 (Byte0)" - values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; - } - - return values; -} - - -//byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB -function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { - let resp = []; - - let cmd = register; - - if (typeof adresa === 'string') adresa = parseInt(adresa); - if (typeof byte1 === 'string') byte1 = parseInt(byte1); - if (typeof byte2 === 'string') byte2 = parseInt(byte2); - if (typeof byte3 === 'string') byte3 = parseInt(byte3); - if (typeof byte4 === 'string') byte4 = parseInt(byte4); - - if (rw === 0) - { - cmd = cmd + 0x8000; - } - - //master - if(rec === 0) adresa = 0; - - if(rec === 2) - { - adresa = 0xffffffff;//Broadcast - } - - //recipient - if (rec === 3) - { - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push( adresa & 0xFF );//band - } - else - { - resp.push( (adresa >> 24) & 0xFF);//rshift - resp.push( (adresa >> 16) & 0xFF); - resp.push( (adresa >> 8) & 0xFF); - resp.push( adresa & 0xFF ); - - if (rec === 2) - { + if (rec === 2) { resp.push(0xFF); + } + else resp.push(0); } - else resp.push(0); + + resp.push((cmd >> 8) & 0xFF);//rshift + resp.push(cmd & 0xFF);//band + resp.push(byte1 & 0xFF);//band + resp.push(byte2 & 0xFF);//band + resp.push(byte3 & 0xFF);//band + resp.push(byte4 & 0xFF);//band + + //let data = '12345'; + let crc = crc16('ARC', resp); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; + + resp.push(c1); + resp.push(c2); + + //logger.debug("checksum", crc); + //logger.debug("resp", resp); + + return resp; + } - resp.push( (cmd >> 8) & 0xFF);//rshift - resp.push( cmd & 0xFF );//band - resp.push( byte1 & 0xFF );//band - resp.push( byte2 & 0xFF );//band - resp.push( byte3 & 0xFF );//band - resp.push( byte4 & 0xFF );//band - - //let data = '12345'; - let crc = crc16('ARC', resp); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; - - resp.push(c1); - resp.push(c2); - - //logger.debug("checksum", crc); - //logger.debug("resp", resp); - - return resp; - -} - function getObjectByTbValue(object, tbname) { return object[Object.keys(object).find(key => object[key].tbname === tbname)]; } - function isObject (item) { + function isObject(item) { return (typeof item === "object" && !Array.isArray(item) && item !== null); } diff --git a/flow/cmd_manager131.js b/flow/cmd_manager131.js deleted file mode 100644 index 63c1a1f..0000000 --- a/flow/cmd_manager131.js +++ /dev/null @@ -1,3603 +0,0 @@ -exports.id = 'cmd_manager'; -exports.title = 'CMD Manager'; -exports.group = 'Worksys'; -exports.color = '#5D9CEC'; -exports.version = '0.0.3'; -exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; - -//blue - send message to relays - -exports.input = 2; -exports.icon = 'cloud-upload'; -//exports.npm = ['serialport' , 'child_process']; - -exports.html = ` -
-
-
-
RPC - run RPC calls

-
-
-
@(User)
-
-
-
@(Password)
-
-
-
@(My edge)
-
-
-
-`; - -exports.readme = `Manager for CMD calls`; - -const SerialPort = require('serialport'); -const { exec } = require('child_process'); -const { crc8, crc16, crc32 } = require('easy-crc'); -const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js'); -const { bytesToInt, longToByteArray, addZeroBefore, isEmptyObject, convertUTCDateToLocalDate } = require('./helper/utils'); -const bitwise = require('bitwise'); - -var SunCalc = require('./helper/suncalc.js'); -const DataToTbHandler = require('./helper/DataToTbHandler.js'); -const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); -const { promisifyBuilder, makeMapFromDbResult} = require('./helper/db_helper.js'); -const { sendNotification, initNotifications, ERRWEIGHT } = require('./helper/notification_reporter.js'); - -const dbNodes = TABLE("nodes"); -const dbRelays = TABLE("relays"); -const dbSettings = TABLE("settings"); - -//https://github.com/log4js-node/log4js-node/blob/master/examples/example.js -//file: { type: 'file', filename: path.join(__dirname, 'log/file.log') } -var path = require('path'); -var log4js = require("log4js"); -const process = require('process'); - -//TODO - to remove? -// runTasks intervals -const SHORT_INTERVAL = 30; -const LONG_INTERVAL = 300; - -//send data to following instances: -const SEND_TO = { - debug: 0, - tb: 1, - http_response: 2, - dido_controller: 3, - infoSender: 4 -} - -const PRIORITY_TYPES = { - terminal: 0, - fw_detection: 1,//reserved only for FW detection - FLOW.OMS_masterNodeIsResponding - high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) - relay_profile: 3, - node_broadcast: 4, - node_profile: 5, - node_cmd: 6 -} - -const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes - -//list of command calls to process. Processing in runTasks function -let tasks = []; - -let interval = null;//timeout for procesing tasks -let customTasksInterval = null; -let refFlowdata = null;//holds reference to httprequest flowdata -let refFlowdataObj = {}; - -//load from settings -let latitude = 48.70826502;//48.682255758; -let longitude = 17.28455203;//17.278910807; - -const gmtOffset = 0; - -//ak nie je nastaveny -//https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ -//https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates - -//priorities for registers -let priorities = []; - -let minutes = 1; -priorities["0"] = minutes; -priorities["1"] = minutes; - -minutes = 5; -priorities["74"] = minutes; -priorities["75"] = minutes; -priorities["76"] = minutes; -priorities["77"] = minutes; -priorities["78"] = minutes; -priorities["79"] = minutes; -priorities["84"] = minutes; - -minutes = 10; -priorities["87"] = minutes; -priorities["6"] = minutes; -priorities["7"] = minutes; -priorities["80"] = minutes; -priorities["8"] = minutes; -priorities["3"] = minutes; -priorities["89"] = minutes; - -//prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) -let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89]; - -const errorHandler = new ErrorToServiceHandler(); - -let rotary_switch_state = "Off"; -let lux_sensor; -let state_of_breaker = {};//key is line, value is On/Off -let disconnectedReport = {};//key is tbname, value true/false - -let relaysData = {};//key is line, value is data from db -let nodesData = {};//key is node, value data from db - -//helper container for counting resolved group of commands (commands related to set profile) -let cmdCounter = {};//key is node, value is counter - -//END OF VARIABLE SETTINGS -//-------------------------------- - - -log4js.configure({ - appenders: { - errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } -}); - -const errLogger = log4js.getLogger("errLogs"); -const logger = log4js.getLogger(); -const monitor = log4js.getLogger("monitorLogs"); - -//USAGE -//logger.debug("text") -//monitor.info('info'); -//errLogger.error("some error"); - - -function cmdCounterResolve(address) -{ - if(cmdCounter.hasOwnProperty(address)) - { - cmdCounter[address] = cmdCounter[address] - 1; - - let result = cmdCounter[address]; - if(result == 0) delete cmdCounter[address]; - return result; - } - return -1; -} - - -function getParams(priority) -{ - let params = {}; - - //core rpc values - params.address = 0;//if(recipient === 0) address = 0; - params.byte1 = 0;//msb, podla dokumentacie data3 - params.byte2 = 0;//podla dokumentacie data2 - params.byte3 = 0;//podla dokumentacie data1 - params.byte4 = 0;//lsb, podla dokumentacie data0 - params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast - params.register = -1;//register number - params.rw = 0;//0: read, 1: write - - //other values - //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" - //params.tbname = tbname; - params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority - params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed - if(priority != undefined ) - { - params.timestamp = priority; - params.priority = priority; - } - - params.addMinutesToTimestamp = 0;//repeat task if value is > 0, - - //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" - //params.info = ""; - - return params; -} - - -async function loadSettings() -{ - let responseSettings = await promisifyBuilder(dbSettings.find()); - - latitude = responseSettings[0]["latitude"]; - longitude = responseSettings[0]["longitude"]; - - //globals - FLOW.OMS_language = responseSettings[0]["lang"]; - FLOW.OMS_rvo_name = responseSettings[0]["rvo_name"]; - FLOW.OMS_projects_id = responseSettings[0]["projects_id"]; - //FLOW.OMS_rvo_tbname = responseSettings[0]["tbname"]; - FLOW.OMS_temperature_adress = responseSettings[0]["temperature_adress"]; - FLOW.OMS_controller_type = responseSettings[0]["controller_type"]; - FLOW.OMS_serial_port = responseSettings[0]["serial_port"]; - FLOW.OMS_node_status_nok_time = responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000; // hour * minutes * seconds - //logger.debug('settings', responseSettings[0]); - - initNotifications(); -} - -loadSettings(); - - -async function loadNodes() -{ - const responseNodes = await promisifyBuilder(dbNodes.find()); - nodesData = makeMapFromDbResult(responseNodes, "node"); -} - -loadNodes(); - - -//nastav profil nodu -function processNodeProfile(node) -{ - if(rotary_switch_state != "Automatic") - { - logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); - return; - } - - let nodeObj = nodesData[node]; - let line = nodeObj.line; - - if(relaysData[line].contactor == 0) - { - logger.debug("line line is off", line, node); - return; - } - - if(nodeObj.processed == 1) - { - logger.debug("node was already processed", node); - return; - } - - let nodeProfile = nodeObj.profile; - logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); - if(nodeProfile) { - - try { - nodeProfile = JSON.parse(nodeProfile); - } catch (error) { - logger.debug("Cmd_manager - Error parsing node profile", error); - } - - } - - logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); - - let timestamp = PRIORITY_TYPES.node_cmd; - - removeTask({type: "set_node_profile", address: node}); - - if(nodeProfile === "") - { - //vypneme profil nodu, posleme cmd - //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. - //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia - - logger.debug("turn off profile"); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'turn off/reset node profile'; - - cmdCounter[node] = 1; - - tasks.push(params); - } - else - { - let tasksProfile = []; - - //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'turn off node profile'; - - tasksProfile.push(params); - - timestamp++; - - logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); - - //TS1 Time point a TS1 Time Point Levels - let register = 9; - for(let i = 0; i < nodeProfile.intervals.length; i++) - { - let obj = nodeProfile.intervals[i]; - //let timePoint = obj.time_point; - let dim_value = obj.value; - - - //Reg 9 až Reg 40 - - /* - Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. - Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). - Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. - Časový bod má formát: - Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované - Register úrovne má rovnaký formát ako dimming register (Reg 1). - */ - - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - let start_time = obj.start_time; - let t = start_time.split(":"); - //if(timePoint != undefined) t = timePoint.split(":"); - //else t = [0,0]; - - logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = parseInt(t[0]);//hh - params.byte2 = parseInt(t[1]);//mm - params.byte3 = 0;//ss - params.byte4 = 0;// - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0;//ss - params.byte4 = parseInt(dim_value) + 128;// - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point Levels ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - } - - //Threshold lux level for DUSK/DAWN - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - - //Time schedule settings na koniec - //if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor) - { - - logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 96; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = "Threshold lux level for DUSK/DAWN"; - - if(nodeProfile.dusk_lux_sensor) - { - let v = nodeProfile.dusk_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if(nodeProfile.dawn_lux_sensor) - { - let v = nodeProfile.dawn_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //DUSK/DAWN max. adjust period - { - - logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 97; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = "DUSK/DAWN max. adjust period"; - - if(nodeProfile.astro_clock) - { - let v = nodeProfile.dusk_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if(nodeProfile.astro_clock) - { - let v = nodeProfile.dawn_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //Static offset - { - - //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: - //Bity 0 – 6: hodnota v minútach - //Bit 7: znamienko (1 – mínus) - - logger.debug("processNodeProfile: Static offset", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 98; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = "Static offset"; - - if(nodeProfile.astro_clock) - { - let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); - let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); - - if(dusk_astro_clock_offset < 0) - { - params.byte3 = (dusk_astro_clock_offset * -1) + 128; - } - else - { - params.byte3 = dusk_astro_clock_offset; - } - - if(dawn_astro_clock_offset < 0) - { - params.byte4 = (dawn_astro_clock_offset * -1) + 128; - } - else - { - params.byte4 = dawn_astro_clock_offset; - } - } - - tasksProfile.push(params); - timestamp++; - } - - logger.debug("Time schedule settings - turn on", node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 8; - params.recipient = 1; - params.rw = 1;//write - - //Time schedule settings - let bits = []; - - //Byte 0 (LSB): - //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). - bits.push(1); - //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) - bits.push(0); - bits.push(0); - bits.push(0); - if(nodeProfile.astro_clock == true) - { - //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý - bits.push(1); - } - else bits.push(0); - - //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) - bits.push(0); - - //Bity 6-7 - zatiaľ nepoužité - bits.push(0); - bits.push(0); - - params.byte4 = bitwise.byte.write(bits.reverse()); - - //Byte 2 – nastavenie pre lux senzor: - bits = []; - - //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if(nodeProfile.dusk_lux_sensor == true)//sumrak - { - bits.push(1); - } - else bits.push(0); - - //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if(nodeProfile.dawn_lux_sensor == true)//usvit - { - bits.push(1); - } - else bits.push(0); - - //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. - bits.push(0);//zatial neimplementovane - - //Bit 3 – 7 - nepoužité - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - - params.byte2 = bitwise.byte.write(bits.reverse()); - params.timestamp = timestamp; - params.info = "Time schedule settings - turn on"; - - tasksProfile.push(params); - - //zaver - cmdCounter[node] = tasksProfile.length; - - //tasks.push(tasksProfile); - tasks = tasks.concat(tasksProfile); - - } - - logger.debug("finished set profile for ", node); - -} - - -function cleanUpRefFlowdataObj() -{ - let now = new Date(); - let timestamp = now.getTime(); - - //clear old refFlowdata references - let keys = Object.keys(refFlowdataObj); - for(let i = 0; i < keys.length; i++) - { - let timestampKey = keys[i]; - - if((timestamp - timestampKey) > 60*1000 ) - { - console.log("cleanUpRefFlowdataObj delete", timestampKey); - delete refFlowdataObj[ timestampKey ]; - } - } -} - - -function removeTask(obj) -{ - let keys = Object.keys(obj); - tasks = tasks.filter((task) => { - - let counter = 0; - for(let i = 0; i < keys.length; i++) - { - let key = keys[i]; - if(task.hasOwnProperty(key) && obj.hasOwnProperty(key)) - { - if(task[key] == obj[key]) counter++; - } - } - - if(counter == keys.length) return false; - return true; - }); -} - - -exports.install = function(instance) { - - let now = new Date(); - console.log("CMD Manager installed", now.toLocaleString("sk-SK")); - - const tbHandler = new DataToTbHandler(SEND_TO.tb); - tbHandler.setSender(exports.title); - - //FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name - //const errorHandler = new ErrorToServiceHandler(instance, SEND_TO.infoSender); - errorHandler.setProjectsId(FLOW.OMS_projects_id); - //const errorHandler = new ErrorToServiceHandler(instance); - //errorHandler.sendMessageToService("ahoj", 0); - - let sunCalcResult = calculateDuskDawn(); - - let reportDuskDawn = { - dusk_time: sunCalcResult.dusk_time, - dawn_time: sunCalcResult.dawn_time, - dusk_time_reported: undefined, - dawn_time_reported: undefined - }; - - - process.on('uncaughtException', function (err) { - //TODO send to service - - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); - - errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); - //process.exit(1); - }) - - //te();//force error - - - function processAllNodeProfilesOnLine(line) - { - - for (let k in nodesData) { - //node:number|tbname:string|line:number|profile:string|processed:boolean - - if(line == nodesData[k].line) - { - let node = nodesData[k].node; - let processed = nodesData[k].processed; - - if(!processed) - { - processNodeProfile(node); - } - else - { - logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); - } - } - } - } - - - async function loadRelaysData(line) { - - relaysData = await promisifyBuilder(dbRelays.find()); - relaysData = makeMapFromDbResult(relaysData, "line"); - - for (const [key, value] of Object.entries(relaysData)) - { - if(key == "0") continue; - if(line != undefined) - { - //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData - if(line != value.line ) continue; - } - - if(value.contactor == 1) processAllNodeProfilesOnLine(value.line); - } - -// console.log('.........', relaysData); - } - - - function reportOnlineNodeStatus(line) - { - //broadcast cas, o 3 sek neskor - status, brightness - //Po zapnutí línie broadcastovo aktualizovať predtým čas. - - logger.debug("--->reportOnlineNodeStatus for line", line); - - //return; - - //run broadcast //Actual time - addMinutesToTimestamp = 0; - - let params = {}; - - var d = new Date(); - let hours = d.getHours(); - let minutes = d.getMinutes(); - let seconds = d.getSeconds(); - - let time = d.getTime(); // time in ms - - params.address = 0xffffffff;//Broadcast - params.byte1 = hours;//h - params.byte2 = minutes;//m - params.byte3 = seconds;//s - params.byte4 = 0; - params.recipient = 2;//2 broadcast, address = 0 - params.register = 87;//Actual time - params.rw = 1;//write - - //other values - params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "run broadcast: Actual time"; - - tasks.push(params); - - let sec = 3; - setTimeout(function(){ - //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel - - for (let k in nodesData) { - - //potrebujem nody k danej linii - if(line == nodesData[k].line || line == undefined) - { - let tbname = nodesData[k].tbname; - let node = nodesData[k].node; - let status = "NOK"; - - // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. - if(nodesData[k].status) { - status = "OK"; - nodesData[k].time_of_last_communication = time; - } - - let dataToTb = { - [tbname]: [ - { - ts: time, - values: { - status: status - } - } - ] - } - - tbHandler.sendToTb(dataToTb, instance); - - //prud, vykon - current, input power pre liniu pre vsetky nody - - //a pridame aj vyreportovanie dimmingu - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read dimming'; - //params.debug = true; - - tasks.push(params); - } - - //Prúd - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 75;//prud - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read current'; - //params.debug = true; - - tasks.push(params); - } - - //výkon - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 76;//výkon - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read power'; - //params.debug = true; - - tasks.push(params); - } - } - } - },sec*1000); - } - - - function reportOfflineNodeStatus(line) - { - logger.debug("--->reportOfflineNodeStatus for line", line); - - values = {}; - values["dimming"] = 0;//brightness - values["power"] = 0;//výkon - values["current"] = 0;//prúd - values["status"] = "OFFLINE"; - - // it happens, that some data did not get to tb after sending - // we setTimeout to make more time for db to process telemetry (eg 150 messages at once) - Object.keys(nodesData).forEach((node, index) => { - - setTimeout(function() { - - //potrebujem nody k danej linii - if(line == nodesData[node].line || line == undefined) - { - let tbname = nodesData[node].tbname; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - } - - },(index+1) * 300); - }) - - } - - - function turnOnLine(line, info) - { - let obj = { - line: line, - command: "turnOn", - info: info - }; - - logger.debug("linia", line, obj); - instance.send(SEND_TO.dido_controller, obj); - } - - function turnOffLine(line, info) - { - let obj = { - line: line, - command: "turnOff", - info: info - }; - - logger.debug("linia", line, obj); - instance.send(SEND_TO.dido_controller, obj); - } - - - function detectIfResponseIsValid(bytes) - { - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - let type = "RESPONSE"; - if(bytes[4] == 0) type = "RESPONSE"; - else if(bytes[4] == 1) type = "ERROR"; - else if(bytes[4] == 2) type = "EVENT"; - else type = "UNKNOWN"; - - let crc = crc16('ARC', bytes.slice(0, 9)); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; - - let message = "OK"; - let error = ""; - if(c1 != bytes[9]) - { - //CRC_ERROR - message = "NOK"; - error = "CRC_ERROR c1"; - instance.send(SEND_TO.debug, "CRC_ERROR c1"); - } - - if(c2 != bytes[10]) - { - //CRC_ERROR - message = "NOK"; - error = "CRC_ERROR c2"; - instance.send(SEND_TO.debug, "CRC_ERROR c2"); - } - - //crc error - if(type != "RESPONSE") - { - instance.send(SEND_TO.debug, bytes); - instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); - - //logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes); - - error = "type is: " + type; - - message = "NOK"; - } - - return {message, type, error}; - } - - - //BUILD TASKS// - function buildTasks(params) - { - //report FLOW.OMS_edge_fw_version as fw_version - //report date as startdate - - //return; - - monitor.info("buildTasks - params", params); - - let processLine; //defined line - let init = false; - let processLineProfiles = true; - let processBroadcast = true; - let processNodes = true; - - if(params == undefined) - { - init = true; - tasks = []; - logger.debug("-->buildTasks clear tasks"); - } - else - { - processLineProfiles = false; - processBroadcast = false; - processNodes = false; - - processLineProfiles = params.processLineProfiles; - processLine = params.line; - } - - //load profiles pre linie - //relaysData[ record["line"] ] - - let now = new Date(); - - if(processLineProfiles) - { - //process line profiles - let keys = Object.keys(relaysData); - for(let i = 0; i < keys.length; i++) - { - let line = parseInt(keys[i]); //line is turned off by default - let profilestr = relaysData[line].profile; - - if(processLine != undefined) - { - if(processLine != line) continue; - } - - try { - - /** - * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... - */ - if(profilestr === "") throw ("Profile is not defined"); - let profile = JSON.parse(profilestr); - if(Object.keys(profile).length === 0) throw ("Profile is empty"); - - monitor.info("buildTasks: profile for line", line); - monitor.info("profile:", profile); - - let time_points = profile.time_points; - if(time_points == undefined) time_points = profile.intervals; - - // add name to regular profile timepoint and delete unused end_time key: - time_points.forEach(point => { - point.name = "profileTimepoint" - delete point.end_time; - }); - - //monitor.info("buildTasks: time_points", time_points); - - let currentValue = 0; - if(time_points.length > 0) currentValue = time_points[time_points.length - 1].value; - - - /** - * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. - * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn - * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching - */ - if(profile.astro_clock == true) - { - - // if astro clock true, we remove all regular profile points - time_points = []; - - let sunCalcResult = calculateDuskDawn(new Date(), line); - - // adding dusk dawn to timpoints - if(profile.dawn_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dawn"], "value": 0, "name":"dawn"}); - if(profile.dusk_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dusk"], "value": 1, "name":"dusk"}); - - //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) - //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window - if(profile.dawn_lux_sensor == true) - { - let [ahours, aminutes, aseconds] = sunCalcResult["dawn"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours)); - ad.setMinutes(parseInt(aminutes) + profile.dawn_lux_sensor_time_window); - ad.setSeconds(0); - - let strDate = ad.getHours() + ":" + ad.getMinutes(); - - time_points.push({"value": 0, "start_time": strDate, "name": "luxOff"}); - } - - if(profile.dusk_lux_sensor == true) - { - let [ahours, aminutes, aseconds] = sunCalcResult["dusk"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours)); - ad.setMinutes(parseInt(aminutes) + profile.dusk_lux_sensor_time_window); - ad.setSeconds(0); - - let strDate = ad.getHours() + ":" + ad.getMinutes(); - - time_points.push({"value": 1, "start_time": strDate, "name": "luxOn"}); - //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing - } - } - - //sort time_points - time_points.sort(function (a, b) { - - let [ahours, aminutes, aseconds] = a.start_time.split(':'); - let [bhours, bminutes, bseconds] = b.start_time.split(':'); - - let ad = new Date(); - ad.setHours( parseInt(ahours) ); - ad.setMinutes( parseInt(aminutes) ); - ad.setSeconds(0); - - let bd = new Date(); - bd.setHours( parseInt(bhours) ); - bd.setMinutes( parseInt(bminutes) ); - ad.setSeconds(0); - - return ad.getTime() - bd.getTime(); - }); - - console.log("line timepoints ........", time_points); - - monitor.info("-->comming events turn on/off lines:"); - for(let t = 0; t < time_points.length; t++) - { - - let start_time = new Date(); - let [hours, minutes, seconds] = time_points[t].start_time.split(':'); - - start_time.setHours( parseInt(hours) ); - start_time.setMinutes( parseInt(minutes) ); - start_time.setSeconds(0); - - //task is the past - if(now.getTime() > start_time.getTime()) - { - currentValue = time_points[t].value; - - //timepoint is in past, we add 24 hours - start_time.setDate(start_time.getDate() + 1); - } - - let params = getParams(PRIORITY_TYPES.relay_profile); - params.type = "relay"; - params.line = parseInt(line); - params.value = time_points[t].value; - params.tbname = relaysData[line].tbname; - params.timestamp = start_time.getTime(); - - params.addMinutesToTimestamp = 0; - - // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day - if(time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24*60; - - //astro timepoints will be recalculated dynamically: - params.timePointName = time_points[t].name; - - // if astro timepoint, we save time window: - if(['luxOn', 'luxOff', 'dusk','dawn'].includes(params.timePointName)) - { - params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; - params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; - } - - if(params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; - else if(params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; - - params.debug = true; - - //turn on/off line - tasks.push(params); - monitor.info(params.info, start_time); - - } - - monitor.info("-->time_points final", line, time_points); - - //ensure to turn on/off according to calculated value - let params = getParams(PRIORITY_TYPES.terminal); - params.type = "relay"; - params.line = parseInt(line); - params.tbname = relaysData[line].tbname; - params.value = currentValue; - - params.timestamp = PRIORITY_TYPES.terminal; - params.addMinutesToTimestamp = 0; - params.debug = true; - - //logger.debug(now.toLocaleString("sk-SK")); - monitor.info("-->currentValue for relay", line, currentValue); - - //turn on/off line - if(params.value == 0) params.info = "turn off line on startup: " + line; - else if(params.value == 1) params.info = "turn on line on startup: " + line; - - tasks.push(params); - - } catch (error) { - if(profilestr !=="" ) - { - //errLogger.error(profilestr, error); - errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); - } - } - - } - - //logger.debug("tasks:"); - //logger.debug(tasks); - } - - - //PROCESS DEFAULT BROADCASTS - - //RPC pre nody / broadcast - //Time of dusk, Time of dawn - //Actual Time - - if(processBroadcast) - { - let addMinutesToTimestamp = 5; - - { - //run broadcast Time of dusk - addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk - - let params = getParams(PRIORITY_TYPES.node_broadcast); - - let sunCalcResult = calculateDuskDawn(); - let dusk_hours = sunCalcResult["dusk_hours"]; - let dusk_minutes = sunCalcResult["dusk_minutes"]; - - params.address = 0xffffffff;//broadcast - params.byte1 = dusk_hours;//h - params.byte2 = dusk_minutes;//m - params.byte3 = 0;//s - params.byte4 = 0; - params.recipient = 2;//2 broadcast, - params.register = 6;//Time of dusk - Reg 6 - params.rw = 1;//write - - //other values - params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "Broadcast-duskTime"; - - tasks.push(params); - } - - { - - //run broadcast Time of dawn - // addMinutesToTimestamp = 60*5; - addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn - - let params = getParams(PRIORITY_TYPES.node_broadcast); - - let sunCalcResult = calculateDuskDawn(); - let dawn_hours = sunCalcResult["dawn_hours"]; - let dawn_minutes = sunCalcResult["dawn_minutes"]; - - params.address = 0xffffffff;//broadcast - params.byte1 = dawn_hours;//h - params.byte2 = dawn_minutes;//m - params.byte3 = 0;//s - params.byte4 = 0; - params.recipient = 2; //2 broadcast - params.register = 7;//Time of dawn - Reg 6 - params.rw = 1;//write - - //other values - params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "Broadcast-dawnTime"; - - tasks.push(params); - } - - { - //run broadcast //Actual time - addMinutesToTimestamp = 5; - - let params = getParams(PRIORITY_TYPES.node_broadcast); - - var d = new Date(); - let hours = d.getHours(); - let minutes = d.getMinutes(); - let seconds = d.getSeconds(); - - params.address = 0xffffffff;//broadcast - params.byte1 = hours;//h - params.byte2 = minutes;//m - params.byte3 = seconds;//s - params.byte4 = 0; - params.recipient = 2; //2 broadcast - params.register = 87;//Actual time - params.rw = 1;//write - - //other values - params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "run broadcast: Actual time"; - - tasks.push(params); - } - - } - - //process nodes & tasks - //reportovanie pre platformu - if(processNodes) - { - for (let k in nodesData) { - let address = parseInt(k); - let tbname = nodesData[k].tbname; - let register = 0; - - //logger.debug("generated cmd - buildTasks for node:", address); - - //listOfCommands - READ - for(let i = 0; i < listOfCommands.length; i++) - { - register = listOfCommands[i]; - - let params = getParams(PRIORITY_TYPES.node_cmd); - - //core rpc values - params.address = address; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; - params.byte4 = 0; - params.recipient = 1; - params.register = register; - params.rw = 0; - - let addMinutesToTimestamp = priorities[register]; - - let timestampStart = PRIORITY_TYPES.node_cmd; //run imediatelly in function runTasks - if(addMinutesToTimestamp > 1) - { - timestampStart = timestampStart + addMinutesToTimestamp * 60000; - } - - //other values - params.type = "cmd"; - params.tbname = tbname; - params.timestamp = timestampStart; - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "generated cmd - buildTasks (node)"; - - //monitor last node && last command - /* - if(register == listOfCommands[ listOfCommands.length - 1 ]) - { - //if(k == 632) params.debug = true; - if(k == 698) params.debug = true; - } - */ - - tasks.push(params); - - } - } - } - - - //niektore ulohy sa vygeneruju iba 1x pri starte!!! - if(!init) return; - - - //Priebežne (raz za cca 5 minút) je potrebné vyčítať z Master nodu verziu jeho FW. - //Jedná sa o register 10. Rovnaká interpretácia ako pri FW verzii nodu. - //Adresa mastera je 0. V prípade že kedykoľvek nastane situácia že Master Node neodpovedá (napríklad pri vyčítaní telemetrie z nodu nevráti žiadne dáta), - //tak treba vyreportovať string "NOK". - { - let params = getParams(PRIORITY_TYPES.fw_detection); - params.type = "cmd-master"; - params.register = 4; - params.address = 0; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = 5; - params.tbname = FLOW.OMS_edgeName; - params.info = "Master node FW verzia"; - //params.debug = true; - - //this will set FLOW.OMS_masterNodeIsResponding - - tasks.push(params); - } - - //kazdu hodinu skontrolovat nastavenie profilov - { - let params = getParams(PRIORITY_TYPES.fw_detection); - params.type = "process_profiles"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = 60;//60 = every hour - params.info = "detekcia nespracovaných profilov linie a nodov"; - //params.debug = true; - - tasks.push(params); - } - - monitor.info("tasks created:", tasks.length); - } - - - /** - * We process line profile, where "astro_clock": true - * example profile: - * - "dawn_lux_sensor": true, - "dusk_lux_sensor": true, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 0, - "dusk_astro_clock_offset": 10, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - - * if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line. - * if dusk: we do oposite - * - * dawn: usvit - lux je nad hranicou - vypnem - * dusk: sumrak - lux je pod hranicou - zapnem - */ - function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) - { - - let now = new Date(); - let currentTimestamp = now.getTime(); - let keys = Object.keys(relaysData); - - for(let i = 0; i < keys.length; i++) - { - - let line = keys[i]; //line is turned off by default - let profilestr = relaysData[line].profile; - const contactor = relaysData[line].contactor; - - try { - - let profile = JSON.parse(profilestr); - if(Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); - - if(profile.astro_clock == true) - { - let sunCalcResult = calculateDuskDawn(now, line); - - //usvit - if(profile.dawn_lux_sensor == true) - { - let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut - let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); - - if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) - { - if(lux_sensor_value > profile.dawn_lux_sensor_value) - { - if(contactor) turnOffLine(line, "Profile: dawn - turnOff line according to lux sensor"); - } - } - } - - //sumrak - if(profile.dusk_lux_sensor == true) - { - let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60); - let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60); - - if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) - { - if(lux_sensor_value < profile.dusk_lux_sensor_value) - { - if(!contactor) turnOnLine(line, "Profile: dusk - turnOn line according to lux sensor"); - } - } - } - - } - - } catch (error) { - if(profilestr !== "" ) monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); - } - - } - - } - - /** - * function updates status and time_of_last_communication of node in the dbNodes - * it only updates if conditions are met - * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) - * if newStatus of node is always receiving false, and it is already for more than FLOW.OMS_node_status_nok_time value, we update status to "NOK" in tb - * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). - */ - function updateNodeStatus(node, newStatus) - { - //MASTER - if(node == 0) return; - - let nodeObj = nodesData[node]; - if(nodeObj == undefined) return; - - let nodeCurrentStatus = nodeObj.status; - const now = Date.now(); - - if(newStatus == false && nodeCurrentStatus == false) { - if(node == 638 || node == 637) console.log("false, false, return", node, now) - return true; - } - - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION){ - - if(node == 638 || node == 637) console.log("true true, return", node, now); - return; - } - - if(newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) - { - dbNodes.modify({ time_of_last_communication: now}).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if(!err) { - nodeObj.time_of_last_communication = now; - - if(node == 638 || node == 637) console.log('zapisane do db => status true & true', node, now) - } - }); - }); - return; - } - - if(newStatus == false && nodeCurrentStatus == true) - { - if(nodeObj.time_of_last_communication + FLOW.OMS_node_status_nok_time > now) { - if(node == 638 || node == 637) console.log('false true, return', node, now); - return; - } - else { - dbNodes.modify({ status: newStatus}).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if(!err) { - nodeObj.status = newStatus; - - if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) - } - }); - }); - return true; - } - } - - if(newStatus == true && nodeCurrentStatus == false) - { - dbNodes.modify({ status: newStatus, time_of_last_communication: now}).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if(!err) { - nodeObj.status = newStatus; - nodeObj.time_of_last_communication = now; - - if(node == 638 || node == 637) console.log('zapisane do db => status false & true', node, now) - } - }); - }); - return; - } - } - - - async function runTasks(){ - - clearInterval(interval); - - let currentTimestamp = Date.now(); - - //report dusk, dawn--------------------------------- - if(reportDuskDawn.dusk_time < currentTimestamp) - { - //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if( (currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) - { - //reportovali sme? - if(reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) - { - sendNotification("CMD Manager: calculated Time of dusk", FLOW.OMS_edgeName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", SEND_TO.tb, instance); - reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; - } - } - - var nextDay = new Date(); - nextDay.setDate(nextDay.getDate() + 1); - - sunCalcResult = calculateDuskDawn(nextDay); - reportDuskDawn.dusk_time = sunCalcResult.dusk_time; - } - - if(reportDuskDawn.dawn_time < currentTimestamp) - { - //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if( (currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) - { - //reportovali sme? - if(reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) - { - sendNotification("CMD Manager: calculated Time of dawn", FLOW.OMS_edgeName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", SEND_TO.tb, instance); - reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; - } - } - - var nextDay = new Date(); - nextDay.setDate(nextDay.getDate() + 1); - - sunCalcResult = calculateDuskDawn(nextDay); - reportDuskDawn.dawn_time = sunCalcResult.dawn_time; - - } - //-------------------------------------------------------- - - //sort tasks based on timestamp - tasks.sort(function (a, b) { - if(a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) - { - return a.priority - b.priority; - } - return a.timestamp - b.timestamp; - }); - - if(tasks.length == 0 ) - { - instance.send(SEND_TO.debug, "no tasks created"); - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - if(!rsPort.isOpen) - { - instance.send(SEND_TO.debug, "!rsPort.isOpen"); - //await rsPort.open(); - } - - let currentTask = tasks[0]; - - if(currentTask.debug) - { - //logger.debug("--->task to process", currentTask); - } - - if(currentTask.timestamp <= currentTimestamp) - { - let params = {...tasks[0]}; - - //allow terminal commands - if(FLOW.OMS_maintenance_mode && params.type !== "cmd-terminal") - { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - let type = params.type; - let tbname = params.tbname; - let nodeAddress = params.address; - - let line = null; - - //rpc related - if(nodesData[nodeAddress] !== undefined) line = nodesData[nodeAddress].line; - if(params.line !== undefined) line = params.line; - - let repeatTask = false; - if(params.addMinutesToTimestamp > 0 || params.timePointName) repeatTask = true; - - if(repeatTask) - { - if(type === "cmd" || type === "cmd-master") - { - //set next start time automatically - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } - } - else - { - tasks.shift(); - } - - //kontrola nespracovanych profilov nodov - if(type == "process_profiles") - { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - - //vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov - loadRelaysData(); - - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } - - - //relay - if(type == "relay") - { - - const timePointName = params.timePointName; - const value = params.value; - - let date = new Date(); - date.setDate(date.getDate() + 1);//next day - - let sunCalcResult; - sunCalcResult = calculateDuskDawn(date, params.line); - - if(timePointName == "dawn") - { - tasks[0].timestamp = sunCalcResult.dawn_time; - } - else if(timePointName == "dusk") - { - tasks[0].timestamp = sunCalcResult.dusk_time; - } - else if(timePointName == "luxOn") - { - tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; - } - else if(timePointName == "luxOff") - { - tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; - } - else if(timePointName == "profileTimepoint") - { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } - - let info = "aplikovany bod profilu"; - let message = ""; - if(value == 1) - { - turnOnLine(params.line, info); - message = "on"; - } - else if(value == 0) - { - turnOffLine(params.line, info); - message = "off"; - } - - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance ); - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } - - //zhodeny hlavny istic - let disconnected = false; - //if(rotary_switch_state == "Off") disconnected = true; - - //state_of_breaker[line] - alebo istic linie - if(state_of_breaker.hasOwnProperty(line)) - { - //if(state_of_breaker[line] == "Off") disconnected = true; - } - - //toto sa reportuje po prijati dat z dido_controlera - if(disconnected) - { - let values = {"status": "OFFLINE"}; - - logger.debug("disconnected", values); - logger.debug("rotary_switch_state", rotary_switch_state); - logger.debug("state_of_breaker", state_of_breaker[line]); - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //report only once! - if(!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; - - if(!disconnectedReport[tbname]) - { - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - } - - interval = setInterval(runTasks, SHORT_INTERVAL); - - return; - } - - disconnectedReport[tbname] = false; - - const register = params.register; - - //high_priority - if(!FLOW.OMS_masterNodeIsResponding) - { - //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version - errorHandler.sendMessageToService("Master node is not responding"); - - let stop = true; - - //fw version - register == 4 - if(type == "cmd-terminal" || register == 4) stop = false; - if(stop) - { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - } - - let relayStatus = 1; - if(relaysData[line] != undefined) - { - relayStatus = relaysData[line].contactor; - } - - if(line == 0) relayStatus = 0; - if(type == "cmd-terminal") relayStatus = 1; - - //check if rotary_switch_state == "Off" - - if(relayStatus == 0) - { - //console.log("------------------------------------relayStatus", relayStatus, line); - let values = {"status": "OFFLINE"}; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } - - if(!rsPort.isOpen) - { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - //RE-CALCULATE VALUES - //set actual time for broadcast - if(register == 87 && params.recipient === 2) - { - var d = new Date(); - let hours = d.getHours(); - let minutes = d.getMinutes(); - let seconds = d.getSeconds(); - - params.byte1 = hours;//h - params.byte2 = minutes;//m - params.byte3 = seconds;//s - params.byte4 = 0; - } - - //SET DUSK/DAWN FOR BROADCAST - //Time of dusk - if(register == 6 && params.recipient === 2) - { - - if(type != "cmd-terminal") - { - let sunCalcResult = calculateDuskDawn(); - let dusk_hours = sunCalcResult["dusk_hours"]; - let dusk_minutes = sunCalcResult["dusk_minutes"]; - - params.byte1 = dusk_hours;//h - params.byte2 = dusk_minutes;//m - params.byte3 = 0;//s - params.byte4 = 0; - - //TODO astrohodiny - let dusk = "Time of dusk: " + sunCalcResult["dusk"]; - } - } - - //Time of dawn - if(register == 7 && params.recipient === 2) - { - if(type != "cmd-terminal") - { - let sunCalcResult = calculateDuskDawn(); - let dawn_hours = sunCalcResult["dawn_hours"]; - let dawn_minutes = sunCalcResult["dawn_minutes"]; - - params.byte1 = dawn_hours;//h - params.byte2 = dawn_minutes;//m - params.byte3 = 0;//s - params.byte4 = 0; - - //TODO astrohodiny - let dawn = "Time of dawn: " + sunCalcResult["dawn"]; - } - - } - //----------------------- - - instance.send(SEND_TO.debug, "address: " + nodeAddress + " register:" + register + "type: " + type); - - var startTime, endTime; - startTime = new Date(); - - let saveToTb = true; - if(!tbname) saveToTb = false; - let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) - - let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); - let readBytes = 11; - let timeout = 4000; - - // await keyword is important, otherwise incorrect data is returned! - await writeData(rsPort, resp, readBytes, timeout).then(function (data) { - - endTime = new Date(); - var timeDiff = endTime - startTime; - - //--1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data - //let bytes = data.slice(0); - let bytes = data; - let dataBytes = data.slice(5,9); - - let result = detectIfResponseIsValid(bytes); - - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - let message = result.message; // OK, NOK - let message_type = result.type; - let error = result.error; - - if(params.debug != "generated cmd") - { - //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug, params); - } - - // if(params.hasOwnProperty("debug")) - // { - // if(params.debug) - // { - // console.log("detected response:", result); - - // logger.debug("writeData: done " + message_typetype + " duration: " + timeDiff + " type: " + params.debug, params, result); - // } - // } - - //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug); - //debug("writeData done", message_type, "duration", timeDiff, "message_type", params.debug, result); - - let values = {}; - - //CMD FINISHED - if(message == "OK") - { - - updateNodeStatus(nodeAddress, true); - - //write - if(type == "set_node_profile") - { - let result = cmdCounterResolve(nodeAddress); - if(result == 0) - { - dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { - builder.callback(function(err, response) { - - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: nodeAddress}, "", SEND_TO.tb, instance ); - - logger.debug( "--> profil úspešne odoslaný na node č. " + nodeAddress); - nodesData[nodeAddress].processed = true; - }); - }); - } - } - - //parse read response - if(params.rw == 0) - { - values = processResponse(register, dataBytes); //read - } - - if(itIsNodeCommand) - { - values.comm_status = "OK"; - values.status = "OK"; - } - - //master node - if(nodeAddress == 0) - { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" ); - FLOW.OMS_masterNodeIsResponding = true; - if(register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version; - } - - //odoslanie príkazu z terminálu - dáta - if(type == "cmd-terminal") - { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance ); - } - - if(params.debug) - { - //logger.debug("saveToTb", saveToTb, tbname, values); - } - - if(saveToTb) - { - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - } - else - { - if(type == "cmd-terminal") - { - terminalCommandResponse(params, "SUCCESS", data); - } - } - - } - else - { - - terminalCommandResponse(params, "ERROR", data) - handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); - - if(params.hasOwnProperty("debug")) - { - if(params.debug) - { - //logger.debug("writeData err: ", error, result, params); - logger.debug("writeData err: ", tbname, nodeAddress, register, values); - } - } - - //logger.debug(error, result, params); - } - }).catch(function(reason) { - - console.log("writeData catch exception", reason); - instance.send(SEND_TO.debug, reason); - - terminalCommandResponse(params, "FAILURE", null, reason); - handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); - - if(params.hasOwnProperty("debug")) - { - if(params.debug) - { - logger.debug("-->WRITE FAILED: " + reason, params.debug, params); - } - } - - }); - - } - else - { - // if(currentTask.debug) - // { - // //currentTask.timestamp <= currentTimestamp - // logger.debug("currentTask is not processed - task is in the future", currentTask); - // } - - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - //console.log("----->runTasks - setInterval", new Date()); - interval = setInterval(runTasks, SHORT_INTERVAL); - } - - - function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb){ - - let node = params.address; - let register = params.register; - let type = params.type; - let tbName = params.tbname; - if(!tbName) return; - - let values = {}; - - // console.log(message); - let updateStatus = updateNodeStatus(node, false); - - //master node - if(node == 0) - { - sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); - logger.debug("master_node_is_not_responding", params); - FLOW.OMS_masterNodeIsResponding = false; - - if(register == 4) values["master_node_version"] = "NOK"; - } - - if(type == "set_node_profile") - { - delete cmdCounter[node]; - logger.debug( "profil nebol úspešne odoslaný na node č. ", params); - sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", {node: node}, "", SEND_TO.tb, instance ); - } - - if(itIsNodeCommand) - { - values.comm_status = "NOK"; - } - - if(updateStatus) - { - values.status = "NOK"; - } - - // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); - if(saveToTb && Object.keys(values).length > 0) - { - - let dataToTb = { - [tbName]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - tbHandler.sendToTb(dataToTb, instance); - } - - } - - - /** - * function handles requests from terminal - * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. - * FAILURE means, that we got into catch block of writeData function. - */ - function terminalCommandResponse(params, responseType, data=null, reason="") { //success, error, failure - - if(params.refFlowdataKey == undefined) - { - //console.log("params.refFlowdataKey is undefined", params); - return; - } - else - { - console.log("params.refFlowdataKey: ", params); - } - - let message = null; - let type = null; - - switch (responseType) { - case "SUCCESS": - message = "cmd-terminal SUCCESS"; - type = "SUCCESS"; - break; - case "ERROR": - message = "cmd-terminal FAILED"; - type = "ERROR"; - break; - case "FAILURE": - message = "ERROR WRITE FAILED: " + reason; - type = "ERROR"; - break; - default: - type = undefined; - } - - logger.debug(message); - logger.debug(params); - - //make http response - let responseObj = {} - responseObj["type"] = type; - - if(responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; - else responseObj["bytes"] = data; - - let refFlowdata = refFlowdataObj[params.refFlowdataKey]; - if(refFlowdata) - { - refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, refFlowdata); - } - } - - - /** - * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function - */ - function reportEdgeDateTimeAndNumberOfLuminaires(){ - - if(!FLOW.OMS_edgeName) return; - - //Number of ok and nok nodes on platform does not equals to total number of nodes. - //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: - let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); - - const ts = Date.now(); - const keys = Object.keys(nodesData_clone); - - const number_of_luminaires = keys.length; - let number_of_ok_luminaires = 0; - let number_of_nok_luminaires = 0; - - for(let i = 0; i < keys.length; i++) - { - let key = keys[i]; - let nodeObj = nodesData_clone[key]; - if(nodeObj.tbname == undefined) continue; - - if(nodeObj.status) number_of_ok_luminaires++; - else number_of_nok_luminaires++; - } - - const values = { - "number_of_luminaires": number_of_luminaires, - "number_of_ok_luminaires": number_of_ok_luminaires, - "number_of_nok_luminaires": number_of_nok_luminaires, - "edge_date_time": ts - }; - - let dataToTb = { - [FLOW.OMS_edgeName]: [ - { - "ts": ts, - "values": values - } - ] - } - - tbHandler.sendToTb(dataToTb, instance); - //instance.send(SEND_TO.tb, dataToTb); - } - - //to ensure, edgeDateTime will be send to tb at full minute - customTasksInterval = setInterval(function() { - if(new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); - }, 1000); - - - //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" - // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM - // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI - - if(FLOW.OMS_serial_port == "" || FLOW.OMS_serial_port == undefined || FLOW.OMS_serial_port.length === 1) FLOW.OMS_serial_port = "ttymxc4"; - const rsPort = new SerialPort(`/dev/${FLOW.OMS_serial_port}`, { autoOpen: false }); - //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit - //rsPort.setMaxListeners(0); - - rsPort.on('open', async function() { - - logger.debug("CMD manager - rsPort opened sucess"); - - //loadRelaysData(); - - await runSyncExec(`stty -F /dev/${FLOW.OMS_serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) { - instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); - - logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); - - //APP START - let dataToInfoSender = {id: FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name}; - dataToInfoSender.fw_version = FLOW.OMS_edge_fw_version; - dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); - dataToInfoSender.__force__ = true; - - instance.send(SEND_TO.infoSender, dataToInfoSender); - - logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); - - }).catch(function (reason) { - instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); - }); - }); - - rsPort.on('error', function(err) { - - //TODO report to service!!! - //errLogger.error(exports.title, "unable to open port", FLOW.OMS_serial_port, err.message); - errorHandler.sendMessageToService([exports.title, "unable to open port", FLOW.OMS_serial_port, err.message], 0); - - instance.send(SEND_TO.debug, err.message); - }); - - rsPort.on("close", () => { - rsPort.close(); - }); - - rsPort.open(); - - instance.on("close", () => { - clearInterval(interval); - clearInterval(customTasksInterval); - rsPort.close(); - }); - - - instance.on("data", async function(flowdata) { - - //instance.send(SEND_TO.debug, "on Data"); - //instance.send(SEND_TO.debug, flowdata); - - //logger.debug(flowdata.data); - - //just testing functions - if(flowdata.data == "open") - { - if(!rsPort.isOpen) rsPort.open(); - return; - } - else if(flowdata.data == "close") - { - rsPort.close(); - return; - } - else if(flowdata.data == "clean") - { - tasks = []; - return; - } - else if(flowdata.data == "buildtasks") - { - //build & run - return; - } - else if(flowdata.data == "run") - { - //durations = []; - - if(tasks.length == 0) - { - - buildTasks(); - - if(rsPort.isOpen) - { - interval = setInterval(runTasks, 100); - } - else - { - instance.send(SEND_TO.debug, "port is not opened!!!"); - } - } - } - else - { - //terminal data - object - //logger.debug("flowdata", flowdata.data); - - if(typeof flowdata.data === 'object') - { - //logger.debug("dido", flowdata.data); - if(flowdata.data.hasOwnProperty("sender")) - { - //data from dido_controller - if(flowdata.data.sender == "dido_controller") - { - - if(flowdata.data.hasOwnProperty("cmd")) - { - let cmd = flowdata.data.cmd; - - if(cmd == "buildTasks") - { - clearInterval(interval); - - logger.debug("-->CMD MANAGER - BUILD TASKS"); - buildTasks(); - - //logger.debug("tasks:"); - //logger.debug(tasks); - - logger.debug("-->CMD MANAGER - RUN TASKS"); - interval = setInterval(runTasks, LONG_INTERVAL); - } - else if(cmd == "reload_relays") - { - loadRelaysData(flowdata.data.line); - - if(flowdata.data.dataChanged) - { - if(!flowdata.data.value) - { - reportOfflineNodeStatus(flowdata.data.line); - } - else - { - reportOnlineNodeStatus(flowdata.data.line); - } - } - - } - else if(cmd == "rotary_switch_state") - { - //state was changed - if(rotary_switch_state != flowdata.data.value) - { - if(rotary_switch_state == "Off") - { - //vyreportovat vsetky svietdla - reportOfflineNodeStatus(); - } - else reportOnlineNodeStatus(); - - } - - rotary_switch_state = flowdata.data.value; - } - else if(cmd == "lux_sensor") - { - lux_sensor = parseInt(flowdata.data.value); - - // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? - if(lux_sensor < 100) - { - - // we send lux_sensor value to all nodes: - let params = getParams(PRIORITY_TYPES.node_broadcast); - - params.recipient = 2;//2 broadcast, address = 0 - params.address = 0xffffffff;//Broadcast - - let ba = longToByteArray(lux_sensor); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - params.timestamp = PRIORITY_TYPES.node_broadcast; - params.info = "run broadcast: Actual Lux level from cabinet"; - params.register = 95;//Actual Lux level from cabinet - params.rw = 1;//write - - tasks.push(params); - - //process profiles - turnOnOffLinesAccordingToLuxSensor(lux_sensor); - } - } - else if(cmd == "state_of_breaker") - { - //istic linie - let value = flowdata.data.value; - let line = parseInt(flowdata.data.line); - - let dataChanged = false; - if(state_of_breaker[line] != value) dataChanged = true; - - state_of_breaker[line] = value; - - let status = "OK"; - if(value == "Off") status = "NOK"; - - if(dataChanged) { - - if(relaysData.hasOwnProperty(line)) - { - let tbname = relaysData[line].tbname; - - if(value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); - else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker"); - - //report status liniu - let values = { - "status": status - }; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(SEND_TO.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - - //current value - if(value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii - else reportOnlineNodeStatus(line); - } - - } - } - else{ - logger.debug("undefined cmd", cmd); - } - } - } - - return; - } - - //data from worksys - if(flowdata.data.hasOwnProperty("topic")) - { - - let data = flowdata.data.content.data; - - let command = data.params.command; - let method = data.method; - let profile = data.params.payload; - if(profile == undefined) profile = ""; - let entity = data.params.entities[0]; - let entity_type = entity.entity_type; - let tbname = entity.tb_name; - - instance.send(SEND_TO.debug, flowdata.data); - logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); - logger.debug("----------------------------"); - - if(entity_type == "street_luminaire"|| entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") - { - if(method == "set_command") - { - - //let command = data.params.command; - let value = data.params.payload.value; - - if(command == "dimming") - { - - let nodeWasFound = false; - let keys = Object.keys(nodesData); - - //logger.debug("-----", keys); - - for(let i = 0; i < keys.length; i++) - { - let node = keys[i]; - //logger.debug( node, nodesData[node], tbname); - - if(tbname == nodesData[node].tbname.trim()) - { - let params = getParams(PRIORITY_TYPES.high_priority); - - value = parseInt(value); - if(value > 0) value = value + 128; - - //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.byte4 = value; - params.rw = 1;//write - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'set dimming from platform'; - //params.debug = true; - - //ak linia je - - //debug(params); - logger.debug("dimming", params); - - tasks.push(params); - - setTimeout(function(){ - - //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority - //a pridame aj vyreportovanie dimmingu - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read dimming (after set dimming from platform)'; - params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - vykon - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 76; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Power (after set dimming from platform)'; - params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - prud svietidla - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 75; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Current (after set dimming from platform)'; - params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - power faktor - ucinnik - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 77; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read power factor - Cos phi (after set dimming from platform)'; - params.debug = true; - - tasks.push(params); - } - - },4000); - - - nodeWasFound = true; - - break; - } - } - - if(!nodeWasFound) - { - logger.debug("set dimming from platform", "unable to find tbname", tbname); - } - } - else - { - instance.send(SEND_TO.debug, "undefined command " + command); - logger.debug("undefined command", command); - } - - return; - } - else if(method == "set_profile") - { - //nastav profil nodu - logger.debug("-->set_profile for node", data.params); - logger.debug("------profile data", profile); - //instance.send(SEND_TO.debug, "set_profile" + command); - - let keys = Object.keys(nodesData); - for(let i = 0; i < keys.length; i++) - { - let node = keys[i]; - if(tbname == nodesData[node].tbname.trim()) - { - - if(profile != "") profile = JSON.stringify(profile); - dbNodes.modify({ processed: false, profile: profile }).where("node", node).make(function(builder) { - - builder.callback(function(err, response) { - - logger.debug("worksys - update node profile done", profile); - if(profile === "") logger.debug("worksys - update node profile done - profile is empty"); - - //profil úspešne prijatý pre node č. xx - sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, SEND_TO.tb, instance ); - - nodesData[node].processed = false; - nodesData[node].profile = profile; - - let line = nodesData[node].line; - processNodeProfile(node); - - }); - }); - } - } - } - else - { - - instance.send(SEND_TO.debug, "unknown method " + method); - logger.debug("unknown method", method); - - return; - } - } - - //nastav profil linie z platformy - else if(entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") - { - //profil linie - //relays.table line:number|tbname:string|contactor:number|profile:string - //najdeme line relaysData - - if(method == "set_profile") - { - - logger.debug("-->set_profile for line", data.params); - logger.debug("profile data:", profile); - - let keys = Object.keys(relaysData); - for(let i = 0; i < keys.length; i++) - { - let line = keys[i]; - if(tbname == relaysData[line].tbname) - { - //zmazeme tasky - removeTask({type: "relay", line: line}); - - if(profile != "") profile = JSON.stringify(profile); - dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) { - - builder.callback(function(err, response) { - - //update profile - logger.debug("worksys - update relay profile done:", profile); - instance.send(SEND_TO.debug, "worksys - update relay profile done"); - - loadRelaysData(line).then(function (data) { - logger.debug("loadRelaysData DONE for line", line); - buildTasks({processLineProfiles: true, line: line}); - }); - - sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", {line: line}, profile, SEND_TO.tb, instance ); - - }); - }); - break; - } - } - } - else if(method == "set_command") - { - let value = data.params.payload.value; - - if(command === "switch") - { - - // if we receive rpc from platform, to switch maintenance mode, we set OMS_maintenance_mode flow variable to value; - if(entity_type === "edb" || entity_type === "edb_ver4_se") FLOW.variables.OMS_maintenance_mode = value; - - let responseRelays = await promisifyBuilder(dbRelays.find().where("tbname", tbname)); - - let line = 0; - if(responseRelays.length == 1) line = responseRelays[0].line; - - if(value == false) turnOffLine(line, "command received form platform"); - else turnOnLine(line, "command received form platform"); - } - } - else - { - instance.send(SEND_TO.debug, "undefined method " + method); - logger.debug("undefined method", method); - } - - return; - } - else - { - instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); - logger.debug("UNKNOW entity_type", entity_type); - } - return; - } - - //terminal - if(!rsPort.isOpen) await rsPort.open(); - - let params = flowdata.data.body; - if(params == undefined) - { - //logger.debug("CMD manager flowdata.data.body is undefined"); - return; - } - - params.priority = PRIORITY_TYPES.terminal; - params.type = "cmd-terminal"; - params.tbname = ""; - params.timestamp = PRIORITY_TYPES.terminal; - params.addMinutesToTimestamp = 0;// do not repeat task!!! - params.debug = true; - - let timestamp = Date.now(); - params.refFlowdataKey = timestamp; - //params.refFlowdata = flowdata; - //refFlowdata = flowdata; - - //console.log("flowdata", flowdata); - - cleanUpRefFlowdataObj(); - - refFlowdataObj[ timestamp ] = flowdata; - - //fix - //params.address = params.adress; - logger.debug("received from terminal", params); - logger.debug("date/time:", new Date()); - logger.debug("tasks length:", tasks.length); - - //tasks = []; - - //add to tasks - tasks.push(params); - - } - } - }) - -} // end of instance.export - - -/** - * setCorrectTime function runs once per hour - * If it is 3 o'clock, it sets actual time, which is got from services - * https://service-prod01.worksys.io/gettime - * If also detects Read Only Filesystem once a day - */ -function setCorrectPlcTimeOnceADay() -{ - - const currentTime = new Date(); - if(currentTime.getHours() != 3) return; - - RESTBuilder.make(function(builder) { - - if(!builder) return; - - builder.method('GET'); - builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); - - builder.callback(function(err, response, output) { - - if (err) { - console.log(err); - return; - } - - const res = output.response; - - try { - - const obj = JSON.parse(res); - let d = new Date(obj.date); - - const now = new Date(); - - let diffInMinutes = now.getTimezoneOffset(); - console.log("---->TimezoneOffset", diffInMinutes); - - if(d instanceof Date) - { - - // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); - - let year = d.getFullYear(); - let month = addZeroBefore(d.getMonth() + 1); - let day = addZeroBefore(d.getDate()); - - let hours = addZeroBefore(d.getHours()); - let minutes = addZeroBefore(d.getMinutes() ); - let seconds = addZeroBefore(d.getSeconds()); - - let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - - exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); - console.log(dateStr); - - monitor.info("failed timedatectl set-time", err, stderr); - } - else - { - monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); - } - - }); - } - - } catch (error) { - logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); - monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); - } - - // we detect readOnlyFileSystem once an hour as well - detectReadOnlyFilesystem(); - - }); - }); - -} - - -function detectReadOnlyFilesystem() -{ - exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); - - } else { - //console.log("Read-only", stdout); - - let lines = stdout + ""; - lines = lines.split("\n"); - - let readOnlyDetected = ""; - for(let i = 0; i < lines.length; i++) - { - if(lines[i].startsWith("/dev/mmcblk0p2")) - { - readOnlyDetected = lines[i]; - } - } - - if(readOnlyDetected !== "") - { - errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); - monitor.info("Read only filesystem detected"); - } - - } - }); -} - -let setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour -setCorrectPlcTimeOnceADay(); - - - - - - - - - -///helper functions - -function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) -{ - - if(date === undefined) date = new Date(); - //if(duskOffset === undefined) duskOffset = 0; - //if(dawnOffset === undefined) dawnOffset = 0; - - //let line = keys[i]; - let profilestr = ""; - if(relaysData[line] != undefined) profilestr = relaysData[line].profile; - - let result = {}; - - var times = SunCalc.getTimes(date, latitude, longitude); - let dawn = new Date(times.sunrise);//usvit - let dusk = new Date(times.sunset);//sumrak - - - //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 - //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 - - - let dusk_astro_clock_offset = duskOffset;//minutes - let dawn_astro_clock_offset = dawnOffset;//minutes - - try { - - let profile = JSON.parse(profilestr); - if(Object.keys(profile).length === 0) throw ("profile is not defined"); - - //Jednoduchý režim - if(profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) - { - - } - - //Režim astrohodín - if(profile.astro_clock == true) - { - //if(profile.dusk_lux_sensor == false) - { - if(profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt( profile.dusk_astro_clock_offset ); - } - - //if(profile.dawn_lux_sensor == false) - { - if(profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt( profile.dawn_astro_clock_offset ); - } - - } - - //dusk - súmrak - //down, sunrise - svitanie - - } catch (error) { - if(profilestr != "") - { - logger.debug(profilestr); - logger.debug(error); - } - } - - result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); - - dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset*60000); - dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset*60000); - - result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dusk_hours = dusk.getHours(); - result.dusk_minutes = dusk.getMinutes(); - - result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); - result.dawn_hours = dawn.getHours(); - result.dawn_minutes = dawn.getMinutes(); - - result.dusk_time = dusk.getTime(); - result.dawn_time = dawn.getTime(); - - result.dusk_astro_clock_offset = dusk_astro_clock_offset; - result.dawn_astro_clock_offset = dawn_astro_clock_offset; - - return result; -} - - -function processResponse(register, bytes) -{ - - let values = {}; - - let byte3 = bytes[0]; - let byte2 = bytes[1]; - let byte1 = bytes[2]; - let byte0 = bytes[3]; - - //status - if(register == 0) - { - let statecode = bytesToInt(bytes); - values = {"statecode": statecode}; - return values; - } - - //Dimming, CCT - if(register == 1) - { - let brightness = 0; - let dimming = byte0; - if(dimming > 128) { - //dimming = -128; - brightness = dimming - 128; - } - - //cct - //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 - let cct; - if(byte3 == 1) cct = byte2*256 + byte1; - else cct = bytesToInt(bytes.slice(0, 3)); - - //cct podla auditu - - values["dimming"] = brightness; - return values; - } - - // - if(register == 4) - { - values["master_node_version"] = bytes[1] + "." + bytes[2]; - //logger.debug("FW Version", register, bytes); - } - - //Napätie - if(register == 74) - { - let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); - values["voltage"] = Number(voltage); - } - - //Prúd - if(register == 75) - { - let current = bytesToInt(bytes); - values["current"] = current; - } - - //výkon - if(register == 76) - { - let power = (bytesToInt(bytes) * 0.1).toFixed(2); - values["power"] = Number(power); - } - - //účinník - if(register == 77) - { - let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); - values["power_factor"] = Number(power_factor); - } - - //frekvencia - if(register == 78) - { - let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); - values["frequency"] = Number(frequency); - } - - //energia - if(register == 79) - { - let energy = bytesToInt(bytes); - - //Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal. - - values["energy"] = energy / 1000; - } - - //doba života - if(register == 80) - { - let lifetime = ( bytesToInt(bytes) / 60).toFixed(2); - values["lifetime"] = Number(lifetime); - } - - //nastavenie profilu - if(register == 8) - { - let time_schedule_settings = bytesToInt(bytes); - values["time_schedule_settings"] = time_schedule_settings; - } - - //skupinová adresa 1 - if(register == 3) - { - let gr_add_1 = bytesToInt(byte0); - values["gr_add_1"] = gr_add_1; - - let gr_add_2 = bytesToInt(byte1); - values["gr_add_2"] = gr_add_2; - - let gr_add_3 = bytesToInt(byte2); - values["gr_add_3"] = gr_add_3; - - let gr_add_4 = bytesToInt(byte3); - values["gr_add_4"] = gr_add_4; - } - - //naklon - if(register == 84) - { - let temp; - if(byte3 >= 128) - { - temp = (byte3 - 128) * (-1); - } - else - { - temp = byte3; - } - - let inclination_x; - if(byte2 >= 128) - { - inclination_x = (byte2 - 128) * (-1); - } - else - { - inclination_x = byte2; - } - - let inclination_y; - if(byte1 >= 128) - { - inclination_y = (byte1 - 128) * (-1); - } - else - { - inclination_y = byte1; - } - - let inclination_z; - if(byte0 >= 128) - { - inclination_z = (byte0 - 128) * (-1); - } - else - { - inclination_z = byte0; - } - - values["temperature"] = temp; - - //náklon x - values["inclination_x"] = inclination_x; - - //náklon y - values["inclination_y"] = inclination_y; - - //náklon z - values["inclination_z"] = inclination_z; - } - - let h = byte3; - let m = byte2; - let s = byte1; - - let timestamp; - - if(register == 87 || register == 6 || register == 7 ) - { - //if(byte3 < 10) h = "0" + byte3; - //if(byte2 < 10) m = "0" + byte2; - //if(byte1 < 10) s = "0" + byte1; - - var d = new Date(); - d.setHours(h); - d.setMinutes(m); - d.setSeconds(s); - - timestamp = d.getTime(); - } - - //aktuálny čas - if(register == 87) - { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["actual_time"] = h + ":" + m + ":" + s; - - values["actual_time"] = timestamp; - } - - //čas súmraku - if(register == 6) - { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["dusk_time"] = h + ":" + m + ":" + s; - - values["dusk_time"] = timestamp; - } - - //čas úsvitu - if(register == 7) - { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["dawn_time"] = h + ":" + m + ":" + s; - - values["dawn_time"] = timestamp; - } - - //FW verzia - if(register == 89) - { - //formát: "Byte3: Byte2.Byte1 (Byte0)" - values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; - } - - return values; -} - - -//byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB -function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { - let resp = []; - - let cmd = register; - - if (typeof adresa === 'string') adresa = parseInt(adresa); - if (typeof byte1 === 'string') byte1 = parseInt(byte1); - if (typeof byte2 === 'string') byte2 = parseInt(byte2); - if (typeof byte3 === 'string') byte3 = parseInt(byte3); - if (typeof byte4 === 'string') byte4 = parseInt(byte4); - - if (rw === 0) - { - cmd = cmd + 0x8000; - } - - //master - if(rec === 0) adresa = 0; - - if(rec === 2) - { - adresa = 0xffffffff;//Broadcast - } - - //recipient - if (rec === 3) - { - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push( adresa & 0xFF );//band - } - else - { - resp.push( (adresa >> 24) & 0xFF);//rshift - resp.push( (adresa >> 16) & 0xFF); - resp.push( (adresa >> 8) & 0xFF); - resp.push( adresa & 0xFF ); - - if (rec === 2) - { - resp.push(0xFF); - } - else resp.push(0); - } - - resp.push( (cmd >> 8) & 0xFF);//rshift - resp.push( cmd & 0xFF );//band - resp.push( byte1 & 0xFF );//band - resp.push( byte2 & 0xFF );//band - resp.push( byte3 & 0xFF );//band - resp.push( byte4 & 0xFF );//band - - //let data = '12345'; - let crc = crc16('ARC', resp); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; - - resp.push(c1); - resp.push(c2); - - //logger.debug("checksum", crc); - //logger.debug("resp", resp); - - return resp; - -} - - - - - - -// SAMPLE DATA - -const relaysDataExample = -{ - '0': { - line: 0, - tbname: 'jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV', - contactor: 1, - profile: '' - }, - '1': { - line: 1, - tbname: 'MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O', - contactor: 1, - profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' - }, - '2': { - line: 2, - tbname: 'jBL12pg63eX4N9P7zy0lJLyEJKmlbkGwZMx0avQV', - contactor: 1, - profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' - }, - '3': { - line: 3, - tbname: 'aAOzENGrvpbe0VoK7D6E1a819PZmdg3nl24JLQMk', - contactor: 1, - profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}' - } -} - - -const rpcSwitchOffLine = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 8, - "method": "set_command", - "params": { - "entities": [ - { - "entity_type": "edb_line", - "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" - } - ], - "command": "switch", - "payload": { - "value": false - } - } - } - } -} - -const rpcSetNodeDimming = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 10, - "method": "set_command", - "params": { - "entities": [ - { - "entity_type": "street_luminaire", - "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" - } - ], - "command": "dimming", - "payload": { - "value": 5 - } - } - } - } -} - -const rpcLineProfile = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 9, - "method": "set_profile", - "params": { - "entities": [ - { - "entity_type": "edb_line", - "tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O" - } - ], - "payload": { - "intervals": [ - { - "value": 0, - "end_time": "20:00", - "start_time": "13:00" - }, - { - "value": 1, - "end_time": "05:30", - "start_time": "20:00" - }, - { - "value": 0, - "end_time": "13:00", - "start_time": "05:30" - } - ], - "astro_clock": true, - "dawn_lux_sensor": false, - "dusk_lux_sensor": false, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 0, - "dusk_astro_clock_offset": 0, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - } - } - } - } -} - - -const rpcNodeProfile = -{ - "topic": "v1/gateway/rpc", - "content": { - "device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV", - "data": { - "id": 11, - "method": "set_profile", - "params": { - "entities": [ - { - "entity_type": "street_luminaire", - "tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV" - } - ], - "payload": { - "intervals": [ - { - "cct": 3000, - "value": 0, - "end_time": "17:50", - "start_time": "13:00" - }, - { - "cct": 3000, - "value": 100, - "end_time": "21:30", - "start_time": "17:50" - }, - { - "cct": 3000, - "value": 0, - "end_time": "13:00", - "start_time": "07:10" - }, - { - "cct": 3000, - "value": 50, - "end_time": "00:00", - "start_time": "21:30" - }, - { - "cct": 3000, - "value": 10, - "end_time": "04:30", - "start_time": "00:00" - }, - { - "cct": 3000, - "value": 100, - "end_time": "07:10", - "start_time": "04:30" - } - ], - "astro_clock": true, - "dawn_lux_sensor": false, - "dusk_lux_sensor": false, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 30, - "dusk_astro_clock_offset": 20, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - } - } - } - } -} - - const sunCalcExample = { - dusk_no_offset: '20:18', - dawn_no_offset: '05:19', - dusk: '20:18', - dusk_hours: 20, - dusk_minutes: 18, - dawn: '05:19', - dawn_hours: 5, - dawn_minutes: 19, - dusk_time: 1715278688962, - dawn_time: 1715224744357, - dusk_astro_clock_offset: 0, - dawn_astro_clock_offset: 0 -} diff --git a/flow/db_init.js b/flow/db_init.js index 5fb447e..cf3b5a7 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -40,7 +40,6 @@ exports.install = async function(instance) { const dbNodes = TABLE("nodes"); const dbRelays = TABLE("relays"); const dbSettings = TABLE("settings"); - const dbStatus = TABLE("status"); const dbPins = TABLE("pins"); const dbNotifications = TABLE("notifications"); @@ -50,14 +49,12 @@ exports.install = async function(instance) { const responseSettings = await promisifyBuilder(dbSettings.find()); const responseNodes = await promisifyBuilder(dbNodes.find()); const responsePins = await promisifyBuilder(dbPins.find()); - const responseStatus = await promisifyBuilder(dbStatus.find()); const responseRelays = await promisifyBuilder(dbRelays.find()); const response = await promisifyBuilder(dbNotifications.find()); dbs.pinsData = makeMapFromDbResult(responsePins, "pin"); dbs.relaysData = makeMapFromDbResult(responseRelays, "line"); dbs.nodesData = makeMapFromDbResult(responseNodes, "node"); - dbs.statusData = responseStatus[0]; dbs.notificationsData = makeMapFromDbResult(response, "key"); //+|354|nodesdata.....+|482|nodesdata.... diff --git a/flow/designer.json b/flow/designer.json index 09abdc3..455b37a 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -140,8 +140,8 @@ "component": "debug", "tab": "1612772287426", "name": "to TB", - "x": 323.75, - "y": 429, + "x": 312.75, + "y": 426, "connections": {}, "disabledio": { "input": [ @@ -270,9 +270,7 @@ "y": 557.3500061035156, "connections": {}, "disabledio": { - "input": [ - 0 - ], + "input": [], "output": [] }, "state": { @@ -291,14 +289,12 @@ "id": "1615809128443", "component": "debug", "tab": "1611921777196", - "name": "Debug", + "name": "tempToTb", "x": 598.8833312988281, "y": 654.3500061035156, "connections": {}, "disabledio": { - "input": [ - 0 - ], + "input": [], "output": [] }, "state": { @@ -1142,7 +1138,7 @@ "output": [] }, "state": { - "text": "847.42 MB / 985.68 MB", + "text": "846.37 MB / 985.68 MB", "color": "gray" }, "options": { @@ -1172,7 +1168,7 @@ "output": [] }, "state": { - "text": "5.79 GB / 7.26 GB", + "text": "5.77 GB / 7.26 GB", "color": "gray" }, "options": { @@ -1628,7 +1624,7 @@ "output": [] }, "state": { - "text": "1.3% / 74.34 MB", + "text": "6.2% / 70.96 MB", "color": "gray" }, "options": { @@ -1960,6 +1956,10 @@ { "index": "0", "id": "1621340721628" + }, + { + "index": "0", + "id": "1732889185927" } ] }, @@ -2141,9 +2141,9 @@ "options": { "slack_channel": "C071KN2Q8SK", "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", - "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\"]", + "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "rvo_senica_1_10.0.0.141" + "name": "rvo_senica_46_10.0.0.133" }, "color": "#30E193", "notes": "" @@ -2342,7 +2342,7 @@ "clientid": "", "port": "2764", "host": "192.168.252.2", - "topic": "u118" + "topic": "u133" }, "color": "#888600", "notes": "" @@ -2780,7 +2780,137 @@ "options": {}, "color": "#F6BB42", "notes": "" + }, + { + "id": "1732700042559", + "component": "nodesdb_change_check", + "tab": "1612772287426", + "name": "Nodes DB change check", + "x": 250.88333129882812, + "y": 1813.2333984375, + "connections": { + "0": [ + { + "index": "0", + "id": "1732700071298" + }, + { + "index": "0", + "id": "1732700642917" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#888600", + "notes": "" + }, + { + "id": "1732700057052", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 71.75, + "y": 1814, + "connections": { + "0": [ + { + "index": "0", + "id": "1732700042559" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1732700071298", + "component": "debug", + "tab": "1612772287426", + "name": "nodesChange", + "x": 548.8833312988281, + "y": 1875.2333984375, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "options": { + "type": "data", + "repository": false, + "enabled": true + }, + "color": "#967ADC", + "notes": "" + }, + { + "id": "1732700642917", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "tb-push", + "x": 544.8833312988281, + "y": 1769, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "options": { + "wirename": "tb-push" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1732889185927", + "component": "debug", + "tab": "1611921777196", + "name": "tempToDido", + "x": 594.8833312988281, + "y": 745, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "options": { + "type": "data", + "repository": false, + "enabled": true + }, + "color": "#967ADC", + "notes": "" } ], "version": 615 -} +} \ No newline at end of file diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 11e7ae3..86fb6a5 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -56,7 +56,6 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme */ -const dbStatus = TABLE("status"); const { errLogger, logger, monitor } = require('./helper/logger'); const SerialPort = require('serialport'); const WebSocket = require('ws'); @@ -90,9 +89,6 @@ const SEND_TO = { cmd_manager: 2 } -const TIME_AFTER_TEMPERATURE_NOK_STATUS = 3600; //seconds -const DIFFERENCE_TO_SEND_TEMPERATURE = 0.31; - exports.install = function(instance) { @@ -108,8 +104,7 @@ exports.install = function(instance) { //process.exit(1); }) - // temperature value is initialized to -1000. It can be literally anything, we just needs to be able to enter if block in ws.onmessage function, when first temperatere data comes - let previousValues = {temperature: {value: -1000, lastTimeTemperatureReceived: Date.now() / 1000}}; + let previousValues = {}; let rsPortReceivedData = []; //to be able to get proper twilight values, when @@ -163,15 +158,12 @@ exports.install = function(instance) { rvoTbName = SETTINGS.rvoTbName; pinsData = GLOBALS.pinsData; relaysData = GLOBALS.relaysData; - statusData = GLOBALS.statusData; tbHandler = new DataToTbHandler(SEND_TO.tb) tbHandler.setSender(exports.title); controller_type = SETTINGS.controller_type //"lm" or "unipi" //logicMachine if(controller_type == "") controller_type = "lm"; - - deviceStatus["temperature"] = statusData.thermometer; console.log(exports.title, "controller type: ", controller_type); @@ -379,39 +371,19 @@ exports.install = function(instance) { data = JSON.parse(data.data); // data comes in array except of "temperature" ==> it comes as an object - if(isObject(data)) - { - let value = data['value']; - const values = {}; - previousValues["temperature"]["lastTimeTemperatureReceived"] = data['time']; - - // we received data from thermometer, but thermometer status is NOK: - if(deviceStatus["temperature"] === "NOK") - { - await writeThermometerStatusToDb("OK"); - sendRvoStatus(); - } - - // temperature value comes very often. To handle it, we check if it change for more than 0.3 degrees, if yes, we send to TB - if(Math.abs(previousValues["temperature"]["value"] - value) > DIFFERENCE_TO_SEND_TEMPERATURE) - { - previousValues["temperature"]["value"] = value; - values['temperature'] = value; - sendTelemetry(values, rvoTbName); - } - return; - } + // we do not handle temperature from evok any more => we return, if temperature comes: + if(isObject(data)) return; data.map(item => { - + let value = item['value']; let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01" - if(pin == undefined) return; + if(pin == undefined) return; switchLogic(pin, value); }) } - + ws.on('error', (err) => { monitor.info('websocket error, reconnect') @@ -464,19 +436,19 @@ exports.install = function(instance) { { let value = 0; if(onOrOff == "on") value = 1; - + if(value == 1 && SETTINGS.maintenance_mode) return; alarmStatus = "OFF"; if(value == 1) alarmStatus = "ON"; - + if(rsPort) { let arr = [0x55]; arr.push(13); arr.push(0); arr.push(value); - + rsPort.write(Buffer.from(arr), function(err) { logger.debug(`sirena - ${onOrOff}`); }); @@ -605,7 +577,7 @@ exports.install = function(instance) { //data from modbus_reader or temperature sensor or twilight sensor or other modbus device instance.on("0", flowdata => { - if(!flowdata.data instanceof Object) return; + if(!isObject(flowdata.data)) return; // console.log('***********************', flowdata.data) instance.send(SEND_TO.debug, flowdata.data); @@ -622,10 +594,8 @@ exports.install = function(instance) { { deviceStatus["em"] = "NOK"; } - //"NOK-thermometer" comes just from LM. Unipi handles thermometer from ws evok. else if(status == "NOK-thermometer") { - previousValues["temperature"]["lastTimeTemperatureReceived"] = null; deviceStatus["temperature"] = "NOK"; } } @@ -637,10 +607,8 @@ exports.install = function(instance) { instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]}); deviceStatus["twilight_sensor"] = "OK" } - //"temperature" comes just from LM. Unipi handles thermometer from ws evok. else if(values.hasOwnProperty("temperature")) { - previousValues["temperature"]["lastTimeTemperatureReceived"] = Date.now() / 1000; //time in seconds deviceStatus["temperature"] = "OK"; } // EM @@ -669,8 +637,8 @@ exports.install = function(instance) { let force = obj.force; let info = obj.info; - if(obj.command == "turnOn") turnLine("on", line, undefined, force, info); - else if(obj.command == "turnOff") turnLine("off", line, undefined, force, info); + if(obj.command == "on") turnLine("on", line, undefined, force, info); + else if(obj.command == "off") turnLine("off", line, undefined, force, info); else if(obj.command == "turnOnAlarm") turnAlarm("on"); else if(obj.command == "turnOffAlarm") turnAlarm("off"); @@ -822,15 +790,13 @@ exports.install = function(instance) { let byte = bytesToInt([byte1, byte0]); //console.log("calculateStateCode -------------------", byte); - + return byte; } async function sendRvoStatus() { - // test if dbLoaded is ok to check - //if(!FLOW.dbLoaded) return; if(SETTINGS === undefined) return; const table = { @@ -849,21 +815,11 @@ exports.install = function(instance) { }; for (const phase of SETTINGS.no_voltage) dataToTb[`phase_${phase}_status`] = 0; - - //thermometer did not send data for more than a hour. Time in seconds - if(deviceStatus["temperature"] === "OK") - { - if(previousValues["temperature"]["lastTimeTemperatureReceived"] + TIME_AFTER_TEMPERATURE_NOK_STATUS < Date.now() / 1000) - { - // to be able to calculate proper RVO status, we need to await writeThermometerStatusToDb function - await writeThermometerStatusToDb("NOK"); - dataToTb["thermometer_status"] = 0; - } - } dataToTb["status"] = checkRvoStatus(); dataToTb["statecode"] = calculateStateCode(); + //console.log(dataToTb); sendTelemetry(dataToTb, rvoTbName); } @@ -876,8 +832,7 @@ exports.install = function(instance) { let status = "OK"; for (const [key, value] of Object.entries(deviceStatus)) { - //if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; - if(["em", "twilight_sensor"].includes(key) && value == "NOK") status = "NOK"; + if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; } if(status == "OK") @@ -899,6 +854,7 @@ exports.install = function(instance) { if(!SETTINGS.masterNodeIsResponding) status = "NOK"; if(SETTINGS.no_voltage.size > 0) status = "NOK"; + // console.log("rvo status",status) return status; } @@ -1258,7 +1214,6 @@ exports.install = function(instance) { } if(newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue; - if(rvoTbName == tbname) sendRvoStatus(); if(Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname); } @@ -1277,24 +1232,6 @@ exports.install = function(instance) { } - function writeThermometerStatusToDb(status) { - return new Promise(function(resolve, reject) { - - dbStatus.modify({ thermometer: status }).make(function(builder) { - builder.callback(function(err, response) { - if(!err) - { - deviceStatus["temperature"] = status; - console.log(`Wrote to db status: thermometer ${status}`); - resolve("ok") - } - reject("nok") - }); - }); - }) - } - - function isObject (item) { return (typeof item === "object" && !Array.isArray(item) && item !== null); } diff --git a/flow/nodesdb_changecheck.js b/flow/nodesdb_changecheck.js new file mode 100644 index 0000000..be1e700 --- /dev/null +++ b/flow/nodesdb_changecheck.js @@ -0,0 +1,70 @@ +exports.id = 'nodesdb_change_check'; +exports.title = 'Nodes DB change check'; +exports.group = 'Worksys'; +exports.color = '#888600'; +exports.version = '1.0.2'; +exports.icon = 'sign-out'; +exports.input = 1; +exports.output = 1; +exports.readme = `Check, if nodes.table db changed compared to original database`; + +const fs = require('fs'); +const path = require('path'); +const { sendNotification } = require('./helper/notification_reporter'); +const nodesOriginalFile = path.join(__dirname, '../databases/nodes_original/', 'nodes_original.table'); + + +exports.install = function(instance) { + + function compareArrays(array1, array2) { + let message = ""; + let areEqual = true; + let zmenene = [] + + if (array1.length !== array2.length) { + message += "Nezhoda v pocte nodov. " + } + + const set1 = new Set(array1.map(obj => JSON.stringify(obj))); + const set2 = new Set(array2.map(obj => JSON.stringify(obj))); + + for (const objStr of set1) { + + if (!set2.has(objStr)) { + zmenene.push(objStr) + areEqual = false; + } else { + set2.delete(objStr); + } + } + + if(!areEqual) { + message += `Aktualne nody: ${zmenene.toString()}. Zmenene proti originalu: ${Array.from(set2).join(' ')}`; + sendNotification("Nodesdb_changecheck", FLOW.GLOBALS.settings.rvoTbName, "nodes_db_changed", "", message, 0, instance); + } + else console.log("Arrays are equal."); + + console.log(message) + } + + + instance.on("data", _ => { + + let nodesData = FLOW.GLOBALS.nodesData; + + // we check if nodes.table has changed compared to nodes_original.table (we have array of nodes e.g. [{node:255, tbname: "agruhuwhgursuhgo34hgsdiguhrr"}] + const nodes_actual = Object.keys(nodesData).map(node => ({[node]: nodesData[node].tbname})) + let nodes_original = fs.readFileSync(nodesOriginalFile, { encoding: 'utf8', flag: 'r' }); + + try { + nodes_original = JSON.parse(nodes_original); + } catch(e) { + console.log(e) + } + + + setTimeout(() => compareArrays(nodes_actual, nodes_original),10000); + }) + +} + diff --git a/flow/thermometer.js b/flow/thermometer.js index b3bb350..0be7eff 100644 --- a/flow/thermometer.js +++ b/flow/thermometer.js @@ -1,122 +1,98 @@ -exports.id = 'thermometer'; -exports.title = 'Thermometer'; -exports.group = 'Worksys'; -exports.color = '#5CB36D'; -exports.input = 1; -exports.version = '1.0.3'; -exports.output = ["red", "white", "blue"]; -exports.author = 'Rastislav Kovac'; -exports.icon = 'thermometer-three-quarters'; - -exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`; - -const { errLogger, logger, monitor } = require('./helper/logger'); - -const SEND_TO = { - debug: 0, - tb: 1, - dido_controller: 2 -} - -//read temperature - frequency -let timeoutMin = 5;//minutes - - -exports.install = function(instance) { - - const { exec } = require('child_process'); - const { sendNotification } = require('./helper/notification_reporter'); - - let startRead; - let counter = 0; - let rvoTbName = ""; - let temperatureAddress = ""; - - logger.debug(exports.title, "installed"); - - instance.on("close", function(){ - clearInterval(startRead); - }) - - - const main = function() { - - try { - - if(FLOW.GLOBALS.settings.controller_type === "unipi") - { - clearInterval(startRead); - return; - } - - if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined"; - - exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => { - - if(!error) - { - parseData(stdout) - return; - } - - sendNotification("main", rvoTbName, "thermometer_is_not_responding", {}, {"Error": error}, SEND_TO.tb, instance, "thermometer"); - monitor.info("Thermometer is not responding", error); - instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"}); - }); - - } - catch(err) { - errLogger.error(exports.title, err); - clearInterval(startRead); - } - } - - const parseData = function(data) { - - data = parseFloat(data); - - logger.debug("gettemperature", data); - - if(!isNaN(data)) { - - if(counter > 290) - { - instance.send(SEND_TO.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); - sendNotification("parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer"); - } - - logger.debug("gettemperature", data); - - const values = { - "temperature": Number(data.toFixed(2)), - } - - instance.send(SEND_TO.dido_controller, {values: values}); - counter = 0; - - } else { - - counter++; - monitor.info("gettemperature err", counter, data); - - //ked je problem 1 den - let day = 24 * 60 / timeoutMin; - if ( counter > day && counter < day + 2 ) { - //sendNotification("parseData", rvoTbName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", SEND_TO.tb, instance, "thermometer"); - sendNotification("parseData", rvoTbName, "thermometer_sends_invalid_data", {}, "", SEND_TO.tb, instance, "thermometer"); - - instance.send(SEND_TO.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); - instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"}); - } - - } - - } - - instance.on("data", _ => { - temperatureAddress = FLOW.GLOBALS.settings.temperature_address; - rvoTbName = FLOW.GLOBALS.settings.rvoTbName; - startRead = setInterval(main, timeoutMin * 1000 * 60); - main(); - }) -}; \ No newline at end of file +exports.id = 'thermometer'; +exports.title = 'Thermometer'; +exports.group = 'Worksys'; +exports.color = '#5CB36D'; +exports.input = 1; +exports.version = '1.0.3'; +exports.output = ["red", "white", "blue"]; +exports.icon = 'thermometer-three-quarters'; + +exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`; + +const { errLogger, logger, monitor } = require('./helper/logger'); + +const SEND_TO = { + debug: 0, + tb: 1, + dido_controller: 2 +} + +//read temperature - frequency +let timeoutMin = 5;//minutes +let NUMBER_OF_FAILURES_TO_SEND_ERROR = 13; + +exports.install = function(instance) { + + const { exec } = require('child_process'); + const { sendNotification } = require('./helper/notification_reporter'); + + let startRead; + let counter = 0; + let rvoTbName = ""; + let temperatureAddress = ""; + + logger.debug(exports.title, "installed"); + + instance.on("close", function(){ + clearInterval(startRead); + }) + + + const main = function() { + + try { + + if(temperatureAddress === "") throw "Thermometer: temperatureAddress is not defined"; + + exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => { + + if(!error) + { + parseData(stdout) + return; + } + + counter++; + if(counter == NUMBER_OF_FAILURES_TO_SEND_ERROR) sendNotification("Thermometer_main", rvoTbName, "thermometer_is_not_responding", {}, {"Error": error}, SEND_TO.tb, instance, "thermometer"); + monitor.info("Thermometer is not responding", error); + instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"}); + }); + + } + catch(err) { + errLogger.error(exports.title, err); + clearInterval(startRead); + } + } + + const parseData = function(data) { + + data = parseFloat(data); + logger.debug("Thermometer", data); + + if(isNaN(data)) { + errLogger.error("Thermometer sends invalid data"); + return; + } + + if(counter > NUMBER_OF_FAILURES_TO_SEND_ERROR) //1 hour + { + instance.send(SEND_TO.debug, "Thermometer - temperature data are comming again"); + sendNotification("Thermometer_parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer"); + } + + const values = { + "temperature": Number(data.toFixed(2)), + } + + instance.send(SEND_TO.dido_controller, {values: values}); + counter = 0; + } + + instance.on("data", _ => { + temperatureAddress = FLOW.GLOBALS.settings.temperature_address; + rvoTbName = FLOW.GLOBALS.settings.rvoTbName; + startRead = setInterval(main, timeoutMin * 1000 * 60); + setTimeout(main, 20000); + }) +}; From 31b5dbba5c5f2b1e07f961bbc63d433c693132be Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 2 Dec 2024 17:25:56 +0100 Subject: [PATCH 08/25] add d to temperature_address in settings --- databases/settings.table | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/databases/settings.table b/databases/settings.table index 98ae003..b7ec7d6 100644 --- a/databases/settings.table +++ b/databases/settings.table @@ -1,2 +1,2 @@ -rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number +rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number +|rvo_senica_42_10.0.0.118|en|28.427B45920702|48.70826502|17.28455203|192.168.252.1|rvo_senica_42_10.0.0.118|QMvF7etEvbYMMr8Q6baP|1883|0|59|unipi|ttyUSB0|1|20|5|6|3|.............. From 41d1ec28dd39764d10f2eb6a697742fde32f1f8a Mon Sep 17 00:00:00 2001 From: rasta5man Date: Wed, 1 Jan 2025 12:53:20 +0100 Subject: [PATCH 09/25] version with cloud_topic --- cloud_topic.py | 76 ++++++ config | 2 +- databases/settings.table | 4 +- flow/cloudmqttconnect.js | 13 +- flow/cmd_manager.js | 2 +- flow/db_init.js | 1 + flow/designer.json | 386 ++++++++++++--------------- flow/helper/ErrorToServiceHandler.js | 4 +- flow/slack_connector.js | 124 --------- flow/thermometer.js | 2 +- 10 files changed, 264 insertions(+), 350 deletions(-) create mode 100644 cloud_topic.py delete mode 100644 flow/slack_connector.js diff --git a/cloud_topic.py b/cloud_topic.py new file mode 100644 index 0000000..77c31f1 --- /dev/null +++ b/cloud_topic.py @@ -0,0 +1,76 @@ +# import os +# +# def modify_file(file_path): +# """ +# Modifies the given file by: +# 1. Appending "|cloud_topic" to the first line. +# 2. Inserting the text from the third "." to the first "|" on the second line after the last "|" character. +# +# Args: +# file_path (str): The path to the file to be modified. +# """ +# +# with open(file_path, 'r+') as f: +# lines = f.readlines() +# +# # Modify the first line +# lines[0] += "|cloud_topic:string" +# +# # Modify the second line +# second_line = lines[1].strip() # Remove leading/trailing whitespace +# first_pipe_index = second_line.find('|') +# third_dot_index = second_line.find('.', second_line.find('.', second_line.find('.') + 1) + 1) +# text_to_insert = second_line[third_dot_index:first_pipe_index] +# +# last_pipe_index = second_line.rfind('|') +# lines[1] = second_line[:last_pipe_index + 1] + text_to_insert + "|" + second_line[last_pipe_index + 1:] +# +# print(first_pipe_index, third_dot_index, text_to_insert, last_pipe_index) +# # Write the modified lines back to the file +# # f.seek(0) +# # f.writelines(lines) +# # f.truncate() +# +# # Example usage: +# file_path = "settings.table" # Replace with the actual file path +# modify_file(file_path) +# + + +def modify_file(file_path): + """ + Modifies the given file by: + 1. Appending "|cloud_topic" to the first line. + 2. Inserting the text between the third "." and the second "|" on the second line after the last "|" character. + + Args: + file_path (str): The path to the file to be modified. + """ + + with open(file_path, 'r+') as f: + lines = f.readlines() + + first_line = lines[0].strip() + first_line += "|cloud_topic:string\n" + # Modify the first line + lines[0] = first_line + + # Modify the second line + second_line = lines[1].strip() # Remove leading/trailing whitespace + first_pipe_index = second_line.find('|') + second_pipe_index = second_line.find('|', first_pipe_index + 1) + third_dot_index = second_line.find('.', second_line.find('.', second_line.find('.') + 1) + 1) + text_to_insert = "u" + second_line[third_dot_index+1:second_pipe_index] + + last_pipe_index = second_line.rfind('|') + lines[1] = second_line[:last_pipe_index + 1] + text_to_insert + "|" + second_line[last_pipe_index + 1:] + + print(first_pipe_index, third_dot_index, text_to_insert, last_pipe_index) + # Write the modified lines back to the file + f.seek(0) + f.writelines(lines) + f.truncate() + +# Example usage: +file_path = "/home/unipi/flowserver/databases/settings.table" # Replace with the actual file path +modify_file(file_path) diff --git a/config b/config index 25ee651..6913e51 100644 --- a/config +++ b/config @@ -7,6 +7,6 @@ package#flow (Object) : { url: '/' } table.relays : line:number|tbname:string|contactor:number|profile:string table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number -table.settings : rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number +table.settings : rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string table.pins : pin:string|type:string|line:number table.notifications : key:string|weight:string|sk:string|en:string diff --git a/databases/settings.table b/databases/settings.table index b7ec7d6..0aec6b3 100644 --- a/databases/settings.table +++ b/databases/settings.table @@ -1,2 +1,2 @@ -rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number -+|rvo_senica_42_10.0.0.118|en|28.427B45920702|48.70826502|17.28455203|192.168.252.1|rvo_senica_42_10.0.0.118|QMvF7etEvbYMMr8Q6baP|1883|0|59|unipi|ttyUSB0|1|20|5|6|3|.............. +rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string ++|rvo_senica_22_ip10.0.0.109|en|28.F46E9D0E0000|48.70826502|17.28455203|192.168.252.1|rvo_senica_22_ip10.0.0.109|9excvr7yBcF3gl3kYZGY|1883|0|48|unipi|ttyUSB0|1|20|5|6|3|u109|........................................... diff --git a/flow/cloudmqttconnect.js b/flow/cloudmqttconnect.js index 79bc04e..85acfdc 100644 --- a/flow/cloudmqttconnect.js +++ b/flow/cloudmqttconnect.js @@ -4,7 +4,7 @@ exports.group = 'MQTT'; exports.color = '#888600'; exports.version = '1.0.2'; exports.icon = 'sign-out'; -exports.input = 1; +exports.input = 2; exports.output = 2; exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" }; @@ -22,7 +22,7 @@ exports.html = `
@(Client id)
-
topic
+
topic
`; @@ -77,6 +77,8 @@ exports.install = function(instance) { { o = instance.options; + if(!o.topic) o.topic = FLOW.GLOBALS.settings.cloud_topic; + opts = { host: o.host, port: o.port, @@ -86,8 +88,7 @@ exports.install = function(instance) { resubscribe: false }; - - console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); + console.log("wsmqttpublich -> loadSettings from instance.options",o); connectToTbServer(); } @@ -212,6 +213,9 @@ exports.install = function(instance) { } }); + instance.on("1", _ => { + main(); + }) instance.close = function(done) { if(clientReady){ @@ -399,6 +403,5 @@ exports.install = function(instance) { } instance.on('options', main); - main(); }; diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index ed4a928..255f48e 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -1451,7 +1451,7 @@ exports.install = function(instance) { if (!rsPort.isOpen) { instance.send(SEND_TO.debug, "!rsPort.isOpen"); //await rsPort.open(); - console.log("Cmd_manager - !rsPort.isOpen"); + //console.log("Cmd_manager - !rsPort.isOpen"); } let currentTask = tasks[0]; diff --git a/flow/db_init.js b/flow/db_init.js index cf3b5a7..76408c8 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -84,6 +84,7 @@ exports.install = async function(instance) { mqtt_username : responseSettings[0]["mqtt_username"], mqtt_port : responseSettings[0]["mqtt_port"], phases: responseSettings[0]["phases"], + cloud_topic: responseSettings[0]["cloud_topic"], //dynamic values masterNodeIsResponding : true, //cmd_manager diff --git a/flow/designer.json b/flow/designer.json index 455b37a..2223c18 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -82,6 +82,10 @@ { "index": "0", "id": "1731068754606" + }, + { + "index": "0", + "id": "1733574445563" } ] }, @@ -449,8 +453,8 @@ "color": "gray" }, "options": { - "data": "{line: 3, command: \"turnOff\", force: true}", - "datatype": "object" + "datatype": "object", + "data": "{line: 3, command: \"turnOff\", force: true}" }, "color": "#F6BB42", "notes": "" @@ -477,116 +481,13 @@ "color": "#303E4D", "notes": "" }, - { - "id": "1617178324650", - "component": "debug", - "tab": "1615551125555", - "name": "Debug", - "x": 700, - "y": 1495, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "options": { - "type": "data", - "repository": false, - "enabled": true - }, - "color": "#967ADC", - "notes": "" - }, - { - "id": "1617179044099", - "component": "trigger", - "tab": "1615551125555", - "name": "start import", - "x": 330, - "y": 1503, - "connections": { - "0": [ - { - "index": "0", - "id": "1617180390661" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "options": { - "datatype": "object", - "data": "{table: \"konsberg_production_line_operations_error\", startFrom: 1, delimiter: \";\", uniqueColumn: \"\", path: \"flow/operations_error.csv\", mapImport: {0: \"production_line\",\t1: \"operation\", 2: \"error_type\", 3: \"error_code\", 4: \"error_text\", 5: \"error_text_user_defined\"}}" - }, - "color": "#F6BB42", - "notes": "" - }, - { - "id": "1617180390661", - "component": "csv_import", - "tab": "1615551125555", - "name": "CsvImport", - "x": 509, - "y": 1484, - "connections": { - "0": [ - { - "index": "0", - "id": "1617178324650" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "options": { - "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" - }, - { - "id": "1617197763128", - "component": "comment", - "tab": "1615551125555", - "name": "import data from csv", - "x": 496, - "y": 1417, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "options": {}, - "color": "#704cff", - "notes": "" - }, { "id": "1617284749681", "component": "trigger", "tab": "1615551125555", "name": "update profile / node", - "x": 122, - "y": 209, + "x": 112, + "y": 215, "connections": { "0": [ { @@ -604,8 +505,8 @@ "color": "gray" }, "options": { - "datatype": "string", - "data": "profile_nodes" + "data": "profile_nodes", + "datatype": "string" }, "color": "#F6BB42", "notes": "" @@ -841,8 +742,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{line: 1, command: \"turnOn\", force: true}" + "data": "{line: 1, command: \"turnOn\", force: true}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -852,8 +753,8 @@ "component": "cmd_manager", "tab": "1615551125555", "name": "CMD Manager", - "x": 448.1091003417969, - "y": 351.05455017089844, + "x": 452.1091003417969, + "y": 341.05455017089844, "connections": { "0": [ { @@ -971,8 +872,8 @@ "color": "gray" }, "options": { - "data": "{command: \"turnOnAlarm\"}", - "datatype": "object" + "datatype": "object", + "data": "{command: \"turnOnAlarm\"}" }, "color": "#F6BB42", "notes": "" @@ -1001,8 +902,8 @@ "color": "gray" }, "options": { - "data": "{command: \"turnOffAlarm\"}", - "datatype": "object" + "datatype": "object", + "data": "{command: \"turnOffAlarm\"}" }, "color": "#F6BB42", "notes": "" @@ -1034,8 +935,8 @@ "component": "httproute", "tab": "1615551125555", "name": "POST /db_connector", - "x": 1107, - "y": 338, + "x": 98, + "y": 1586, "connections": { "0": [ { @@ -1074,8 +975,8 @@ "component": "db_connector", "tab": "1615551125555", "name": "DbConnector", - "x": 1363, - "y": 395, + "x": 372, + "y": 1572, "connections": { "1": [ { @@ -1103,8 +1004,8 @@ "component": "httpresponse", "tab": "1615551125555", "name": "HTTP Response", - "x": 1588, - "y": 454, + "x": 596, + "y": 1586, "connections": {}, "disabledio": { "input": [], @@ -1123,8 +1024,8 @@ "component": "monitormemory", "tab": "1612772287426", "name": "RAM", - "x": 77.88333129882812, - "y": 703.5, + "x": 69.88333129882812, + "y": 885.5, "connections": { "0": [ { @@ -1138,7 +1039,7 @@ "output": [] }, "state": { - "text": "846.37 MB / 985.68 MB", + "text": "848.02 MB / 985.68 MB", "color": "gray" }, "options": { @@ -1153,8 +1054,8 @@ "component": "monitordisk", "tab": "1612772287426", "name": "disk", - "x": 78.88333129882812, - "y": 800.5, + "x": 70.88333129882812, + "y": 982.5, "connections": { "0": [ { @@ -1184,8 +1085,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "send-to-services", - "x": 38.883331298828125, - "y": 1220.5, + "x": 51.883331298828125, + "y": 1400.5, "connections": { "0": [ { @@ -1217,8 +1118,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "send-to-services", - "x": 434.8833312988281, - "y": 696.5, + "x": 426.8833312988281, + "y": 878.5, "connections": {}, "disabledio": { "input": [], @@ -1262,8 +1163,8 @@ "tab": "1612772287426", "name": "192.168.252.2:8004/sentmessage", "reference": "", - "x": 467.8833312988281, - "y": 1154.7333374023438, + "x": 480.8833312988281, + "y": 1334.7333374023438, "connections": { "0": [ { @@ -1281,9 +1182,9 @@ "color": "gray" }, "options": { - "stringify": "json", + "url": "http://192.168.252.2:8004/sentmessage", "method": "POST", - "url": "http://192.168.252.2:8004/sentmessage" + "stringify": "json" }, "color": "#5D9CEC", "notes": "" @@ -1293,8 +1194,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 267.75, - "y": 1266, + "x": 280.75, + "y": 1446, "connections": {}, "disabledio": { "input": [ @@ -1319,8 +1220,8 @@ "component": "code", "tab": "1612772287426", "name": "Code", - "x": 261, - "y": 606, + "x": 253, + "y": 788, "connections": { "0": [ { @@ -1354,8 +1255,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 436, - "y": 602, + "x": 428, + "y": 784, "connections": {}, "disabledio": { "input": [ @@ -1380,8 +1281,8 @@ "component": "code", "tab": "1612772287426", "name": "Code", - "x": 250, - "y": 702, + "x": 242, + "y": 884, "connections": { "0": [ { @@ -1415,8 +1316,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 437, - "y": 794, + "x": 429, + "y": 976, "connections": {}, "disabledio": { "input": [ @@ -1441,8 +1342,8 @@ "component": "code", "tab": "1612772287426", "name": "Code", - "x": 253, - "y": 796, + "x": 245, + "y": 978, "connections": { "0": [ { @@ -1476,8 +1377,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 440, - "y": 886, + "x": 432, + "y": 1068, "connections": {}, "disabledio": { "input": [ @@ -1502,8 +1403,8 @@ "component": "debug", "tab": "1612772287426", "name": "Send info", - "x": 467, - "y": 1261, + "x": 480, + "y": 1441, "connections": {}, "disabledio": { "input": [ @@ -1528,8 +1429,8 @@ "component": "infosender", "tab": "1612772287426", "name": "Info sender", - "x": 272, - "y": 1158, + "x": 285, + "y": 1338, "connections": { "0": [ { @@ -1561,8 +1462,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 782.8833312988281, - "y": 1149.5, + "x": 795.8833312988281, + "y": 1329.5, "connections": {}, "disabledio": { "input": [ @@ -1609,8 +1510,8 @@ "component": "monitorconsumption", "tab": "1612772287426", "name": "CPU", - "x": 77, - "y": 609, + "x": 69, + "y": 791, "connections": { "0": [ { @@ -1624,7 +1525,7 @@ "output": [] }, "state": { - "text": "6.2% / 70.96 MB", + "text": "0.5% / 88.03 MB", "color": "gray" }, "options": { @@ -2087,8 +1988,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "tb-push", - "x": 64.75, - "y": 1450, + "x": 77.75, + "y": 1630, "connections": { "0": [ { @@ -2116,8 +2017,8 @@ "component": "slack_filter", "tab": "1612772287426", "name": "Slack Filter", - "x": 283, - "y": 1491, + "x": 296, + "y": 1671, "connections": { "0": [ { @@ -2143,7 +2044,7 @@ "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "rvo_senica_46_10.0.0.133" + "name": "rvo_senica_1_10.0.0.141" }, "color": "#30E193", "notes": "" @@ -2153,8 +2054,8 @@ "component": "httprequest", "tab": "1612772287426", "name": "http://192.168.252.2:8004/slack", - "x": 482, - "y": 1573, + "x": 495, + "y": 1753, "connections": { "0": [ { @@ -2172,9 +2073,9 @@ "color": "gray" }, "options": { - "stringify": "json", + "url": "http://192.168.252.2:8004/slack", "method": "POST", - "url": "http://192.168.252.2:8004/slack" + "stringify": "json" }, "color": "#5D9CEC", "notes": "" @@ -2184,8 +2085,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 819, - "y": 1484, + "x": 832, + "y": 1664, "connections": {}, "disabledio": { "input": [ @@ -2210,8 +2111,8 @@ "component": "trigger", "tab": "1612772287426", "name": "Trigger", - "x": 66, - "y": 1543, + "x": 79, + "y": 1723, "connections": { "0": [ { @@ -2229,8 +2130,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -2240,8 +2141,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "platform-rpc-call", - "x": 1058.933334350586, - "y": 488.3500061035156, + "x": 1062.933334350586, + "y": 532.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -2262,8 +2163,8 @@ "component": "debug", "tab": "1612772287426", "name": "rpc cloud", - "x": 1066.933334350586, - "y": 393.3500061035156, + "x": 1063.933334350586, + "y": 442.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -2315,8 +2216,8 @@ "component": "cloudmqttconnect", "tab": "1612772287426", "name": "MQTT client - to senica-prod01", - "x": 739.75, - "y": 434, + "x": 742.75, + "y": 520, "connections": { "1": [ { @@ -2338,11 +2239,11 @@ "color": "green" }, "options": { - "username": "", - "clientid": "", - "port": "2764", "host": "192.168.252.2", - "topic": "u133" + "port": "2764", + "clientid": "", + "username": "", + "topic": "" }, "color": "#888600", "notes": "" @@ -2401,8 +2302,8 @@ "component": "showdb", "tab": "1612772287426", "name": "Show db data", - "x": 1076.75, - "y": 745.25, + "x": 1121.75, + "y": 814.25, "connections": { "0": [ { @@ -2428,8 +2329,8 @@ "component": "debug", "tab": "1612772287426", "name": "dbData", - "x": 1270.75, - "y": 784.25, + "x": 1315.75, + "y": 853.25, "connections": {}, "disabledio": { "input": [], @@ -2452,8 +2353,8 @@ "component": "trigger", "tab": "1612772287426", "name": "settings", - "x": 863.75, - "y": 622.75, + "x": 911.75, + "y": 710.75, "connections": { "0": [ { @@ -2479,8 +2380,8 @@ "component": "trigger", "tab": "1612772287426", "name": "relaysData", - "x": 800.75, - "y": 684.75, + "x": 832.75, + "y": 775.75, "connections": { "0": [ { @@ -2506,8 +2407,8 @@ "component": "trigger", "tab": "1612772287426", "name": "nodesData", - "x": 693.75, - "y": 749.75, + "x": 747.75, + "y": 840.75, "connections": { "0": [ { @@ -2533,8 +2434,8 @@ "component": "trigger", "tab": "1612772287426", "name": "pinsData", - "x": 755.75, - "y": 802.75, + "x": 803.75, + "y": 899.75, "connections": { "0": [ { @@ -2560,8 +2461,8 @@ "component": "trigger", "tab": "1612772287426", "name": "sample data", - "x": 812.75, - "y": 856.75, + "x": 858.75, + "y": 959.75, "connections": { "0": [ { @@ -2587,8 +2488,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "db-init", - "x": 50.75, - "y": 1099, + "x": 63.75, + "y": 1279, "connections": { "0": [ { @@ -2616,8 +2517,8 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "db-init", - "x": 184.88333129882812, - "y": 129, + "x": 151.88333129882812, + "y": 148, "connections": { "0": [ { @@ -2732,8 +2633,8 @@ "component": "trigger", "tab": "1612772287426", "name": "monitor.txt", - "x": 865.75, - "y": 911.75, + "x": 915.75, + "y": 1017.75, "connections": { "0": [ { @@ -2759,8 +2660,8 @@ "component": "trigger", "tab": "1612772287426", "name": "err.txt", - "x": 921.75, - "y": 968.75, + "x": 966.75, + "y": 1080.75, "connections": { "0": [ { @@ -2786,8 +2687,8 @@ "component": "nodesdb_change_check", "tab": "1612772287426", "name": "Nodes DB change check", - "x": 250.88333129882812, - "y": 1813.2333984375, + "x": 263.8833312988281, + "y": 1993.2333984375, "connections": { "0": [ { @@ -2817,8 +2718,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "db-init", - "x": 71.75, - "y": 1814, + "x": 84.75, + "y": 1994, "connections": { "0": [ { @@ -2846,8 +2747,8 @@ "component": "debug", "tab": "1612772287426", "name": "nodesChange", - "x": 548.8833312988281, - "y": 1875.2333984375, + "x": 561.8833312988281, + "y": 2055.2333984375, "connections": {}, "disabledio": { "input": [], @@ -2870,8 +2771,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "tb-push", - "x": 544.8833312988281, - "y": 1769, + "x": 557.8833312988281, + "y": 1949, "connections": {}, "disabledio": { "input": [], @@ -2910,7 +2811,62 @@ }, "color": "#967ADC", "notes": "" + }, + { + "id": "1733574412965", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 557.75, + "y": 574, + "connections": { + "0": [ + { + "index": "1", + "id": "1731068754606" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "options": { + "wirename": "db-init" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1733574445563", + "component": "debug", + "tab": "1612772287426", + "name": "toCloudRado", + "x": 740.75, + "y": 435, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "options": { + "type": "data", + "repository": false, + "enabled": true + }, + "color": "#967ADC", + "notes": "" } ], "version": 615 -} \ No newline at end of file +} diff --git a/flow/helper/ErrorToServiceHandler.js b/flow/helper/ErrorToServiceHandler.js index 97ee9d4..110ea8b 100644 --- a/flow/helper/ErrorToServiceHandler.js +++ b/flow/helper/ErrorToServiceHandler.js @@ -97,7 +97,9 @@ class ErrorToServiceHandler console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender); //TODO UGLY!!! - if(this.projects_id === undefined) this.projects_id = FLOW.GLOBALS.settings.project_id; + // if error occures too early FLOW.GLOBALs.settings.project_id is still undefined + // if(this.projects_id === undefined) this.projects_id = FLOW.GLOBALS.settings.project_id; + if(this.projects_id === undefined) return; /* if(this.projects_id === undefined) diff --git a/flow/slack_connector.js b/flow/slack_connector.js deleted file mode 100644 index 8c073a6..0000000 --- a/flow/slack_connector.js +++ /dev/null @@ -1,124 +0,0 @@ -exports.id = 'slack_connector'; -exports.title = 'Slack_Connector'; -exports.version = '1.0.0'; -exports.group = 'Worksys'; -exports.color = '#888600'; -exports.input = 1; -exports.output = 1; -exports.click = false; -exports.author = 'Jakub Klena'; -exports.icon = 'sign-out'; -exports.options = { slack_channel: "C071KN2Q8SK", api_key: "", bot_name: "Flow DEMO", bot_icon: ":totaljs:" }; -// Slack channel - where to post the messages, can be name like "backend-alerts" -// Bot Name - Name of the "user" that will post these messages, it should be based on which server it is running on. -// Bot Icon - We can use any slack icon (even custom ones uploaded by us) as the "user" profile picture - -exports.html = `
-
-
-
Slack Channel
-
-
-
-
-
API Key:
-
-
-
-
-
Bot Name
-
-
-
Bot Icon
-
-
-
`; - -exports.readme = `Sends any string received on input to Slack Channel.`; - -var log4js = require("log4js"); -var path = require('path'); - -log4js.configure({ - appenders: { - errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } -}); - -const errLogger = log4js.getLogger("errLogs"); -const logger = log4js.getLogger(); -const monitor = log4js.getLogger("monitorLogs"); - -exports.install = function(instance) { - var can = false; - - process.on('uncaughtException', function (err) { - errLogger.error('uncaughtException:', err.message); - errLogger.error(err.stack); - instance.error(err); - }); - - instance.on('data', function(data) { - if (!can) return; - - let str = String(data.data); // Ensuring data get converted to string - let message = { - 'channel': instance.options.slack_channel, - 'username': instance.options.bot_name, - 'icon_emoji': instance.options.bot_icon, - 'text': str - }; - let headers = { - 'Content-type': `application/json`, - 'Authorization': `Bearer ${instance.options.api_key}` - }; - - if (F.is4) { - let opt = { - 'method': 'post', - 'url': 'https://slack.com/api/chat.postMessage', - 'headers': headers, - 'body': JSON.stringify(message), - 'type': 'json', - 'callback': function(err, response) { - if (response && !err) { - var msg = { data: response.body, status: response.status, headers: response.headers, host: response.host, cookies: response.cookies }; - instance.send2(msg); - } else if (err) { - errLogger.error('Slack post failed - err:', err, '\n - response was:', response); - instance.error(err, response); - } - } - }; - REQUEST(opt); - - } else { - U.request('https://slack.com/api/chat.postMessage', ['json', 'post'], JSON.stringify(message), function(err, data, status, headers, host) { - if (response && !err) { - response.data = { data: data, status: status, headers: headers, host: host }; - instance.send2(response); - } else if (err) { - errLogger.error('Slack post failed - err:', err, '\n - response was:', response); - instance.error(err, response); - } - }, null, headers); - } - }); - - instance.reconfigure = function() { - var options = instance.options; - can = options.slack_channel && options.bot_name && options.bot_icon && options.api_key ? true : false; - instance.status(can ? '' : 'Not configured', can ? undefined : 'red'); - }; - - instance.on('options', instance.reconfigure); - instance.reconfigure(); -} diff --git a/flow/thermometer.js b/flow/thermometer.js index 0be7eff..fa015c6 100644 --- a/flow/thermometer.js +++ b/flow/thermometer.js @@ -68,7 +68,7 @@ exports.install = function(instance) { const parseData = function(data) { data = parseFloat(data); - logger.debug("Thermometer", data); + //logger.debug("Thermometer", data); if(isNaN(data)) { errLogger.error("Thermometer sends invalid data"); From ef7817bf223f25a09bfdf8532e893ada1669e6ed Mon Sep 17 00:00:00 2001 From: rasta5man Date: Wed, 1 Jan 2025 13:12:02 +0100 Subject: [PATCH 10/25] Limit, what we send --- flow/cmd_manager.js | 85 ++++++++++++++++++---------------- flow/designer.json | 50 ++++++++++---------- flow/dido_controller.js | 4 ++ flow/helper/DataToTbHandler.js | 47 ++++++++++++++++--- flow/modbus_reader.js | 10 ++-- 5 files changed, 122 insertions(+), 74 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 255f48e..a63185f 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -3,7 +3,6 @@ exports.title = 'CMD Manager'; exports.group = 'Worksys'; exports.color = '#5D9CEC'; exports.version = '0.0.3'; -//blue - send message to relays exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; exports.input = 3; exports.icon = 'cloud-upload'; @@ -141,6 +140,9 @@ exports.install = function(instance) { //helper container for counting resolved group of commands (commands related to set profile) let cmdCounter = {};//key is node, value is counter + + //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice + const nodeProfileSendFail = new Set(); //END OF VARIABLE SETTINGS //-------------------------------- @@ -1403,7 +1405,7 @@ exports.install = function(instance) { if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { //reportovali sme? if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { - sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); + //sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; } } @@ -1420,7 +1422,7 @@ exports.install = function(instance) { if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { //reportovali sme? if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { - sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); + //sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; } } @@ -1471,12 +1473,12 @@ exports.install = function(instance) { let type = params.type; let tbname = params.tbname; - let nodeAddress = params.address; + let node = params.address; let line = null; //rpc related - if (nodesData[nodeAddress] !== undefined) line = nodesData[nodeAddress].line; + if (nodesData[node] !== undefined) line = nodesData[node].line; if (params.line !== undefined) line = params.line; let repeatTask = false; @@ -1510,6 +1512,8 @@ exports.install = function(instance) { const value = params.value; let date = new Date(); + let hour = date.getHours(); + date.setDate(date.getDate() + 1);//next day let sunCalcResult; @@ -1532,12 +1536,11 @@ exports.install = function(instance) { } let info = "aplikovany bod profilu"; - let message = ""; - value == 1 ? message = "on" : message = "off"; + let onOrOff = ""; + value == 1 ? onOrOff = "on" : onOrOff = "off"; - turnLine(message, params.line, info); - - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", { line: params.line, value: message }, "", SEND_TO.tb, instance); + turnLine(onOrOff, params.line, info); + interval = setInterval(runTasks, SHORT_INTERVAL); return; } @@ -1590,25 +1593,26 @@ exports.install = function(instance) { } } - let relayStatus = 1; - if (relaysData[line] != undefined) { - relayStatus = relaysData[line].contactor; - } + //TODO -> status offline for rvo if rotary_switch_state is OFF + //let relayStatus = 1; + //if (relaysData[line] != undefined) { + // relayStatus = relaysData[line].contactor; + //} - if (line == 0) relayStatus = 0; - if (type == "cmd-terminal") relayStatus = 1; + //if (line == 0) relayStatus = 0; + //if (type == "cmd-terminal") relayStatus = 1; //check if rotary_switch_state == "Off" - if (relayStatus == 0) { + //if (relayStatus == 0) { //console.log("------------------------------------relayStatus", relayStatus, line); - let values = { "status": "OFFLINE" }; + //let values = { "status": "OFFLINE" }; - sendTelemetry(values, tbname) + //if(tbname) sendTelemetry(values, tbname) - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } + //interval = setInterval(runTasks, SHORT_INTERVAL); + //return; + //} if (!rsPort.isOpen) { interval = setInterval(runTasks, LONG_INTERVAL); @@ -1667,7 +1671,7 @@ exports.install = function(instance) { } //----------------------- - instance.send(SEND_TO.debug, "address: " + nodeAddress + " register:" + register + "type: " + type); + instance.send(SEND_TO.debug, "address: " + node + " register:" + register + "type: " + type); var startTime, endTime; startTime = new Date(); @@ -1676,7 +1680,7 @@ exports.install = function(instance) { if (!tbname) saveToTb = false; let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) - let resp = com_generic(nodeAddress, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); + let resp = com_generic(node, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); let readBytes = 11; let timeout = 4000; @@ -1717,19 +1721,20 @@ exports.install = function(instance) { //CMD FINISHED if (message == "OK") { - updateNodeStatus(nodeAddress, true); + updateNodeStatus(node, true); //write if (type == "set_node_profile") { - let result = cmdCounterResolve(nodeAddress); + let result = cmdCounterResolve(node); if (result == 0) { - dbNodes.modify({ processed: true }).where("node", nodeAddress).make(function(builder) { + dbNodes.modify({ processed: true }).where("node", node).make(function(builder) { builder.callback(function(err, response) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: nodeAddress }, "", SEND_TO.tb, instance); + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); - logger.debug("--> profil úspešne odoslaný na node č. " + nodeAddress); - nodesData[nodeAddress].processed = true; + logger.debug("--> profil úspešne odoslaný na node č. " + node); + nodesData[node].processed = true; + nodeProfileSendFail.delete(node); }); }); } @@ -1746,7 +1751,7 @@ exports.install = function(instance) { } //master node - if (nodeAddress == 0) { + if (node == 0) { sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); SETTINGS.masterNodeIsResponding = true; if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; @@ -1779,7 +1784,7 @@ exports.install = function(instance) { if (params.hasOwnProperty("debug")) { if (params.debug) { //logger.debug("writeData err: ", error, result, params); - logger.debug("writeData err: ", tbname, nodeAddress, register, values); + logger.debug("writeData err: ", tbname, node, register, values); } } @@ -1803,11 +1808,9 @@ exports.install = function(instance) { } else { - // if(currentTask.debug) - // { - // //currentTask.timestamp <= currentTimestamp - // logger.debug("currentTask is not processed - task is in the future", currentTask); - // } + if(currentTask.debug) { + // currentTask.timestamp <= currentTimestamp && logger.debug("currentTask is not processed - task is in the future", currentTask); + } interval = setInterval(runTasks, LONG_INTERVAL); return; @@ -1843,7 +1846,11 @@ exports.install = function(instance) { if (type == "set_node_profile") { delete cmdCounter[node]; logger.debug("profil nebol úspešne odoslaný na node č. ", params); - sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); + + if(!nodeProfileSendFail.has(node)) { + sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); + nodeProfileSendFail.add(node); + } } if (itIsNodeCommand) { @@ -2870,7 +2877,7 @@ exports.install = function(instance) { //if(byte1 < 10) s = "0" + byte1; var d = new Date(); - d.setHours(h, m, 0); + d.setHours(h, m, 0, 0); timestamp = d.getTime(); } diff --git a/flow/designer.json b/flow/designer.json index 2223c18..e960086 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -453,8 +453,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{line: 3, command: \"turnOff\", force: true}" + "data": "{line: 3, command: \"turnOff\", force: true}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -505,8 +505,8 @@ "color": "gray" }, "options": { - "data": "profile_nodes", - "datatype": "string" + "datatype": "string", + "data": "profile_nodes" }, "color": "#F6BB42", "notes": "" @@ -742,8 +742,8 @@ "color": "gray" }, "options": { - "data": "{line: 1, command: \"turnOn\", force: true}", - "datatype": "object" + "datatype": "object", + "data": "{line: 1, command: \"turnOn\", force: true}" }, "color": "#F6BB42", "notes": "" @@ -872,8 +872,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{command: \"turnOnAlarm\"}" + "data": "{command: \"turnOnAlarm\"}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -902,8 +902,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{command: \"turnOffAlarm\"}" + "data": "{command: \"turnOffAlarm\"}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -1039,7 +1039,7 @@ "output": [] }, "state": { - "text": "848.02 MB / 985.68 MB", + "text": "870.85 MB / 985.68 MB", "color": "gray" }, "options": { @@ -1069,7 +1069,7 @@ "output": [] }, "state": { - "text": "5.77 GB / 7.26 GB", + "text": "5.78 GB / 7.26 GB", "color": "gray" }, "options": { @@ -1182,9 +1182,9 @@ "color": "gray" }, "options": { - "url": "http://192.168.252.2:8004/sentmessage", + "stringify": "json", "method": "POST", - "stringify": "json" + "url": "http://192.168.252.2:8004/sentmessage" }, "color": "#5D9CEC", "notes": "" @@ -1525,7 +1525,7 @@ "output": [] }, "state": { - "text": "0.5% / 88.03 MB", + "text": "12.8% / 71.62 MB", "color": "gray" }, "options": { @@ -2044,7 +2044,7 @@ "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "rvo_senica_1_10.0.0.141" + "name": "rvo_senica_35_10.0.0.129" }, "color": "#30E193", "notes": "" @@ -2073,9 +2073,9 @@ "color": "gray" }, "options": { - "url": "http://192.168.252.2:8004/slack", + "stringify": "json", "method": "POST", - "stringify": "json" + "url": "http://192.168.252.2:8004/slack" }, "color": "#5D9CEC", "notes": "" @@ -2130,8 +2130,8 @@ "color": "gray" }, "options": { - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", - "datatype": "object" + "datatype": "object", + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" }, "color": "#F6BB42", "notes": "" @@ -2239,11 +2239,11 @@ "color": "green" }, "options": { - "host": "192.168.252.2", - "port": "2764", - "clientid": "", "username": "", - "topic": "" + "clientid": "", + "port": "2764", + "host": "192.168.252.2", + "topic": "u129" }, "color": "#888600", "notes": "" @@ -2869,4 +2869,4 @@ } ], "version": 615 -} +} \ No newline at end of file diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 86fb6a5..e9379ea 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -565,6 +565,10 @@ exports.install = function(instance) { ws.send(JSON.stringify(cmd)); switchLogic(pin, value) } + + //if rvo is 24/7, it has just one switching profile point at 13:00. we do not want to send notification as it repeats every day. + const d = new Date(); + if(d.getHours() != 13) sendNotification("Dido_controller: ", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", { line: line, value: onOrOff }, "", SEND_TO.tb, instance); } diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index a89b75b..65e4ec3 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -11,6 +11,34 @@ class DataToTbHandler { this.messageCounter = 0; this.sender = ""; + + // if attribute difference is less than limit value, we do not send to tb. + this.attributeChangeLimit = { + temperature: 0.5, + Phase_1_voltage: 2, + Phase_2_voltage: 2, + Phase_3_voltage: 2, + Phase_1_current: 0.1, + Phase_2_current: 0.1, + Phase_3_current: 0.1, + Phase_1_power: 2, + Phase_2_power: 2, + Phase_3_power: 2, + total_power: 2, + Phase_1_pow_factor: 0.1, + Phase_2_pow_factor: 0.1, + Phase_3_pow_factor: 0.1, + power_factor: 0.1, + lifetime: 0.5, + voltage: 2, + power: 2, + frequency: 3, + energy: 0.1, + current: 2, + inclination_x: 10, + inclination_y: 10, + inclination_z: 10 + }; } dump() { @@ -101,16 +129,20 @@ class DataToTbHandler { continue; } - if(this.previousValues[tbname][key].value === value) + // attributeData ==> voltage: {ts:333333, value:5} + let attributeData = this.previousValues[tbname][key]; + let attributeToChange = false; + if(key in this.attributeChangeLimit) attributeToChange = true; + let limit = this.attributeChangeLimit[key]; + + if(attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { - let diff = timestamp - this.previousValues[tbname][key].ts; - + let diff = timestamp - attributeData.ts; let timestampDiffToRemoveKey = this.getDiffTimestamp(key); if(diff > timestampDiffToRemoveKey) { - this.previousValues[tbname][key].ts = Date.now(); + attributeData.ts = Date.now(); //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); - } else { @@ -120,8 +152,8 @@ class DataToTbHandler { } else { - this.previousValues[tbname][key].value = value; - this.previousValues[tbname][key].ts = timestamp; + attributeData.value = value; + attributeData.ts = timestamp; } } @@ -131,3 +163,4 @@ class DataToTbHandler { } module.exports = DataToTbHandler; + diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index b0908ae..b907148 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -317,20 +317,24 @@ exports.install = function(instance) { } /** - * function sends notification to slack and to tb, if EM total_power value changes more than 500. This should show, that RVO lamps has been switched on or off + * function sends notification to slack and to tb, if EM total_power value changes more than numberOfNodes*15. This should show, that RVO lamps has been switched on or off */ lampSwitchNotification = (values) => { if(!values.hasOwnProperty("total_power")) return; const actualTotalPower = values.total_power; - if(actualTotalPower > 600 && this.onNotificationSent == false) + + const numberOfNodes = Object.keys(FLOW.GLOBALS.nodesData).length; + if(numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300 + + if(actualTotalPower > numberOfNodes * 15 && this.onNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance); this.onNotificationSent = true; this.offNotificationSent = false; } - else if(actualTotalPower <= 600 && this.offNotificationSent == false) + else if(actualTotalPower <= numberOfNodes * 15 && this.offNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance); this.onNotificationSent = false; From c052887a1fffca7d0bb8d007d90ff1980580586c Mon Sep 17 00:00:00 2001 From: rasta5man Date: Tue, 7 Jan 2025 15:30:46 +0100 Subject: [PATCH 11/25] version 2025-02-01 --- databases/notifications.table | 4 +- flow/audit_test_panel.csv | 48 --- flow/cloudmqttconnect.js | 67 +--- flow/cmd_manager.js | 2 - flow/csv_import.js | 175 --------- flow/db_init.js | 2 +- flow/designer.json | 700 ++++++++++++++++------------------ flow/notifikacie.csv | 31 -- flow/variables.txt | 0 flow/wsmqttpublish.js | 38 +- package.json | 1 + 11 files changed, 368 insertions(+), 700 deletions(-) delete mode 100644 flow/audit_test_panel.csv delete mode 100644 flow/csv_import.js delete mode 100644 flow/notifikacie.csv delete mode 100644 flow/variables.txt diff --git a/databases/notifications.table b/databases/notifications.table index 8e3788f..9dbe047 100644 --- a/databases/notifications.table +++ b/databases/notifications.table @@ -1,5 +1,5 @@ key:string|weight:string|sk:string|en:string -+|switching_profile_point_applied_to_line|INFORMATIONAL|Aplikovaný bod spínacieho profilu na línií č. ${line} : ${value}|Switching profile point applied to line no. ${line} : ${value}|............... ++|switching_profile_point_applied_to_line|INFORMATIONAL|Aplikovaný bod spínacieho profilu na línií č. ${line} : ${value}|Switching profile point applied to line no. ${line} : ${value}|............... +|dusk_has_occured|INFORMATIONAL|Nastal súmrak: ${value}|Dusk has occured: ${value}|............... +|dawn_has_occured|INFORMATIONAL|Nastal úsvit: ${value}|Dawn has occured: ${value}|............... +|dimming_profile_was_successfully_received_by_node|NOTICE|Stmievací profil bol úspešne prijatý nodom č. ${node}|Dimming profile was successfully received by node no. ${node}|............... @@ -22,7 +22,7 @@ key:string|weight:string|sk:string|en:string +|battery_level_is_ok|NOTICE|Batéria má správnu úroveň napätia|Battery level is OK|............... +|door_opened|NOTICE|Dvere boli otvorené|Door has been opeed|............... +|door_closed|NOTICE|Dvere boli zatvorené|Door has been closed|............... -+|door_opened_without_permission|WARNING|Dvere boli otvorené bez povoeania - zapnutá siréna|Door has been oed without permision - alarm is on|............... ++|door_opened_without_permission|WARNING|Dvere boli otvorené bez povolenia - zapnutá siréna|Door opened without permision - alarm is on|............... +|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|............... +|local_database_is_corrupted|CRITICAL|||............... +|electrometer_nok|ERROR|Elektromer neodpovedá|Electrometer is not responding|............... diff --git a/flow/audit_test_panel.csv b/flow/audit_test_panel.csv deleted file mode 100644 index 907a6a9..0000000 --- a/flow/audit_test_panel.csv +++ /dev/null @@ -1,48 +0,0 @@ -LumDimm;NODE (HEX);NODE (DEC);Line;TB name -1;299;665;3;gbv4nzqxW0XGAPKVNk8kr25ZQ2l3O6LRBprM97ew -2;28A;650;3;0XYElWeKBNJn1gdoMG8lYdDALkPvj4V3xra2q6mO -3;296;662;3;gbv4nzqxW0XGAPKVNk8kW48ZQ2l3O6LRBprM97ew -4;297;663;1;LpkVlmq4b3jMwJQxBZ8akayrXAP6o97Ke0aOYEg2 -5;29C;668;3;lekrmdvO0BQG1ZW4AV8jeZ5M39xnN2wEbRgPjXLp -6;2B1;689;3;q0rElBPdL6kxMAjnzVDRl95emNZY7oOv2wK9gb31 -7;2AB;683;3;XKQbz3WAwY21dGa0R453rWyJm9PZOjqlvpr6Nkeo -8;2B0;688;3;PaGbQ3wBAZWOmRvK9VDpvz5endLJYopEqlkzNMxX -9;2B9;697;3;joqRYBVL30k9eQWOlZ5qwpD2KJpNEmA6gPxXzwaM -10;293;659;3;Ymn9oleRxJ0vw17WzAyGwdyEBk4ObdMXj2VgpNLG -11;294;660;3;gj7zbKV46oQ1p2e0AJ8XqZDG3YNWaRrlOEXvBxmM -12;295;661;3;laYK7Pomn2bNZXEpedDxAqyOJkQ3WwV49gqxLrAR -13;2A0;672;2;0XYElWeKBNJn1gdoMG8lON5ALkPvj4V3xra2q6mO -14;2B4;692;2;l9YkRpoB2vVa0mKqEO8ZGGDjW43eXnJML6GxzbwQ -15;2B2;690;2;wGjQobgOK0n2YqBZmVDVR3DR9ep6EXA1ka3vzlP7 -16;27C;636;2;M6ogKQW09bOXewAYvZyvJqyJrV1aRnPGE37p42Nx -17;27B;635;2;Vq2JaWpw1OdBKmNeoj8w605XE40l3kgL76Azb9QP -18;2B6;694;2;Jm32GR1qpwQxlZza0N5mE15AP96YbOKLogrXVW4e -19;2B5;693;2;KjbN4q7JPZmexgdnz2yKdn5YAWwO0Q3BMX6ERLoV -20;2B3;691;1;lekrmdvO0BQG1ZW4AV8jzq8M39xnN2wEbRgPjXLp -21;27F;639;3;BOjEzGRZ46bnp9wa2A8z76D0JkmW1QPNdrqevXVL -22;27E;638;3;9xgzG4Op1BrKZPmoQkDrmj8E73ndJNMjavAwX2Re -23;27D;637;3;koW06PeGrLlBp2YJQE5Ogw5RmMaXKzj3wOAZg9n7 -24;28F;655;2;RMgnK93rkoAazbqdQ4yBYpDZ1YXGx6pmwBeVEP2O -25;288;648;2;gaMGN4x1e9JlZz0QPRDd9Rym6dVr3OpvqKnoWBbk -26;298;664;1;oGVzxNWP9lrjaQ7vKODQ7g51gqp62YZREmdw3XBM -27;29F;671;3;AvVdgzYJZaPx3oMqeED4Oj8NnmKkw716bRO90jLB -28;280;640;2;WjBL12pg63eX4N9P7zy0XYyEJKmlbkGwZMx0avQV -29;28B;651;2;qaAOzENGrvpbe0VoK7D6Ld519PZmdg3nl24JLQMk -30;27A;634;2;NGWamnYqlP1wbgrZQxDAWm5e2X7OVAK69koR04vL -31;29E;670;2;dlE1VQjYrNx9gZRmb38g1YyoLBO4qaAk2M6JPnG7 -32;281;641;2;vnmG4kJxaXWNBgMQq0D7Mz5e9oZzOAlr6LdR3w2V -33;278;632;2;LpkVlmq4b3jMwJQxBZ8aM78rXAP6o97Ke0aOYEg2 -34;29D;669;3;Y9aLW03wOZkABvKXbMyL0lyV1xdNj72r4egqGRzJ -35;2A8;680;1;KL2jNOVpdARa9XvoeJDPga8bkmPBxqn7Ww3gzGQ1 -36;2BA;698;1;mYnBzbeGaAL62jowRv59M35Xq9QpZ0K7O1dg4xVl -37;29B;667;1;MzXBoWbEZjO0lrpqnRyoJ4DkmVeaNAGdL9g4QKxP -38;289;649;1;0p2rwdP7aGoOQLJNgAynJNy6xWXbmMe3nvZqlzkV -39;290;656;1;BrQx3NGKgVMRaXYAo9y1GE8ZzkWnj1le6bdOLE20 -40;2AA;682;1;vnreBJ6PMqgz20pYEL82XQyG1jkWwdQxZVNAOlmK -41;285;645;1;jklN4JpQAx362o9XYZDN6wDgrWw1P7GEbdBM0vRV -42;283;643;1;oZmYXEbw9lVWRv1jLxDe9bDdgAMz4PKQnNJ6eB23 -43;282;642;1;pEonaKBOGbj9034MgJ8W3G8qXvxNWVkAPQz21R6L -44;287;647;1;BLQal6Pn9oz1KmNgek5Yqd50vd2MAbqG3OV7Rp4j -45;286;646;1;4agVJ9dPQkmp1R2X3EDJKxyrK6ZlNoM0n7qxBOev -46;29A;666;1;9PpgLEnvk4WMV6RmOJybMGDaeAXzo2BQNG3K17Zw -47;28E;654;1;Mmp93b2nvd7OoqgBeEyEZq5kjlAV1Y4ZNXwW0zLG diff --git a/flow/cloudmqttconnect.js b/flow/cloudmqttconnect.js index 85acfdc..d619784 100644 --- a/flow/cloudmqttconnect.js +++ b/flow/cloudmqttconnect.js @@ -32,6 +32,7 @@ exports.html = `
const { promisifyBuilder } = require('./helper/db_helper'); const fs = require('fs'); const mqtt = require('mqtt'); +const nosql = NOSQL('tbdatacloud'); const SEND_TO = { debug: 0, @@ -56,9 +57,6 @@ let lastRestoreTime = 0; let sendClientError = true; -const nosql = NOSQL('tbdatacloud'); - - exports.install = function(instance) { var client; @@ -112,11 +110,6 @@ exports.install = function(instance) { }); client.on('reconnect', function() { - client.subscribe(`${o.topic}_backward`, (err) => { - if (!err) { - console.log("MQTT subscribed"); - } - }); instance.status("Reconnecting", "yellow"); clientReady = false; }); @@ -139,18 +132,11 @@ exports.install = function(instance) { instance.send(SEND_TO.rpcCall, {"topic":o.topic, "content":message }); }); - client.on('close', function(err) { + client.on('close', function() { clientReady = false; - if (err && err.toString().indexOf('Error')) { - instance.status("Err: "+err.code, "red"); - instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); - } else { - instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); - } - - client.reconnect(); + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !"}); }); client.on('error', function(err) { @@ -170,46 +156,30 @@ exports.install = function(instance) { if(clientReady) { - //do we have some data in backup file? - //if any, process data from database + //do we have some data in backup file? if any, process data from database if(saveTelemetryOnError) { //read telemetry data and send back to server if(!processingData) processDataFromDatabase(); } - } - - if(clientReady) - { - client.publish(`${o.topic}_forward`, data.data, {qos: 1}); - //console.log("ondata..",data.data) - - - // Pokad ten error na 38 chápem tak tento parameter MUSÍ byt string... - // Tak to musim dekódovat na strane Cloudu zas - + + let stringifiedJson = JSON.stringify(data.data) + client.publish(`${o.topic}_forward`, stringifiedJson, {qos: 1}); } else { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); if(saveTelemetryOnError) { - try { - let d = JSON.parse(data.data); - //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql - makeBackupFromDbFile(); + //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql + makeBackupFromDbFile(); - //write to tb - d.id = UID(); - nosql.insert(d); - } catch(e) { - console.log("cloudconnect - unable to parse data from wsmqtt"); - } + //write to tb + data.data.id = UID(); + nosql.insert(data.data); } - } }); @@ -358,12 +328,10 @@ exports.install = function(instance) { try { - let o = JSON.parse(JSON.stringify(item)); - delete o.id; - let message = JSON.stringify(o); - - client.publish(`${o.topic}_forward`, message, {qos:1}); - //console.log("db...", message) + let message = JSON.parse(JSON.stringify(item)); + delete message.id; + client.publish(`${o.topic}_forward`, JSON.stringify(message), {qos:1}); + //remove from database await promisifyBuilder(nosql.remove().where("id", id)); @@ -372,7 +340,6 @@ exports.install = function(instance) { console.log("processDataFromDatabase", error); } - } } diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index a63185f..a9376a5 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -1512,8 +1512,6 @@ exports.install = function(instance) { const value = params.value; let date = new Date(); - let hour = date.getHours(); - date.setDate(date.getDate() + 1);//next day let sunCalcResult; diff --git a/flow/csv_import.js b/flow/csv_import.js deleted file mode 100644 index 5ae1e68..0000000 --- a/flow/csv_import.js +++ /dev/null @@ -1,175 +0,0 @@ -exports.id = 'csv_import'; -exports.title = 'CsvImport'; -exports.version = '1.0.0'; -exports.group = 'Worksys'; -exports.color = '#2134B0'; -exports.input = 1; -exports.output = ["red", "white"]; -exports.click = false; -exports.author = 'Daniel Segeš'; -exports.icon = 'file-import'; -exports.options = { edge: "undefined" }; - -exports.html = `
-
-
-
CSV Import
-
-
-
`; - -exports.readme = `# load csv to table db`; - -//config -let delimiter = ";"; -let uniqueColumn = "node"; -let path = "flow/audit_test_panel.csv"; -let startFrom = 1; -let table = "nodes"; -let mapImport = { - 2: "node", - 4: "tbname", - 3: "line" -}; - -//10.0.0.62 -delimiter = ";"; -uniqueColumn = "node"; -path = "flow/audit_rvo14_lampy.csv"; -startFrom = 1; -table = "nodes"; -mapImport = { - 1: "node", - 3: "tbname", - 2: "line" -}; - -//notification -delimiter = ";"; -uniqueColumn = undefined; -path = "flow/notifikacie.csv"; -startFrom = 1; -table = "notifications"; -mapImport = { - 0: "key", - 1: "weight", - 2: "en", - 3: "sk" -}; - -const fs = require('fs'); - -exports.install = function(instance) { - - //console.log("csv import installed"); - - instance.on("close", () => { - - }) - - - instance.on("data", (flowdata) => { - - instance.send(0, "start import"); - console.log("csv import", flowdata.data); - - //{table: "nodes", startFrom: 1, delimiter: ";", uniqueColumn: "node", path: "flow/audit_rvo14_lampy.csv", mapImport: {1: "node", 3: "tbname", 2: "line"}} - - - if(typeof flowdata.data === 'object') - { - console.log("*******************", flowdata.data); - - if(!flowdata.data.hasOwnProperty("table")) - { - instance.send(0, "!!!!csv import - nedefinovana tabulka"); - return; - } - - if(!flowdata.data.hasOwnProperty("uniqueColumn")) - { - //instance.send(0, "!!!!csv import - nedefinovane uniqueColumn"); - //return; - } - - if(!flowdata.data.hasOwnProperty("path")) - { - instance.send(0, "!!!!csv import - nedefinovana cesta k suboru"); - return; - } - - if(!flowdata.data.hasOwnProperty("mapImport")) - { - instance.send(0, "!!!!csv import - nedefinovany mapImport"); - return; - } - - table = flowdata.data.table; - uniqueColumn = flowdata.data.uniqueColumn; - if(uniqueColumn === "") uniqueColumn = undefined; - - path = flowdata.data.path; - mapImport = flowdata.data.mapImport; - - if(flowdata.data.hasOwnProperty("delimiter")) delimiter = flowdata.data.delimiter; - if(flowdata.data.hasOwnProperty("startFrom")) startFrom = flowdata.data.startFrom; - } - - - var db = TABLE(table); - db.clear(); - - let keys = Object.keys(mapImport); - - try { - const data = fs.readFileSync(path, 'utf8') - - let lines = data.split("\n"); - - for(let i = startFrom; i < lines.length; i++) - { - let line = lines[i]; - if(line === "") continue; - - let data = line.split(delimiter); - if(data.length == 0) continue; - - let insertData = {}; - - keys.map(function(key){ - let k = mapImport[key]; - - //console.log("importineg", i, key, k); - - if(data[key] != undefined) insertData[k] = data[key].trim(); - else{ - console.log("undefined", key, data); - } - }); - - console.log("insertData", insertData); - - if(uniqueColumn != undefined) - { - db.insert(insertData, true).where(uniqueColumn, insertData[uniqueColumn]); - } - else - { - db.insert(insertData); - } - - - } - - console.log("csv import finished"); - instance.send(0, "csv import finished"); - - } catch (err) { - console.error(err) - instance.send(0, err); - } - }) - -} - - diff --git a/flow/db_init.js b/flow/db_init.js index 76408c8..c43e4a2 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -64,7 +64,7 @@ exports.install = async function(instance) { if(dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; dbs.settings = { - edge_fw_version : "2024-11-04", //rok-mesiac-den + edge_fw_version : "2025-01-02", //rok-mesiac-den language : responseSettings[0]["lang"], rvo_name : responseSettings[0]["rvo_name"], project_id : responseSettings[0]["project_id"], diff --git a/flow/designer.json b/flow/designer.json index e960086..fbdda76 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -36,21 +36,21 @@ "text": "Enabled", "color": "gray" }, + "color": "#DA4453", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#DA4453", - "notes": "" + } }, { "id": "1612776786008", "component": "wsmqttpublish", "tab": "1612772287426", "name": "WS MQTT publish", - "x": 310.75, - "y": 263, + "x": 304.75, + "y": 237, "connections": { "0": [ { @@ -82,10 +82,6 @@ { "index": "0", "id": "1731068754606" - }, - { - "index": "0", - "id": "1733574445563" } ] }, @@ -94,25 +90,25 @@ "output": [] }, "state": { - "text": "Connected", - "color": "green" + "text": "Reconnecting", + "color": "yellow" }, + "color": "#888600", + "notes": "", "options": { "username": "", "clientid": "", "port": "1883", "host": "" - }, - "color": "#888600", - "notes": "" + } }, { "id": "1612778461252", "component": "virtualwirein", "tab": "1612772287426", "name": "tb-push", - "x": 85.75, - "y": 352, + "x": 86.75, + "y": 375, "connections": { "0": [ { @@ -122,6 +118,10 @@ { "index": "1", "id": "1612776786008" + }, + { + "index": "0", + "id": "1731068754606" } ] }, @@ -133,19 +133,19 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1612783322136", "component": "debug", "tab": "1612772287426", "name": "to TB", - "x": 312.75, - "y": 426, + "x": 306.75, + "y": 371, "connections": {}, "disabledio": { "input": [ @@ -157,21 +157,21 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615551060773", "component": "debug", "tab": "1612772287426", "name": "errors from MQTT Broker", - "x": 743, - "y": 65, + "x": 650, + "y": 76, "connections": {}, "disabledio": { "input": [ @@ -183,13 +183,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#DA4453", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#DA4453", - "notes": "" + } }, { "id": "1615563373927", @@ -209,13 +209,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#DA4453", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#DA4453", - "notes": "" + } }, { "id": "1615566865233", @@ -233,11 +233,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1615798582262", @@ -257,13 +257,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615802995322", @@ -281,13 +281,13 @@ "text": "Disabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": false - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615809128443", @@ -305,13 +305,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615809595184", @@ -329,11 +329,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1616165795916", @@ -362,6 +362,9 @@ "text": "Listening", "color": "green" }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, "options": { "timeout": 10, "cachepolicy": 0, @@ -376,10 +379,7 @@ 10000 ], "emptyresponse": false - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false + } }, { "id": "1616165824813", @@ -397,11 +397,11 @@ "text": "", "color": "gray" }, + "color": "#5D9CEC", + "notes": "", "options": { "datatype": "json" - }, - "color": "#5D9CEC", - "notes": "" + } }, { "id": "1617104731852", @@ -421,13 +421,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1617114651703", @@ -452,12 +452,12 @@ "text": "", "color": "gray" }, - "options": { - "data": "{line: 3, command: \"turnOff\", force: true}", - "datatype": "object" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "datatype": "object", + "data": "{line: 3, command: \"turnOff\", force: true}" + } }, { "id": "1617115013095", @@ -475,11 +475,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1617284749681", @@ -504,12 +504,12 @@ "text": "", "color": "gray" }, - "options": { - "datatype": "string", - "data": "profile_nodes" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "data": "profile_nodes", + "datatype": "string" + } }, { "id": "1618235171399", @@ -534,19 +534,19 @@ "text": "", "color": "gray" }, + "color": "#F6BB42", + "notes": "", "options": { "data": "run" - }, - "color": "#F6BB42", - "notes": "" + } }, { "id": "1618300858252", "component": "debug", "tab": "1612772287426", "name": "wsmqtt-exit1", - "x": 742.8833312988281, - "y": 153, + "x": 650.8833312988281, + "y": 160, "connections": {}, "disabledio": { "input": [], @@ -556,21 +556,21 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1618300863816", "component": "debug", "tab": "1612772287426", "name": "wsmqtt-exit2", - "x": 1064.8833312988281, - "y": 292, + "x": 845.8833312988281, + "y": 320, "connections": {}, "disabledio": { "input": [ @@ -582,13 +582,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1618393583970", @@ -606,11 +606,11 @@ "text": "from-dido-controller", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "from-dido-controller" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618393674428", @@ -635,11 +635,11 @@ "text": "platform-rpc-call", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "platform-rpc-call" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618393759854", @@ -668,11 +668,11 @@ "text": "cmd_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "cmd_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618393827655", @@ -690,18 +690,18 @@ "text": "cmd_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "cmd_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618558465485", "component": "virtualwireout", "tab": "1612772287426", "name": "platform-rpc-call", - "x": 739.8833312988281, + "x": 649.8833312988281, "y": 246, "connections": {}, "disabledio": { @@ -712,11 +712,11 @@ "text": "platform-rpc-call", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "platform-rpc-call" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618572059773", @@ -741,12 +741,12 @@ "text": "", "color": "gray" }, - "options": { - "datatype": "object", - "data": "{line: 1, command: \"turnOn\", force: true}" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "data": "{line: 1, command: \"turnOn\", force: true}", + "datatype": "object" + } }, { "id": "1619515097737", @@ -799,9 +799,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": {} }, { "id": "1619605019281", @@ -830,6 +830,9 @@ "text": "Listening", "color": "green" }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -843,10 +846,7 @@ "get", 5000 ] - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false + } }, { "id": "1619784672383", @@ -871,12 +871,12 @@ "text": "", "color": "gray" }, - "options": { - "data": "{command: \"turnOnAlarm\"}", - "datatype": "object" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "datatype": "object", + "data": "{command: \"turnOnAlarm\"}" + } }, { "id": "1619784812964", @@ -901,12 +901,12 @@ "text": "", "color": "gray" }, - "options": { - "data": "{command: \"turnOffAlarm\"}", - "datatype": "object" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "datatype": "object", + "data": "{command: \"turnOffAlarm\"}" + } }, { "id": "1621340721628", @@ -924,11 +924,11 @@ "text": "modbus_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "modbus_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1622640022885", @@ -953,6 +953,9 @@ "text": "Listening", "color": "green" }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -965,10 +968,7 @@ "post", 5000 ] - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false + } }, { "id": "1622640073521", @@ -993,11 +993,11 @@ "text": "", "color": "gray" }, + "color": "#2134B0", + "notes": "", "options": { "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" + } }, { "id": "1622641420685", @@ -1015,9 +1015,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": {} }, { "id": "1634303504177", @@ -1039,15 +1039,15 @@ "output": [] }, "state": { - "text": "870.85 MB / 985.68 MB", + "text": "776.75 MB / 987.80 MB", "color": "gray" }, + "color": "#F6BB42", + "notes": "", "options": { "enabled": true, "interval": 30000 - }, - "color": "#F6BB42", - "notes": "" + } }, { "id": "1634303533779", @@ -1069,16 +1069,16 @@ "output": [] }, "state": { - "text": "5.78 GB / 7.26 GB", + "text": "5.79 GB / 7.26 GB", "color": "gray" }, + "color": "#F6BB42", + "notes": "", "options": { "enabled": true, "path": "/", "interval": 30000 - }, - "color": "#F6BB42", - "notes": "" + } }, { "id": "1634303595494", @@ -1107,11 +1107,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1634303602169", @@ -1129,19 +1129,19 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1634303685503", "component": "virtualwireout", "tab": "1612772287426", "name": "send-to-services", - "x": 740.8833312988281, - "y": 341.5, + "x": 650.8833312988281, + "y": 355.5, "connections": {}, "disabledio": { "input": [], @@ -1151,11 +1151,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1634303743260", @@ -1181,13 +1181,13 @@ "text": "", "color": "gray" }, - "options": { - "stringify": "json", - "method": "POST", - "url": "http://192.168.252.2:8004/sentmessage" - }, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": { + "url": "http://192.168.252.2:8004/sentmessage", + "method": "POST", + "stringify": "json" + } }, { "id": "1634463186563", @@ -1207,13 +1207,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634464580289", @@ -1242,13 +1242,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "let response = {};\nresponse.cpu = value.cpu;\nresponse.uptime = value.uptime;\n\nsend(0, response);", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1634465243324", @@ -1268,13 +1268,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634465281992", @@ -1303,13 +1303,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "value.sender = \"ram\";\n//let total = value.total/1024/1024;\n//let free = value.free/1024/1024;\n//let used = value.used/1024/1024;\nlet response = {};\n//value.memory_total = (total).toFixed(0) + ' MB';\n//value.memory_free = (free).toFixed(0) + ' MB';\n//value.memory_used = (used).toFixed(0) + ' MB';\n\nresponse.memory_total = value.total;\nresponse.memory_free = value.free;\nresponse.memory_used = value.used;\n\nsend(0, response);", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1634465338103", @@ -1329,13 +1329,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634465821120", @@ -1364,13 +1364,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "value.sender = \"hdd\";\n//let total = value.total/1024/1024;\n//let free = value.free/1024/1024;\n//let used = value.used/1024/1024;\nlet response = {};\n//value.hdd_total = (total).toFixed(0) + ' MB';\n//value.hdd_free = (free).toFixed(0) + ' MB';\n//value.used = (used).toFixed(0) + ' MB';\n\nresponse.hdd_total = value.total;\nresponse.hdd_free = value.free;\nresponse.hdd_used = value.used;\n\nsend(0, response);", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1634465892500", @@ -1390,13 +1390,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634484067516", @@ -1416,13 +1416,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634488120710", @@ -1451,11 +1451,11 @@ "text": "", "color": "gray" }, + "color": "#2134B0", + "notes": "", "options": { "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" + } }, { "id": "1635327431236", @@ -1475,13 +1475,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1635936391935", @@ -1499,11 +1499,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1637069803394", @@ -1525,9 +1525,11 @@ "output": [] }, "state": { - "text": "12.8% / 71.62 MB", + "text": "1.5% / 72.32 MB", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "monitorfiles": true, "monitorconnections": true, @@ -1535,9 +1537,7 @@ "monitorconsumption": true, "enabled": true, "interval": 30000 - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1683664161036", @@ -1557,13 +1557,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1683981346282", @@ -1592,11 +1592,11 @@ "text": "from-dido-controller", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "from-dido-controller" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1684055037116", @@ -1616,13 +1616,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1684060205000", @@ -1642,13 +1642,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1684179110403", @@ -1666,13 +1666,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1699963668903", @@ -1713,11 +1713,11 @@ "text": "", "color": "gray" }, + "color": "#2134B0", + "notes": "", "options": { "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" + } }, { "id": "1699964678894", @@ -1746,11 +1746,11 @@ "text": "modbus_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "modbus_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1699964793925", @@ -1770,13 +1770,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1699965957410", @@ -1825,9 +1825,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#2134B0", - "notes": "" + "notes": "", + "options": {} }, { "id": "1700411878636", @@ -1872,9 +1872,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#5CB36D", - "notes": "" + "notes": "", + "options": {} }, { "id": "1714752862828", @@ -1892,13 +1892,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1717441414646", @@ -1927,13 +1927,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n}", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1717442627834", @@ -1953,13 +1953,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1717442631338", @@ -1977,11 +1977,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1718016045116", @@ -2006,11 +2006,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1718016052341", @@ -2039,15 +2039,15 @@ "text": "Running", "color": "gray" }, + "color": "#30E193", + "notes": "", "options": { "slack_channel": "C071KN2Q8SK", "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "rvo_senica_35_10.0.0.129" - }, - "color": "#30E193", - "notes": "" + "name": "al_shariff_10.0.0.38" + } }, { "id": "1718016073501", @@ -2072,13 +2072,13 @@ "text": "", "color": "gray" }, - "options": { - "stringify": "json", - "method": "POST", - "url": "http://192.168.252.2:8004/slack" - }, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": { + "url": "http://192.168.252.2:8004/slack", + "method": "POST", + "stringify": "json" + } }, { "id": "1718016086212", @@ -2098,13 +2098,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1718016094070", @@ -2129,20 +2129,20 @@ "text": "", "color": "gray" }, - "options": { - "datatype": "object", - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", + "datatype": "object" + } }, { "id": "1729855334955", "component": "virtualwireout", "tab": "1612772287426", "name": "platform-rpc-call", - "x": 1062.933334350586, - "y": 532.3500061035156, + "x": 649.9333343505859, + "y": 541.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -2152,19 +2152,19 @@ "text": "platform-rpc-call", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "platform-rpc-call" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1729855371093", "component": "debug", "tab": "1612772287426", "name": "rpc cloud", - "x": 1063.933334350586, - "y": 442.3500061035156, + "x": 650.9333343505859, + "y": 451.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -2174,13 +2174,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1731068658334", @@ -2188,7 +2188,7 @@ "tab": "1612772287426", "name": "db-init", "x": 90.75, - "y": 247, + "y": 250, "connections": { "0": [ { @@ -2205,19 +2205,19 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731068754606", "component": "cloudmqttconnect", "tab": "1612772287426", "name": "MQTT client - to senica-prod01", - "x": 742.75, - "y": 520, + "x": 304.75, + "y": 474, "connections": { "1": [ { @@ -2235,18 +2235,18 @@ "output": [] }, "state": { - "text": "Connected", - "color": "green" + "text": "Reconnecting", + "color": "yellow" }, + "color": "#888600", + "notes": "", "options": { "username": "", "clientid": "", "port": "2764", "host": "192.168.252.2", - "topic": "u129" - }, - "color": "#888600", - "notes": "" + "topic": "" + } }, { "id": "1731069001548", @@ -2271,9 +2271,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#888600", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069033416", @@ -2291,11 +2291,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069059135", @@ -2320,9 +2320,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#888600", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069079243", @@ -2340,13 +2340,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1731069116691", @@ -2371,9 +2371,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069131637", @@ -2398,9 +2398,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069137374", @@ -2425,9 +2425,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069179846", @@ -2452,9 +2452,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069192937", @@ -2479,9 +2479,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069264443", @@ -2506,11 +2506,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069334626", @@ -2535,11 +2535,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069548145", @@ -2564,11 +2564,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069567152", @@ -2593,11 +2593,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731070156936", @@ -2622,11 +2622,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731234189516", @@ -2651,9 +2651,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731234189551", @@ -2678,9 +2678,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1732700042559", @@ -2709,9 +2709,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#888600", - "notes": "" + "notes": "", + "options": {} }, { "id": "1732700057052", @@ -2736,11 +2736,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1732700071298", @@ -2758,13 +2758,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1732700642917", @@ -2782,11 +2782,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1732889185927", @@ -2804,21 +2804,21 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1733574412965", "component": "virtualwirein", "tab": "1612772287426", "name": "db-init", - "x": 557.75, - "y": 574, + "x": 86.75, + "y": 495, "connections": { "0": [ { @@ -2835,38 +2835,12 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" - }, - { - "id": "1733574445563", - "component": "debug", - "tab": "1612772287426", - "name": "toCloudRado", - "x": 740.75, - "y": 435, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "options": { - "type": "data", - "repository": false, - "enabled": true - }, - "color": "#967ADC", - "notes": "" + } } ], - "version": 615 -} \ No newline at end of file + "version": 618 +} diff --git a/flow/notifikacie.csv b/flow/notifikacie.csv deleted file mode 100644 index 520c997..0000000 --- a/flow/notifikacie.csv +++ /dev/null @@ -1,31 +0,0 @@ -key;weight;en;sk -switching_profile_point_applied_to_line;INFO;Switching profile point applied to line no. ${line} : ${value};Aplikovaný bod spínacieho profilu na línií č. ${line} : ${value} -dusk_has_occured;INFO;Dusk has occured;Nastal súmrak -dawn_has_occured;INFO;Dawn has occured;Nastal úsvit -dimming_profile_was_successfully_received_by_node;NOTICE;Dimming profile was successfully received by node no. ${node};Stmievací profil bol úspešne prijatý nodom č. ${node} -master_node_is_responding_again;NOTICE;Master node is responding again;Master node začal znovu odpovedať -command_was_sent_from_terminal_interface;DEBUG;A command was sent from terminal interface;Z terminálu bol odoslaný príkaz -master_node_is_not_responding;ALERT;Master node is not responding;Master node neodpovedá -configuration_of_dimming_profile_to_node_failed;ALERT;Configuration of dimming profile to node no. ${node} has failed;Konfigurácia stmievacieho profilu pre node č. ${node} zlyhala -circuit_breaker_was_turned_on_line;NOTICE;Circuit breaker was turned on - line no. ${line};Zapnutie ističa na línii č. ${line} -circuit_breaker_was_turned_off_line;ERROR;Circuit breaker was turned off - line no. ${line};Vypnutie ističa na línií č. ${line} -dimming_profile_was_processed_for_node;INFO;Dimming profile was processed for node no. ${node};Stmievací profil bol spracovaný pre node č. ${node} -switching_profile_was_processed_for_line;INFO;Switching profile was processed for line no. ${line};Spínací profil bol spracovaný pre líniu č. ${line} -thermometer_is_not_responding;WARNING;Thermometer is not responding;Teplomer neodpovedá -thermometer_is_responding_again;NOTICE;Thermometer is responding again;Teplomer znovu odpovedá -thermometer_sends_invalid_data;WARNING;Thermometer sends invalid data;Teplomer posiela neplatné hodnoty -main_switch_has_been_turned_off;CRITICAL;Main switch has been turned off;Hlavný vypínač bol vypnutý -main_switch_has_been_turned_on;NOTICE;Main switch has been turned on;Hlavný vypínač bol zapnutý -power_supply_has_disconnected_input;ALERT;Power supply has disconnected input;Napájací zdroj nemá napätie na vstupe -power_supply_works_correctly;NOTICE;Power supply works correctly ;Napájací zdroj pracuje správne -battery_level_is_low;ERROR;Battery level is low;Batéria má nízku úroveň napätia -battery_level_is_ok;NOTICE;Battery level is OK;Batéria má správnu úroveň napätia -door_has_been_open;NOTICE;Door has been open;Dvere boli otvorené -door_has_been_closed;NOTICE;Door has been closed;Dvere boli zatvorené -door_has_been_open_without_permision_alarm_is_on;WARNING;Door has been open without permision - alarm is on;Dvere boli otvorené bez povolania - zapnutá siréna -state_of_contactor_for_line;INFO;State of contactor for line no. ${line} is ${value};Stav stýkača pre líniu č. ${line} je ${value} -local_database_is_corrupted;CRITICAL;; -electrometer_is_not_responding;ERROR;Electrometer is not responding;Elektromer neodpovedá -no_voltage_detected_on_phase;CRITICAL;No voltage detected on phase no. ${phase};Na fáze č. ${phase} nie je napätie -electrometer_is_responding_again;NOTICE;Electrometer is responding again;Elektromer znovu odpovedá -voltaga_on_phase_has_been_restored;NOTICE;Voltaga on phase no. ${phase} has been restored;Napätie na fáze č. ${phase} bolo obnovené diff --git a/flow/variables.txt b/flow/variables.txt deleted file mode 100644 index e69de29..0000000 diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index e11d9eb..b2a218d 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -5,7 +5,7 @@ exports.color = '#888600'; exports.version = '1.0.2'; exports.icon = 'sign-out'; exports.input = 2; -exports.output = 4; +exports.output = 3; exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" }; exports.html = `
@@ -209,19 +209,12 @@ exports.install = function(instance) { instance.send(SEND_TO.rpcCall, {"topic":topic, "content":message }); }); - client.on('close', function(err) { + client.on('close', function() { clientReady = false; wsmqtt_status = 'disconnected'; - - if (err && err.toString().indexOf('Error')) { - instance.status("Err: "+err.code, "red"); - instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); - } else { - instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !", "error":err, "opt":opts }); - } - - client.reconnect(); + + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !"}); }); client.on('error', function(err) { @@ -247,22 +240,16 @@ exports.install = function(instance) { if(clientReady) { - //do we have some data in backup file? - //if any, process data from database + //do we have some data in backup file? if any, process data from database if(saveTelemetryOnError) { //read telemetry data and send back to server if(!processingData) processDataFromDatabase(); } - } - - if(clientReady) - { + let stringifiedJson = JSON.stringify(data.data); client.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1}); - instance.send(3, stringifiedJson); - //backup telemetry if(createTelemetryBackup) { @@ -285,7 +272,6 @@ exports.install = function(instance) { } else { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); @@ -298,7 +284,6 @@ exports.install = function(instance) { data.data.id = UID(); nosql.insert(data.data); } - } }); @@ -445,12 +430,10 @@ exports.install = function(instance) { try { - let o = JSON.parse(JSON.stringify(item)); - delete o.id; - let message = JSON.stringify(o); + let message = JSON.parse(JSON.stringify(item)); + delete message.id; - client.publish("v1/gateway/telemetry", message, {qos:1}); - instance.send(3, message); + client.publish("v1/gateway/telemetry", JSON.stringify(message), {qos:1}); //remove from database await promisifyBuilder(nosql.remove().where("id", id)); @@ -460,7 +443,6 @@ exports.install = function(instance) { console.log("processDataFromDatabase", error); } - } } diff --git a/package.json b/package.json index e524d7c..7f7c795 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "easy-crc": "0.0.2", "jsmodbus": "^4.0.6", "log4js": "^6.3.0", + "moment": "^2.30.1", "mqtt": "^4.2.6", "nodemailer": "^6.9.7", "serialport": "^9.2.8", From 63504df84d8064bf315416ce0dfc69fb1708e2ce Mon Sep 17 00:00:00 2001 From: rasta5man Date: Thu, 9 Jan 2025 14:55:53 +0100 Subject: [PATCH 12/25] Do not send 'switching profile applied to line' notification; Handle nok,ok,offline node status --- flow/cmd_manager.js | 56 ++-- flow/dido_controller.js | 584 +++++++++++++++++----------------------- 2 files changed, 277 insertions(+), 363 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index a9376a5..6e0bd42 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -140,7 +140,7 @@ exports.install = function(instance) { //helper container for counting resolved group of commands (commands related to set profile) let cmdCounter = {};//key is node, value is counter - + //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice const nodeProfileSendFail = new Set(); @@ -1538,7 +1538,7 @@ exports.install = function(instance) { value == 1 ? onOrOff = "on" : onOrOff = "off"; turnLine(onOrOff, params.line, info); - + interval = setInterval(runTasks, SHORT_INTERVAL); return; } @@ -1590,27 +1590,35 @@ exports.install = function(instance) { return; } } + + let contactorStatus = 1; + if(relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; + + if (line == 0 || contactorStatus == 0) { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } - //TODO -> status offline for rvo if rotary_switch_state is OFF - //let relayStatus = 1; - //if (relaysData[line] != undefined) { - // relayStatus = relaysData[line].contactor; - //} + // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors + // + // let relayStatus = 1; + // if (relaysData[line] != undefined) { + // relayStatus = relaysData[line].contactor; + // } + // if (line == 0) relayStatus = 0; + // if (type == "cmd-terminal") relayStatus = 1; - //if (line == 0) relayStatus = 0; - //if (type == "cmd-terminal") relayStatus = 1; + // //check if rotary_switch_state == "Off" + // if (relayStatus == 0) { + // console.log("------------------------------------relayStatus", relayStatus, line); + // let values = { "status": "OFFLINE" }; - //check if rotary_switch_state == "Off" - //if (relayStatus == 0) { - //console.log("------------------------------------relayStatus", relayStatus, line); - //let values = { "status": "OFFLINE" }; + // if(tbname) sendTelemetry(values, tbname) - //if(tbname) sendTelemetry(values, tbname) - - //interval = setInterval(runTasks, SHORT_INTERVAL); - //return; - //} + // interval = setInterval(runTasks, SHORT_INTERVAL); + // return; + // } if (!rsPort.isOpen) { interval = setInterval(runTasks, LONG_INTERVAL); @@ -1806,7 +1814,7 @@ exports.install = function(instance) { } else { - if(currentTask.debug) { + if (currentTask.debug) { // currentTask.timestamp <= currentTimestamp && logger.debug("currentTask is not processed - task is in the future", currentTask); } @@ -1844,11 +1852,11 @@ exports.install = function(instance) { if (type == "set_node_profile") { delete cmdCounter[node]; logger.debug("profil nebol úspešne odoslaný na node č. ", params); - - if(!nodeProfileSendFail.has(node)) { - sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); - nodeProfileSendFail.add(node); - } + + if (!nodeProfileSendFail.has(node)) { + sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); + nodeProfileSendFail.add(node); + } } if (itIsNodeCommand) { diff --git a/flow/dido_controller.js b/flow/dido_controller.js index e9379ea..3a24f87 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -92,15 +92,15 @@ const SEND_TO = { exports.install = function(instance) { - process.on('uncaughtException', function (err) { + process.on('uncaughtException', function(err) { //TODO send to service - + errLogger.error('uncaughtException:', err.message) errLogger.error(err.stack); errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); - + //process.exit(1); }) @@ -133,7 +133,7 @@ exports.install = function(instance) { "16": {tbname: "", type: "twilight_sensor", "line": 0}, // twilight_sensor = pin16 }; */ - + //status for calculating Statecodes let deviceStatus = { //key is device name: temperature,.... "state_of_main_switch": "Off", //Hlavný istič @@ -163,16 +163,14 @@ exports.install = function(instance) { tbHandler.setSender(exports.title); controller_type = SETTINGS.controller_type //"lm" or "unipi" //logicMachine - if(controller_type == "") controller_type = "lm"; + if (controller_type == "") controller_type = "lm"; console.log(exports.title, "controller type: ", controller_type); - if(controller_type === "lm") - { + if (controller_type === "lm") { handleRsPort(); } - else if(controller_type === "unipi") - { + else if (controller_type === "unipi") { handleWebSocket(); } else { @@ -181,35 +179,29 @@ exports.install = function(instance) { } - function initialSetting() - { + function initialSetting() { //force turn off relays let keys = Object.keys(pinsData); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; - let line = pinsData[key].line; + let line = pinsData[key].line; - if(line != undefined) - { - if(relaysData[line] != undefined) - { + if (line != undefined) { + if (relaysData[line] != undefined) { pinsData[key].tbname = relaysData[line].tbname; //relaysData[line].contactor = 0; } - else - { + else { errLogger.error("CRITICAL!!! undefined relay", relaysData[line], line); - sendNotification("set port ", rvoTbName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance ); + sendNotification("set port ", rvoTbName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance); } } - if(pinsData[key].type == "state_of_contactor") - { + if (pinsData[key].type == "state_of_contactor") { let pin = key - 1; - if(controller_type === "unipi") pin = key; - + if (controller_type === "unipi") pin = key; + //this will modify database let forceTurnOff = true; turnLine("off", line, pin, forceTurnOff, "turn off on startup"); @@ -223,9 +215,9 @@ exports.install = function(instance) { sendTelemetry(values, rvoTbName); - let time = 5*1000; - setTimeout(function(){ - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"}); + let time = 5 * 1000; + setTimeout(function() { + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "buildTasks" }); sendNotification("rsPort.open()", rvoTbName, "flow_start", {}, "", SEND_TO.tb, instance); monitor.info("-->FLOW bol spustený", rvoTbName, SETTINGS.edge_fw_version); @@ -233,27 +225,26 @@ exports.install = function(instance) { } - function handleRsPort() - { + function handleRsPort() { //TODO build according to pins!!! //! rsPort to open are the same for lm and unipi and electromer ("/dev/ttymxc0") - const setRSPortData = [0xAA,6,6,6,6,6,6,0,6,6,6,1,1,1,1,0,0,10,10,10,10,10,10,0,10,10,10,0,0,0,0,0,0,5,0,0,0,15,15,15,15,15,15,0,15,15,15,0,0,0,0,0,0,30,0,0,0]; + const setRSPortData = [0xAA, 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 1, 1, 1, 1, 0, 0, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0]; rsPort = new SerialPort("/dev/ttymxc0", { autoOpen: false }); rsPort.on('error', function(err) { logger.debug("rsPort opened error - failed", err.message); instance.send(SEND_TO.debug, err.message); - errorHandler.sendMessageToService( exports.title + " rsPort opened error - failed: " + err.message); + errorHandler.sendMessageToService(exports.title + " rsPort opened error - failed: " + err.message); }) rsPort.on('open', async function() { - - await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function (status) { + + await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function(status) { //set port rsPort.write(Buffer.from(setRSPortData), function(err) { - if(!err) { + if (!err) { monitor.info(exports.title + "--->Digital in_out has been set (runSyncExec was sucessfull)"); turnAlarm("off"); @@ -265,48 +256,48 @@ exports.install = function(instance) { errLogger.error(exports.title + " runSyncExec - promise rejected:" + reason); errorHandler.sendMessageToService(exports.title + " runSyncExec - promise rejected:" + reason); }); - + }); - + rsPort.on('data', function(data) { - + rsPortReceivedData = [...rsPortReceivedData, ...data]; - + if (rsPortReceivedData[0] != 85) { rsPortReceivedData = []; return; } - + let l = rsPortReceivedData.length; - - if (l < 4 ) return; - - if (l > 4 ) { - + + if (l < 4) return; + + if (l > 4) { + // if array length is greater than 4, we take first 4 byte and do the logic, second 4 bytes, do the logic and so on let i, j, temparray, chunk = 4; - for ( i = 0, j = l; i < j; i += chunk ) { - temparray = rsPortReceivedData.slice(i, i + chunk); - - if ( temparray.length < 4 ){ - rsPortReceivedData = [...temparray]; - return; - } - - switchLogic(temparray); + for (i = 0, j = l; i < j; i += chunk) { + temparray = rsPortReceivedData.slice(i, i + chunk); + + if (temparray.length < 4) { + rsPortReceivedData = [...temparray]; + return; + } + + switchLogic(temparray); } - + rsPortReceivedData = []; return; } - + switchLogic(rsPortReceivedData); - + rsPortReceivedData = []; - + }); - + rsPort.on("close", () => { rsPort.close(); }) @@ -324,62 +315,62 @@ exports.install = function(instance) { ws = new WebSocket('ws:/0.0.0.0:1234/ws'); ws.onopen = function open() { - + instance.send(0, exports.title + " running"); turnAlarm("off"); // useTurnOffCounter = true; // turnOffCounter = relaysData.length - 1; initialSetting(); - ws.send(JSON.stringify({"cmd":"all"})); + ws.send(JSON.stringify({ "cmd": "all" })); // we request dev info about neuron device from evok to keep websocket connection alive // for some reason this request returns no data, but connection keeps alive // https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket startRequests = setInterval(() => { // console.log(" *** data from evok requested"); - ws.send(JSON.stringify({"cmd":"filter", "dev": ["neuron"]})); + ws.send(JSON.stringify({ "cmd": "filter", "dev": ["neuron"] })); }, 150000) }; - -// SAMPLE DATA FROM WEBSOCKET -// { -// glob_dev_id: 1, -// modes: [ 'Simple' ], -// value: 0, -// circuit: '1_07', -// pending: false, -// relay_type: 'physical', -// dev: 'relay', -// mode: 'Simple' -// }, -// { -// counter_modes: [ 'Enabled', 'Disabled' ], -// glob_dev_id: 1, -// modes: [ 'Simple', 'DirectSwitch' ], -// value: 0, -// circuit: '1_08', -// debounce: 50, -// counter: 0, -// counter_mode: 'Enabled', -// dev: 'input', -// mode: 'Simple' -// }, + + // SAMPLE DATA FROM WEBSOCKET + // { + // glob_dev_id: 1, + // modes: [ 'Simple' ], + // value: 0, + // circuit: '1_07', + // pending: false, + // relay_type: 'physical', + // dev: 'relay', + // mode: 'Simple' + // }, + // { + // counter_modes: [ 'Enabled', 'Disabled' ], + // glob_dev_id: 1, + // modes: [ 'Simple', 'DirectSwitch' ], + // value: 0, + // circuit: '1_08', + // debounce: 50, + // counter: 0, + // counter_mode: 'Enabled', + // dev: 'input', + // mode: 'Simple' + // }, ws.onmessage = async function(data) { data = JSON.parse(data.data); // data comes in array except of "temperature" ==> it comes as an object // we do not handle temperature from evok any more => we return, if temperature comes: - if(isObject(data)) return; + if (isObject(data)) return; data.map(item => { let value = item['value']; let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01" - if(pin == undefined) return; + if (pin == undefined) return; switchLogic(pin, value); }) } @@ -394,7 +385,7 @@ exports.install = function(instance) { }) - ws.onclose = function(){ + ws.onclose = function() { // connection closed, discard old websocket and create a new one in 5s // stopRequests(); monitor.info('websocket onclose, reconnect') @@ -406,23 +397,20 @@ exports.install = function(instance) { } instance.on("close", () => { - if(rsPort) rsPort.close(); - if(ws) ws.close(); + if (rsPort) rsPort.close(); + if (ws) ws.close(); }) - function getPin(line) - { + function getPin(line) { //conversionTable let keys = Object.keys(pinsData); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; - if(pinsData[key].type == "state_of_contactor" && pinsData[key].line == line) - { - if(rsPort) return key - 1; - if(ws) return key; + if (pinsData[key].type == "state_of_contactor" && pinsData[key].line == line) { + if (rsPort) return key - 1; + if (ws) return key; } } @@ -432,18 +420,16 @@ exports.install = function(instance) { } - function turnAlarm(onOrOff) - { + function turnAlarm(onOrOff) { let value = 0; - if(onOrOff == "on") value = 1; + if (onOrOff == "on") value = 1; - if(value == 1 && SETTINGS.maintenance_mode) return; + if (value == 1 && SETTINGS.maintenance_mode) return; alarmStatus = "OFF"; - if(value == 1) alarmStatus = "ON"; + if (value == 1) alarmStatus = "ON"; - if(rsPort) - { + if (rsPort) { let arr = [0x55]; arr.push(13); arr.push(0); @@ -453,29 +439,25 @@ exports.install = function(instance) { logger.debug(`sirena - ${onOrOff}`); }); } - else if(ws) - { - let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": value}; + else if (ws) { + let cmd = { "cmd": "set", "dev": "relay", "circuit": "1_01", "value": value }; ws.send(JSON.stringify(cmd)); logger.debug(`sirena - ${onOrOff}`); } } - function reportLineStatus(line) - { + function reportLineStatus(line) { //Tá hodnota by mala fungovať tak že LSB bit číslo je stav ističa (1 - On, 0 - Off) a druhý bit je stav stýkača (1 - true, 0 - false). let tbname = relaysData[line].tbname; let bits = []; - if(deviceStatus["state_of_breaker"][line] == "On") - { + if (deviceStatus["state_of_breaker"][line] == "On") { bits.push(0); } else bits.push(1); - if(deviceStatus["state_of_contactor"][line] == "On") - { + if (deviceStatus["state_of_contactor"][line] == "On") { bits.push(0); } else bits.push(1); @@ -485,90 +467,80 @@ exports.install = function(instance) { let byte = bitwise.byte.write(bits.reverse()); //console.log("line", line, bits, byte); - sendTelemetry({statecode: byte}, tbname); + sendTelemetry({ statecode: byte }, tbname); } // turn line on or off - function turnLine(onOrOff, line, pin, force, info) - { + function turnLine(onOrOff, line, pin, force, info) { //onOrOff => "on" or "off" let value = 0; - if(onOrOff == "on") value = 1; - - if(force == undefined) force = false; + if (onOrOff == "on") value = 1; - if(line == 0) - { - if(value == 1 && alarmStatus == "ON") turnAlarm("off"); - SETTINGS.maintenance_mode = value? true: false; + if (force == undefined) force = false; + + if (line == 0) { + if (value == 1 && alarmStatus == "ON") turnAlarm("off"); + SETTINGS.maintenance_mode = value ? true : false; let values = {}; values["statecode"] = calculateStateCode(); - values["power_mode"] = value ? "maintenance": "Automatic"; + values["power_mode"] = value ? "maintenance" : "Automatic"; sendTelemetry(values, rvoTbName); - + monitor.info(`turnLine ${onOrOff} - (line, SETTINGS.maintenance_mode)`, line, SETTINGS.maintenance_mode, info); return; } - if(pin === undefined) pin = getPin(line); + if (pin === undefined) pin = getPin(line); - if(pin === undefined) - { + if (pin === undefined) { errLogger.error("pin is undefined!", line); return; } - - if(!force) - { - if(relaysData[line].contactor == value) - { - instance.send(SEND_TO.debug, `line is already ${onOrOff} ` + line ); + + if (!force) { + if (relaysData[line].contactor == value) { + instance.send(SEND_TO.debug, `line is already ${onOrOff} ` + line); logger.debug(`turnLine: line is already ${onOrOff} `, line); return; } } // if(!rsPort.isOpen && !ws) - if(!rsPort && !ws) - { + if (!rsPort && !ws) { errLogger.error("dido controller - port or websocket is not opened"); return; } - - if(rsPort) - { + + if (rsPort) { let arr = [0x55]; arr.push(pin); arr.push(0); arr.push(value); - + rsPort.write(Buffer.from(arr), function(err) { - if(err === undefined) - { + if (err === undefined) { monitor.info(`turnLine ${onOrOff} zapisal do rsPort-u`, line, pin, arr, info); switchLogic(arr); } - else - { + else { monitor.info(`turnLine ${onOrOff} WRITE error`, err); - } + } }); } - else if(ws) - { + else if (ws) { //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method monitor.info(`turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); - let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value}; + let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value }; ws.send(JSON.stringify(cmd)); switchLogic(pin, value) } - + //if rvo is 24/7, it has just one switching profile point at 13:00. we do not want to send notification as it repeats every day. - const d = new Date(); - if(d.getHours() != 13) sendNotification("Dido_controller: ", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", { line: line, value: onOrOff }, "", SEND_TO.tb, instance); + //const d = new Date(); + //if(d.getHours() != 13) sendNotification("Dido_controller: ", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", { line: line, value: onOrOff }, "", SEND_TO.tb, instance); } @@ -581,45 +553,37 @@ exports.install = function(instance) { //data from modbus_reader or temperature sensor or twilight sensor or other modbus device instance.on("0", flowdata => { - if(!isObject(flowdata.data)) return; + if (!isObject(flowdata.data)) return; // console.log('***********************', flowdata.data) instance.send(SEND_TO.debug, flowdata.data); // we handle nok status from modbus_reader component and thermometer - if("status" in flowdata.data) - { + if ("status" in flowdata.data) { const status = flowdata.data.status; - if(status == "NOK-twilight_sensor") - { + if (status == "NOK-twilight_sensor") { deviceStatus["twilight_sensor"] = "NOK"; } - else if(status == "NOK-em340" || status == "NOK-em111") - { + else if (status == "NOK-em340" || status == "NOK-em111") { deviceStatus["em"] = "NOK"; } - else if(status == "NOK-thermometer") - { + else if (status == "NOK-thermometer") { deviceStatus["temperature"] = "NOK"; } } - else if("values" in flowdata.data) - { + else if ("values" in flowdata.data) { const values = flowdata.data.values; - if(values.hasOwnProperty("twilight_sensor")) - { - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]}); + if (values.hasOwnProperty("twilight_sensor")) { + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"] }); deviceStatus["twilight_sensor"] = "OK" } - else if(values.hasOwnProperty("temperature")) - { + else if (values.hasOwnProperty("temperature")) { deviceStatus["temperature"] = "OK"; } // EM - else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current")) - { + else if (values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current")) { deviceStatus["em"] = "OK"; - SETTINGS.no_voltage.size > 0 ? deviceStatus["no_voltage"] = "NOK": deviceStatus["no_voltage"] = "OK"; + SETTINGS.no_voltage.size > 0 ? deviceStatus["no_voltage"] = "NOK" : deviceStatus["no_voltage"] = "OK"; } sendTelemetry(values, rvoTbName); @@ -634,161 +598,137 @@ exports.install = function(instance) { console.log(flowdata.data); - if(!flowdata.data instanceof Object) return; + if (!flowdata.data instanceof Object) return; let obj = flowdata.data; let line = obj.line; let force = obj.force; let info = obj.info; - if(obj.command == "on") turnLine("on", line, undefined, force, info); - else if(obj.command == "off") turnLine("off", line, undefined, force, info); - else if(obj.command == "turnOnAlarm") turnAlarm("on"); - else if(obj.command == "turnOffAlarm") turnAlarm("off"); + if (obj.command == "on") turnLine("on", line, undefined, force, info); + else if (obj.command == "off") turnLine("off", line, undefined, force, info); + else if (obj.command == "turnOnAlarm") turnAlarm("on"); + else if (obj.command == "turnOffAlarm") turnAlarm("off"); //! ake data prichadzaju z cmd_manager.js ??? //TODO transform to websocket - if (Array.isArray(obj)){ + if (Array.isArray(obj)) { rsPort.write(Buffer.from(obj), function(err) { switchLogic(obj); - instance.send(SEND_TO.debug, {"WRITE":obj} ); + instance.send(SEND_TO.debug, { "WRITE": obj }); }); - } + } }) - function calculateStateCode() - { + function calculateStateCode() { let bits = []; //Hlavný istič - state_of_main_switch => v rvo senica je to druhy door pre silovu cast (EM) - if(deviceStatus["state_of_main_switch"] == "closed") - { + if (deviceStatus["state_of_main_switch"] == "closed") { bits.push(0); } - else - { + else { bits.push(1); } //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY - if(!SETTINGS.maintenance_mode) - { - if(deviceStatus["rotary_switch_state"] == "Manual") - { + if (!SETTINGS.maintenance_mode) { + if (deviceStatus["rotary_switch_state"] == "Manual") { bits.push(0); bits.push(1); } - if(deviceStatus["rotary_switch_state"] == "Automatic") - { + if (deviceStatus["rotary_switch_state"] == "Automatic") { bits.push(0); bits.push(0); } - if(deviceStatus["rotary_switch_state"] == "Off") - { + if (deviceStatus["rotary_switch_state"] == "Off") { bits.push(1); bits.push(0); } } - else{ + else { bits.push(1); bits.push(1); } //Dverový kontakt - if(deviceStatus["door_condition"] == "closed") - { + if (deviceStatus["door_condition"] == "closed") { bits.push(0); } - else - { + else { bits.push(1); } //EM - if(deviceStatus["em"] == "NOK") - { + if (deviceStatus["em"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } //Teplomer - if(deviceStatus["temperature"] == "NOK") - { + if (deviceStatus["temperature"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } //Batéria - if(deviceStatus["battery"] == "NOK") - { + if (deviceStatus["battery"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } //Zdroj - if(deviceStatus["power_supply"] == "NOK") - { + if (deviceStatus["power_supply"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } //MN - if(deviceStatus["master_node"] == "NOK") - { + if (deviceStatus["master_node"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } //výpadok napätia na fáze - if(deviceStatus["no_voltage"] == "NOK") - { + if (deviceStatus["no_voltage"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } - if(deviceStatus["twilight_sensor"] == "NOK") - { + if (deviceStatus["twilight_sensor"] == "NOK") { bits.push(1); } - else - { + else { bits.push(0); } - + // doplnime do 16 bitov (2 byty) - for(let i = bits.length; i < 16; i++) - { + for (let i = bits.length; i < 16; i++) { bits.push(0); } - + // console.log("calculateStateCode - deviceStatus", deviceStatus); // console.log("calculateStateCode", bits); - let byte0 = bitwise.byte.write(bits.slice(0,8).reverse()); + let byte0 = bitwise.byte.write(bits.slice(0, 8).reverse()); let byte1 = bitwise.byte.write(bits.slice(8).reverse()); let byte = bytesToInt([byte1, byte0]); @@ -801,7 +741,7 @@ exports.install = function(instance) { async function sendRvoStatus() { - if(SETTINGS === undefined) return; + if (SETTINGS === undefined) return; const table = { "OK": 1, @@ -836,13 +776,12 @@ exports.install = function(instance) { let status = "OK"; for (const [key, value] of Object.entries(deviceStatus)) { - if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; + if (["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; } - if(status == "OK") - { + if (status == "OK") { let pinIndexes = [1, 4, 6]; - if(controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05']; + if (controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05']; for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { @@ -855,10 +794,10 @@ exports.install = function(instance) { // battery status. If value is 1 - battery is NOK if (previousValues[5] === 1) status = "NOK"; - if(!SETTINGS.masterNodeIsResponding) status = "NOK"; - if(SETTINGS.no_voltage.size > 0) status = "NOK"; + if (!SETTINGS.masterNodeIsResponding) status = "NOK"; + if (SETTINGS.no_voltage.size > 0) status = "NOK"; - // console.log("rvo status",status) + // console.log("rvo status",status) return status; } @@ -871,23 +810,20 @@ exports.install = function(instance) { let pinIndex, newPinValue, twilight; //data from rsPort - if(args.length == 1) - { + if (args.length == 1) { pinIndex = args[0][1] + 1; if (pinIndex === 17) pinIndex--; newPinValue = args[0][3]; twilight = args[0][2]; } //data from websocket - else - { + else { pinIndex = args[0]; newPinValue = args[1]; } let obj = pinsData[pinIndex]; - if(obj == undefined) - { + if (obj == undefined) { previousValues[pinIndex] = newPinValue; //logger.debug("dido-switchLogic ==> no pinIndex", pinIndex); return; @@ -899,8 +835,8 @@ exports.install = function(instance) { let tbname = obj.tbname; //default value - let value = "On"; - if(newPinValue === 0) value = "Off"; + let value = "On"; + if (newPinValue === 0) value = "Off"; //Hlavný istič //! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri @@ -923,31 +859,26 @@ exports.install = function(instance) { // } //Prevádzkový mód - if(type == "rotary_switch_state") - { + if (type == "rotary_switch_state") { // combination of these two pins required to get result let pin2, pin3; - if(pinIndex == 2 || pinIndex == "input1_02") - { + if (pinIndex == 2 || pinIndex == "input1_02") { pin2 = newPinValue; pin3 = previousValues[3] || previousValues["input1_03"]; - if(pin3 == undefined) - { + if (pin3 == undefined) { previousValues[pinIndex] = newPinValue; return; - } + } } - else if(pinIndex == 3 || pinIndex == "input1_03") - { + else if (pinIndex == 3 || pinIndex == "input1_03") { pin3 = newPinValue; pin2 = previousValues[2] || previousValues["input1_02"]; - if(pin2 == undefined) - { + if (pin2 == undefined) { previousValues[pinIndex] = newPinValue; return; - } + } } //console.log('***********************', pin2, pin3) @@ -961,22 +892,19 @@ exports.install = function(instance) { //ak je spracovany, a automatic - tak ho zapnem //ak nie je spracovany, iba profil zapisem - if(pin2 != undefined && pin3 != undefined) instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value}); + if (pin2 != undefined && pin3 != undefined) instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "rotary_switch_state", value: value }); //console.log("rotary_switch_state pin", pin2, pin3, value); } //Zdroj - pin 4 - else if (type === "power_supply") - { - if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) - { + else if (type === "power_supply") { + if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", rvoTbName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply"); deviceStatus["power_supply"] = "NOK"; } - else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) - { + else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", rvoTbName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply"); deviceStatus["power_supply"] = "OK"; @@ -984,16 +912,13 @@ exports.install = function(instance) { } //Batéria - pin 5 - else if (type === "battery") - { - if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) - { + else if (type === "battery") { + if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", rvoTbName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level"); deviceStatus["battery"] = "NOK"; } - else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) - { + else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", rvoTbName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level"); deviceStatus["battery"] = "OK"; @@ -1003,27 +928,23 @@ exports.install = function(instance) { //Dverový kontakt - pin 6 //! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch" //! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition' - else if(type == "door_condition" || type === "state_of_main_switch") - { + else if (type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; - if (value === "open" && SETTINGS.maintenance_mode) - { + if (value === "open" && SETTINGS.maintenance_mode) { sendNotification("switchLogic", rvoTbName, "door_opened", {}, "", SEND_TO.tb, instance, "rvo_door"); } - if (value === "open" && !SETTINGS.maintenance_mode) - { + if (value === "open" && !SETTINGS.maintenance_mode) { sendNotification("switchLogic", rvoTbName, "door_opened_without_permission", {}, "", SEND_TO.tb, instance, "rvo_door"); // zapneme sirenu // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition") - if(type === "door_condition") turnAlarm("on"); + if (type === "door_condition") turnAlarm("on"); } - if (value === "closed") - { - if(alarmStatus == "ON") turnAlarm("off"); + if (value === "closed") { + if (alarmStatus == "ON") turnAlarm("off"); sendNotification("switchLogic", rvoTbName, "door_closed", {}, "", SEND_TO.tb, instance, "rvo_door"); } @@ -1031,59 +952,53 @@ exports.install = function(instance) { } //lux sensor - else if(type == "twilight_sensor") - { + else if (type == "twilight_sensor") { //! TODO - to show nok status, if lux value is not changing more then 10 times. //Daylight is far more than 1000. So most of the day, when it is sunshine comes just value 1000. But lux sensor is not NOK. //This is not the case in LM. If value from LM is the same 10x, there is 99% possibility, that sensor is NOK. value = newPinValue; - if(controller_type === 'lm') - { - value = parseFloat(newPinValue + (256*twilight)); + if (controller_type === 'lm') { + value = parseFloat(newPinValue + (256 * twilight)); let now = new Date(); //new Date(dusk.getTime() - let obj = {timestamp: now.getTime(), value: value}; + let obj = { timestamp: now.getTime(), value: value }; //test //twilight_sensor_interval = 1; twilight_sensor.push(obj); //twilight_sensor_array.push(value); - + //check if we receive just 1 constant value from lux sensor ==> error - if(twilight_sensor_array.length > 10) { + if (twilight_sensor_array.length > 10) { let set = new Set(twilight_sensor_array); - if(set.size === 1 && !twilightError) - { + if (set.size === 1 && !twilightError) { twilightError = true; let value = twilight_sensor_array.shift(); newPinValue = 0; } - else if (set.size !== 1 && twilightError) - { + else if (set.size !== 1 && twilightError) { //sendNotification("switchLogic", rvoTbName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance ); twilightError = false; twilight_sensor_array.shift(); newPinValue = value; } - else if (set.size === 1 && twilightError) - { + else if (set.size === 1 && twilightError) { twilight_sensor_array.shift(); newPinValue = 0; } } - let diff = twilight_sensor[ twilight_sensor.length - 1 ].timestamp - twilight_sensor[0].timestamp; - if(diff >= twilight_sensor_interval * 60 * 1000) - { + let diff = twilight_sensor[twilight_sensor.length - 1].timestamp - twilight_sensor[0].timestamp; + if (diff >= twilight_sensor_interval * 60 * 1000) { const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length; - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: average}); + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "lux_sensor", value: average }); twilight_sensor = []; @@ -1093,27 +1008,25 @@ exports.install = function(instance) { } } - else if(type == "state_of_contactor") - { - if(!(deviceStatus["state_of_contactor"][line] == value)) - { - sendNotification("switchLogic", rvoTbName, "state_of_contactor_for_line", {line: line, value: value}, "", SEND_TO.tb, instance ); + else if (type == "state_of_contactor") { + if (!(deviceStatus["state_of_contactor"][line] == value)) { + sendNotification("switchLogic", rvoTbName, "state_of_contactor_for_line", { line: line, value: value }, "", SEND_TO.tb, instance); } deviceStatus["state_of_contactor"][line] = value; //true, false - if(value === "On") value = true; - else if(value === "Off") value = false; + if (value === "On") value = true; + else if (value === "Off") value = false; //TODO do we need to modify relays table with contactor value, if we do not use it on startup ?? let dataChanged = false; - if(relaysData[line].contactor !== newPinValue) { + if (relaysData[line].contactor !== newPinValue) { dataChanged = true; relaysData[line].contactor = newPinValue; } - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, value: value, dataChanged: dataChanged}); + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "reload_relays", line: line, value: value, dataChanged: dataChanged }); reportLineStatus(line); //modify table relays @@ -1145,29 +1058,25 @@ exports.install = function(instance) { // }); } - else if(type === "state_of_breaker") - { + else if (type === "state_of_breaker") { let valueChanged = false; - if(newPinValue != previousValues[pinIndex]) valueChanged = true; + if (newPinValue != previousValues[pinIndex]) valueChanged = true; - if(valueChanged) - { - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line}); + if (valueChanged) { + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line }); //mame iba 3 istice. vyreportujeme a ohandlujeme liniu na tom istom istici ako paralelna linia (napr linia 1, paralelna s nou je linia 4, key je string "4") // ak je 7 linii, na 1 istici je linia 1,4,7 - if(line == 1) - { + if (line == 1) { - const lineOnSameBraker = [4,7]; + const lineOnSameBraker = [4, 7]; - for (var i = 0; i < lineOnSameBraker.length; i++) - { - if(!relaysData.hasOwnProperty(lineOnSameBraker[i])) continue; + for (var i = 0; i < lineOnSameBraker.length; i++) { + if (!relaysData.hasOwnProperty(lineOnSameBraker[i])) continue; - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: lineOnSameBraker[i]}); + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "state_of_breaker", value: value, line: lineOnSameBraker[i] }); deviceStatus["state_of_breaker"][lineOnSameBraker[i]] = value; reportLineStatus(lineOnSameBraker[i]); @@ -1175,18 +1084,16 @@ exports.install = function(instance) { values[type] = value; const tbname = relaysData[lineOnSameBraker[i]].tbname; sendTelemetry(values, tbname); - + delete values[type]; } } - else - { + else { const lineOnSameBraker = line + 3 + ""; - if(relaysData.hasOwnProperty(lineOnSameBraker)) - { - instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line + 3}); + if (relaysData.hasOwnProperty(lineOnSameBraker)) { + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line + 3 }); deviceStatus["state_of_breaker"][line + 3] = value; reportLineStatus(line + 3); @@ -1194,14 +1101,14 @@ exports.install = function(instance) { values[type] = value; const tbname = relaysData[lineOnSameBraker].tbname; sendTelemetry(values, tbname); - + delete values[type]; } } } - if(value == "Off") values["status"] = "NOK"; + if (value == "Off") values["status"] = "NOK"; deviceStatus["state_of_breaker"][line] = value; reportLineStatus(line); @@ -1210,19 +1117,18 @@ exports.install = function(instance) { values[type] = value; - if(type == "rotary_switch_state") - { - if(SETTINGS.maintenance_mode) value = "maintenance"; + if (type == "rotary_switch_state") { + if (SETTINGS.maintenance_mode) value = "maintenance"; value = value.toLowerCase(); values["power_mode"] = value; } - if(newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue; - if(Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname); - } + if (newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue; + if (Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname); + } - function sendTelemetry(values, tbname, date=Date.now()) { + function sendTelemetry(values, tbname, date = Date.now()) { let dataToTb = { [tbname]: [ { @@ -1236,7 +1142,7 @@ exports.install = function(instance) { } - function isObject (item) { + function isObject(item) { return (typeof item === "object" && !Array.isArray(item) && item !== null); } From 613d846dbe17284d7a50be267357c41d1a32d1b9 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Thu, 9 Jan 2025 22:21:50 +0100 Subject: [PATCH 13/25] Handle NOK master_node --- flow/cmd_manager.js | 42 ++++++++++++++++------------------------- flow/dido_controller.js | 4 ++-- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 6e0bd42..a960aaa 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -1239,7 +1239,7 @@ exports.install = function(instance) { params.address = 0; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = 5; - params.tbname = rvoTbName; + params.tbname = SETTINGS.rvoTbName; params.info = "Master node FW verzia"; //params.debug = true; @@ -1590,14 +1590,14 @@ exports.install = function(instance) { return; } } - - let contactorStatus = 1; - if(relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; - - if (line == 0 || contactorStatus == 0) { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } + + let contactorStatus = 1; + if (relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; + + if (line == 0 || contactorStatus == 0) { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors // @@ -1629,13 +1629,9 @@ exports.install = function(instance) { //set actual time for broadcast if (register == 87 && params.recipient === 2) { var d = new Date(); - let hours = d.getHours(); - let minutes = d.getMinutes(); - let seconds = d.getSeconds(); - - params.byte1 = hours;//h - params.byte2 = minutes;//m - params.byte3 = seconds;//s + params.byte1 = d.getHours();//h + params.byte2 = d.getMinutes();//m + params.byte3 = 0;//s params.byte4 = 0; } @@ -1645,11 +1641,8 @@ exports.install = function(instance) { if (type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); - let dusk_hours = sunCalcResult["dusk_hours"]; - let dusk_minutes = sunCalcResult["dusk_minutes"]; - - params.byte1 = dusk_hours;//h - params.byte2 = dusk_minutes;//m + params.byte1 = sunCalcResult["dusk_hours"];//h + params.byte2 = sunCalcResult["dusk_minutes"];//m params.byte3 = 0;//s params.byte4 = 0; @@ -1662,11 +1655,8 @@ exports.install = function(instance) { if (register == 7 && params.recipient === 2) { if (type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); - let dawn_hours = sunCalcResult["dawn_hours"]; - let dawn_minutes = sunCalcResult["dawn_minutes"]; - - params.byte1 = dawn_hours;//h - params.byte2 = dawn_minutes;//m + params.byte1 = sunCalcResult["dawn_hours"];//h + params.byte2 = sunCalcResult["dawn_minutes"];//m params.byte3 = 0;//s params.byte4 = 0; diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 3a24f87..8415921 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -742,6 +742,7 @@ exports.install = function(instance) { async function sendRvoStatus() { if (SETTINGS === undefined) return; + SETTINGS.masterNodeIsResponding ? deviceStatus["master_node"] = "OK" : deviceStatus["master_node"] = "NOK"; const table = { "OK": 1, @@ -776,7 +777,7 @@ exports.install = function(instance) { let status = "OK"; for (const [key, value] of Object.entries(deviceStatus)) { - if (["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK"; + if (["em", "twilight_sensor", "temperature", "master_node"].includes(key) && value == "NOK") status = "NOK"; } if (status == "OK") { @@ -794,7 +795,6 @@ exports.install = function(instance) { // battery status. If value is 1 - battery is NOK if (previousValues[5] === 1) status = "NOK"; - if (!SETTINGS.masterNodeIsResponding) status = "NOK"; if (SETTINGS.no_voltage.size > 0) status = "NOK"; // console.log("rvo status",status) From 75bb2794d2414962cdde7791026bbb54816a37da Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 13 Jan 2025 15:39:53 +0100 Subject: [PATCH 14/25] actual version before - turnOff on startup change --- flow/cmd_manager.js | 58 +++--------------------------- flow/count.js | 60 ++++++++++++++++++++++++++++++++ flow/db_init.js | 2 +- flow/dido_controller.js | 11 ++---- flow/helper/serialport_helper.js | 2 +- 5 files changed, 69 insertions(+), 64 deletions(-) create mode 100644 flow/count.js diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index a960aaa..280a5a6 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -118,11 +118,10 @@ exports.install = function(instance) { priorities["7"] = minutes; priorities["80"] = minutes; priorities["8"] = minutes; - priorities["3"] = minutes; priorities["89"] = minutes; //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) - let listOfCommands = [0, 1, 3, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 84, 87, 89]; + let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 84, 87, 89]; const errorHandler = new ErrorToServiceHandler(); @@ -133,7 +132,6 @@ exports.install = function(instance) { let relaysData; let nodesData; - let rvoTbName; let sunCalcResult; let reportDuskDawn; @@ -159,11 +157,7 @@ exports.install = function(instance) { tbHandler = new DataToTbHandler(SEND_TO.tb); tbHandler.setSender(exports.title); - //SETTINGS.project_id, name: SETTINGS.rvo_name; - //const errorHandler = new ErrorToServiceHandler(instance, SEND_TO.infoSender); errorHandler.setProjectsId(SETTINGS.project_id); - //const errorHandler = new ErrorToServiceHandler(instance); - //errorHandler.sendMessageToService("ahoj", 0); let now = new Date(); console.log("CMD Manager installed", now.toLocaleString("sk-SK")); @@ -225,7 +219,6 @@ exports.install = function(instance) { } params.addMinutesToTimestamp = 0;//repeat task if value is > 0, - //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" //params.info = ""; //params.debug = true; // will console.log params in writeData response @@ -340,13 +333,6 @@ exports.install = function(instance) { Register úrovne má rovnaký formát ako dimming register (Reg 1). */ - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - let start_time = obj.start_time; let t = start_time.split(":"); //if(timePoint != undefined) t = timePoint.split(":"); @@ -394,16 +380,6 @@ exports.install = function(instance) { } //Threshold lux level for DUSK/DAWN - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //params.byte1 = 0;//msb, podla dokumentacie data3 - //params.byte2 = 0;//podla dokumentacie data2 - //params.byte3 = 0;//podla dokumentacie data1 - //params.byte4 = 0;//lsb, podla dokumentacie data0 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - - //Time schedule settings na koniec - //if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor) { logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); @@ -673,16 +649,11 @@ exports.install = function(instance) { function reportOnlineNodeStatus(line) { - //broadcast cas, o 3 sek neskor - status, brightness - //Po zapnutí línie broadcastovo aktualizovať predtým čas. + //Po zapnutí línie broadcastovo aktualizovať predtým čas a o 3 sek neskor - status, brightness logger.debug("--->reportOnlineNodeStatus for line", line); - //return; - //run broadcast //Actual time - addMinutesToTimestamp = 0; - let params = {}; var d = new Date(); @@ -704,7 +675,7 @@ exports.install = function(instance) { //other values params.type = "cmd"; params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; + params.addMinutesToTimestamp = 0; params.info = "run broadcast: Actual time"; tasks.push(params); @@ -729,9 +700,7 @@ exports.install = function(instance) { sendTelemetry({ status: status }, tbname, time); - //prud, vykon - current, input power pre liniu pre vsetky nody - - //a pridame aj vyreportovanie dimmingu + //vyreportovanie dimmingu, prud, vykon - current, input power pre liniu pre vsetky nody { let params = getParams(PRIORITY_TYPES.high_priority); @@ -1655,7 +1624,7 @@ exports.install = function(instance) { if (register == 7 && params.recipient === 2) { if (type != "cmd-terminal") { let sunCalcResult = calculateDuskDawn(); - params.byte1 = sunCalcResult["dawn_hours"];//h + params.byte1 = sunCalcResult["dawn_hours"];//h params.byte2 = sunCalcResult["dawn_minutes"];//m params.byte3 = 0;//s params.byte4 = 0; @@ -1969,8 +1938,6 @@ exports.install = function(instance) { logger.debug("CMD manager - rsPort opened success"); - //loadRelaysData(); - await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); @@ -2801,21 +2768,6 @@ exports.install = function(instance) { values["time_schedule_settings"] = time_schedule_settings; } - //skupinová adresa 1 - if (register == 3) { - let gr_add_1 = bytesToInt(byte0); - values["gr_add_1"] = gr_add_1; - - let gr_add_2 = bytesToInt(byte1); - values["gr_add_2"] = gr_add_2; - - let gr_add_3 = bytesToInt(byte2); - values["gr_add_3"] = gr_add_3; - - let gr_add_4 = bytesToInt(byte3); - values["gr_add_4"] = gr_add_4; - } - //naklon if (register == 84) { let temp; diff --git a/flow/count.js b/flow/count.js new file mode 100644 index 0000000..fa92ee9 --- /dev/null +++ b/flow/count.js @@ -0,0 +1,60 @@ +exports.id = 'count'; +exports.title = 'Count'; +exports.version = '1.0.1'; +exports.author = 'John Graves'; +exports.color = '#656D78'; +exports.icon = 'plus-square'; +exports.input = 2; +exports.output = 1; +exports.options = { increment: 1, initialvalue: 1 }; +exports.readme = `# Counter + +Counter Number of times called.`; + +exports.html = `
+
@(Initial Value)
+
@(Increment)
+

Example Video

+
`; + +exports.readme = `# Count + +This component counts the number of messages received. + +__Response:__ + +Integer value based on the initial value and increment settings. + +__Arguments:__ +- Initial Value: What number should be output on the receipt of the first message. +- Increment: What should the increment be for each following message received.`; + +exports.install = function(instance) { + + var count = 0; + var initialCall = true; + + instance.on('data', function(flowdata) { + var index = flowdata.index; + if (index) { + instance.debug('Reset Count.'); + count = instance.options.initialvalue; + initialCall = true; + } else { + // If this is the first time, set the value to 'initial value' + if(initialCall) { + initialCall = false; + count = instance.options.initialvalue; + } else + count = count+instance.options.increment; + instance.status('Count:' + count); + instance.send2(count); + } + }); + + instance.on('options', function() { + count = instance.options.initialvalue; + initialCall = true; + }); + +}; diff --git a/flow/db_init.js b/flow/db_init.js index c43e4a2..ddd0aa0 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -64,7 +64,7 @@ exports.install = async function(instance) { if(dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; dbs.settings = { - edge_fw_version : "2025-01-02", //rok-mesiac-den + edge_fw_version : "2025-01-09", //rok-mesiac-den language : responseSettings[0]["lang"], rvo_name : responseSettings[0]["rvo_name"], project_id : responseSettings[0]["project_id"], diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 8415921..a6861b4 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -39,21 +39,18 @@ After that, we set this value to "previousValues[allPins[whichpin]]" variable /* RVO objekt: -state_of_main_switch - sem sa bude reportovať stav hlavného ističa : 0-> off 1-> on (toto nie je na platforme, ale Rado to už do entity type doplnil) +state_of_main_switch - reportovať stav hlavného ističa : 0-> off 1-> on rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky: Manual = 1 a Auto = 0 -> vyreportuje Manual Manual = 0 a Auto = 0 -> vyreportuje Off Manual = 0 a Auto = 1 -> vyreportuje Automatic -door_condition - tuto ide pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open +door_condition - pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy -zjavne nám v jsone chýba stav hlavného ističa. Musíme to potom doplniť - Na každú líniu: state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off 1-> on state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off 1-> on - momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme */ const { errLogger, logger, monitor } = require('./helper/logger'); @@ -319,16 +316,12 @@ exports.install = function(instance) { instance.send(0, exports.title + " running"); turnAlarm("off"); - // useTurnOffCounter = true; - // turnOffCounter = relaysData.length - 1; initialSetting(); ws.send(JSON.stringify({ "cmd": "all" })); // we request dev info about neuron device from evok to keep websocket connection alive // for some reason this request returns no data, but connection keeps alive - // https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket startRequests = setInterval(() => { - // console.log(" *** data from evok requested"); ws.send(JSON.stringify({ "cmd": "filter", "dev": ["neuron"] })); }, 150000) }; diff --git a/flow/helper/serialport_helper.js b/flow/helper/serialport_helper.js index 4a0edd5..8efa6af 100644 --- a/flow/helper/serialport_helper.js +++ b/flow/helper/serialport_helper.js @@ -74,7 +74,7 @@ async function writeData(port, data, readbytes, timeout){ let t = setTimeout(() => { port.removeListener('data', callback); - console.log("serialport helper: writeData TIMEOUT READING", rsPortReceivedData); + //console.log("serialport helper: writeData TIMEOUT READING", rsPortReceivedData); reject("TIMEOUT READING"); }, timeout); From fad53c9c01c840293275f315e4b0eec391eee1a3 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Wed, 15 Jan 2025 22:17:14 +0100 Subject: [PATCH 15/25] No turnOff on startup; reportOfflineNodeStatus function fix --- flow/cmd_manager.js | 16 +++++++--- flow/dido_controller.js | 46 ++++++++++------------------ flow/helper/DataToTbHandler.js | 55 ++++++++++++++-------------------- 3 files changed, 49 insertions(+), 68 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 280a5a6..8739037 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -767,8 +767,6 @@ exports.install = function(instance) { const date = Date.now(); - // it happens, that some data did not get to tb after sending - // we setTimeout to make more time for db to process telemetry (eg 150 messages at once) Object.keys(nodesData).forEach((node, index) => { setTimeout(function() { @@ -776,10 +774,20 @@ exports.install = function(instance) { //potrebujem nody k danej linii if (line == nodesData[node].line || line == undefined) { let tbname = nodesData[node].tbname; - sendTelemetry(values, tbname, date) + const dataToTb = { + [tbname]: [ + { + "ts": date, + "values": values + } + ] + } + //NOTE: we can not use sendTelemetry function, as it modifies input data (values object) + instance.send(SEND_TO.tb, dataToTb); + } - }, (index + 1) * 1000); + }, (index + 1) * 300); }) } diff --git a/flow/dido_controller.js b/flow/dido_controller.js index a6861b4..745c0f6 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -34,23 +34,17 @@ Currently we are interested in pins no. 1,2,3,6,8,9,10,16 pins number 11, 12, 13 (we receive 10,11,12 in rsPortReceivedData) are "stykace" When port receives data, it must be exactly 4 bytes long. Second byte is pin, that changed its value, fourth byte is value itself. After that, we set this value to "previousValues[allPins[whichpin]]" variable -*/ - -/* -RVO objekt: state_of_main_switch - reportovať stav hlavného ističa : 0-> off 1-> on -rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky: - Manual = 1 a Auto = 0 -> vyreportuje Manual - Manual = 0 a Auto = 0 -> vyreportuje Off - Manual = 0 a Auto = 1 -> vyreportuje Automatic +rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky: Manual = 1 a Auto = 0 -> Manual, +Manual = 0 a Auto = 0 -> Off, Manual = 0 a Auto = 1 -> Automatic -door_condition - pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open -twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy +door_condition - pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open +twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy Na každú líniu: -state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off 1-> on -state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off 1-> on +state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off, 1-> on +state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off, 1-> on */ const { errLogger, logger, monitor } = require('./helper/logger'); @@ -77,6 +71,7 @@ let rvoTbName; let GLOBALS; //FLOW global GLOBALS let SETTINGS; // GLOBALS.settings let controller_type; +let contactorSwitchCounter = 0; //how many times line contactor switched ? let alarmStatus = "OFF"; @@ -104,7 +99,7 @@ exports.install = function(instance) { let previousValues = {}; let rsPortReceivedData = []; - //to be able to get proper twilight values, when + //to be able to get proper twilight values let twilight_sensor_interval = 5;//minutes let twilight_sensor = []; const twilight_sensor_array = []; @@ -198,10 +193,6 @@ exports.install = function(instance) { if (pinsData[key].type == "state_of_contactor") { let pin = key - 1; if (controller_type === "unipi") pin = key; - - //this will modify database - let forceTurnOff = true; - turnLine("off", line, pin, forceTurnOff, "turn off on startup"); } } @@ -317,7 +308,6 @@ exports.install = function(instance) { turnAlarm("off"); initialSetting(); - ws.send(JSON.stringify({ "cmd": "all" })); // we request dev info about neuron device from evok to keep websocket connection alive // for some reason this request returns no data, but connection keeps alive @@ -528,7 +518,6 @@ exports.install = function(instance) { monitor.info(`turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value }; ws.send(JSON.stringify(cmd)); - switchLogic(pin, value) } //if rvo is 24/7, it has just one switching profile point at 13:00. we do not want to send notification as it repeats every day. @@ -585,6 +574,8 @@ exports.install = function(instance) { sendRvoStatus(); }) + + // we expect array as flowdata.data instance.on("1", flowdata => { @@ -597,22 +588,15 @@ exports.install = function(instance) { let line = obj.line; let force = obj.force; let info = obj.info; - + + //how many times did the lines switched ? if all lines (except line 0) has switched, we request data from evok: + contactorSwitchCounter++; + if(contactorSwitchCounter == Object.keys(relaysData).length-1 && ws) setTimeout(function(){ws.send(JSON.stringify({cmd:"all"}))},5000); + if (obj.command == "on") turnLine("on", line, undefined, force, info); else if (obj.command == "off") turnLine("off", line, undefined, force, info); else if (obj.command == "turnOnAlarm") turnAlarm("on"); else if (obj.command == "turnOffAlarm") turnAlarm("off"); - - //! ake data prichadzaju z cmd_manager.js ??? - //TODO transform to websocket - if (Array.isArray(obj)) { - - rsPort.write(Buffer.from(obj), function(err) { - switchLogic(obj); - - instance.send(SEND_TO.debug, { "WRITE": obj }); - }); - } }) diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 65e4ec3..f8734cc 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -4,15 +4,15 @@ class DataToTbHandler { this.index = index; // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) - this.timeToHoldTbValue = 30*60; //30 minutes + this.timeToHoldTbValue = 30 * 60; //30 minutes this.previousValues = {}; this.debug = false; this.messageCounter = 0; this.sender = ""; - - // if attribute difference is less than limit value, we do not send to tb. + + // if attribute change difference is less than limit value, we do not send to tb. this.attributeChangeLimit = { temperature: 0.5, Phase_1_voltage: 2, @@ -52,7 +52,7 @@ class DataToTbHandler { } isEmptyObject(obj) { - for (var name in obj) { + for (var _ in obj) { return false; } return true; @@ -62,9 +62,8 @@ class DataToTbHandler { let keys = Object.keys(dataToTb); - if(keys.length == 0) - { - if(this.debug) console.log("sendToTb received empty object", dataToTb); + if (keys.length == 0) { + if (this.debug) console.log("sendToTb received empty object", dataToTb); return; } @@ -74,19 +73,16 @@ class DataToTbHandler { let arrayOfValues = dataToTb[tbname]; let arrayOfValuesToSend = []; - for(let i = 0; i < arrayOfValues.length; i++) - { + for (let i = 0; i < arrayOfValues.length; i++) { ts = arrayOfValues[i].ts; let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); - if(!this.isEmptyObject(values)) - { - arrayOfValuesToSend.push({ts: ts, values: values}); + if (!this.isEmptyObject(values)) { + arrayOfValuesToSend.push({ ts: ts, values: values }); } } - if(arrayOfValuesToSend.length == 0) - { + if (arrayOfValuesToSend.length == 0) { //if(this.debug) console.log("data not sent - empty array"); return; } @@ -94,7 +90,7 @@ class DataToTbHandler { this.messageCounter++; let dataToTbModified = { - [tbname]: arrayOfValuesToSend + [tbname]: arrayOfValuesToSend } //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); @@ -111,47 +107,40 @@ class DataToTbHandler { prepareValuesForTb(tbname, timestamp, values) { let keys = Object.keys(values); - if(!this.previousValues.hasOwnProperty(tbname)) - { + if (!this.previousValues.hasOwnProperty(tbname)) { this.previousValues[tbname] = {}; } //if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values); - for(let i = 0; i < keys.length; i++) - { + for (let i = 0; i < keys.length; i++) { let key = keys[i]; let value = values[key]; - if(!this.previousValues[tbname].hasOwnProperty(key)) - { - this.previousValues[tbname][key] = {ts: timestamp, value: value}; + if (!this.previousValues[tbname].hasOwnProperty(key)) { + this.previousValues[tbname][key] = { ts: timestamp, value: value }; continue; } // attributeData ==> voltage: {ts:333333, value:5} let attributeData = this.previousValues[tbname][key]; let attributeToChange = false; - if(key in this.attributeChangeLimit) attributeToChange = true; + if (key in this.attributeChangeLimit) attributeToChange = true; let limit = this.attributeChangeLimit[key]; - - if(attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) - { + + if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { let diff = timestamp - attributeData.ts; let timestampDiffToRemoveKey = this.getDiffTimestamp(key); - if(diff > timestampDiffToRemoveKey) - { + if (diff > timestampDiffToRemoveKey) { attributeData.ts = Date.now(); //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); } - else - { + else { delete values[key]; //if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey); } } - else - { + else { attributeData.value = value; attributeData.ts = timestamp; } @@ -162,5 +151,5 @@ class DataToTbHandler { } } -module.exports = DataToTbHandler; +module.exports = DataToTbHandler; From 4c59ccd095334c463373666dbc3cee4a3842960f Mon Sep 17 00:00:00 2001 From: rasta5man Date: Fri, 17 Jan 2025 15:09:02 +0100 Subject: [PATCH 16/25] refactoring; version update --- flow/cmd_manager.js | 3 ++- flow/db_init.js | 2 +- flow/helper/DataToTbHandler.js | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 8739037..4a2b408 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -773,6 +773,7 @@ exports.install = function(instance) { //potrebujem nody k danej linii if (line == nodesData[node].line || line == undefined) { + let tbname = nodesData[node].tbname; const dataToTb = { [tbname]: [ @@ -782,9 +783,9 @@ exports.install = function(instance) { } ] } + //NOTE: we can not use sendTelemetry function, as it modifies input data (values object) instance.send(SEND_TO.tb, dataToTb); - } }, (index + 1) * 300); diff --git a/flow/db_init.js b/flow/db_init.js index ddd0aa0..e2f6b81 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -64,7 +64,7 @@ exports.install = async function(instance) { if(dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; dbs.settings = { - edge_fw_version : "2025-01-09", //rok-mesiac-den + edge_fw_version : "2025-01-13", //rok-mesiac-den language : responseSettings[0]["lang"], rvo_name : responseSettings[0]["rvo_name"], project_id : responseSettings[0]["project_id"], diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index f8734cc..7bd93bd 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -39,6 +39,7 @@ class DataToTbHandler { inclination_y: 10, inclination_z: 10 }; + } dump() { From 73a2620add1be091b7288716504cefea8383e632 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Sat, 25 Jan 2025 14:50:44 +0100 Subject: [PATCH 17/25] version 2025-01-21; offline node status, do not turn lines on startup if not needed --- flow/cmd_manager.js | 261 ++++++++++++++++------------------------ flow/db_init.js | 110 ++++++++--------- flow/dido_controller.js | 44 ++----- 3 files changed, 164 insertions(+), 251 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 4a2b408..aab6843 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -275,9 +275,6 @@ exports.install = function(instance) { let params = getParams(PRIORITY_TYPES.node_cmd); params.type = "set_node_profile"; params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; params.byte4 = 96; params.recipient = 1; params.register = 8; @@ -297,9 +294,6 @@ exports.install = function(instance) { let params = getParams(PRIORITY_TYPES.node_cmd); params.type = "set_node_profile"; params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; params.byte4 = 96; params.recipient = 1; params.register = 8; @@ -345,8 +339,6 @@ exports.install = function(instance) { params.address = node; params.byte1 = parseInt(t[0]);//hh params.byte2 = parseInt(t[1]);//mm - params.byte3 = 0;//ss - params.byte4 = 0;// params.recipient = 1; params.register = register; params.rw = 1;//write @@ -362,9 +354,6 @@ exports.install = function(instance) { params = getParams(PRIORITY_TYPES.node_cmd); params.type = "set_node_profile"; params.address = node; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0;//ss params.byte4 = parseInt(dim_value) + 128;// params.recipient = 1; params.register = register; @@ -653,28 +642,18 @@ exports.install = function(instance) { logger.debug("--->reportOnlineNodeStatus for line", line); + const d = new Date(); + //run broadcast //Actual time - let params = {}; - - var d = new Date(); - let hours = d.getHours(); - let minutes = d.getMinutes(); - let seconds = d.getSeconds(); - - let time = d.getTime(); // time in ms - + let params = getParams(); params.address = 0xffffffff;//Broadcast - params.byte1 = hours;//h - params.byte2 = minutes;//m - params.byte3 = seconds;//s - params.byte4 = 0; + params.byte1 = d.getHours(); + params.byte2 = d.getMinutes(); params.recipient = 2;//2 broadcast, address = 0 params.register = 87;//Actual time params.rw = 1;//write - - //other values params.type = "cmd"; - params.timestamp = Date.now() + 60000; + params.timestamp = d.getTime() + 30000; params.addMinutesToTimestamp = 0; params.info = "run broadcast: Actual time"; @@ -684,42 +663,31 @@ exports.install = function(instance) { setTimeout(function() { //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel + let time = Date.now(); + for (let k in nodesData) { //potrebujem nody k danej linii if (line == nodesData[k].line || line == undefined) { + let tbname = nodesData[k].tbname; let node = nodesData[k].node; let status = "NOK"; // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. - if (nodesData[k].status) { + if (nodesData[k].node_status_before_offline == true || nodesData[k].status === true) { status = "OK"; nodesData[k].time_of_last_communication = time; } + updateNodeStatus(k, status === "OK" ? true : false); + if (nodesData[k].hasOwnProperty("node_status_before_offline")) delete nodesData[k].node_status_before_offline; sendTelemetry({ status: status }, tbname, time); - //vyreportovanie dimmingu, prud, vykon - current, input power pre liniu pre vsetky nody + //vyreportovanie dimming, current, input power pre liniu pre vsetky nody + //Prud { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read dimming'; - //params.debug = true; - - tasks.push(params); - } - - //Prúd - { - let params = getParams(PRIORITY_TYPES.high_priority); + let params = getParams(); params.type = "cmd"; params.tbname = tbname; @@ -727,16 +695,16 @@ exports.install = function(instance) { params.register = 75;//prud params.recipient = 1;//slave params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; + params.timestamp = time + 3000; params.info = 'read current'; //params.debug = true; tasks.push(params); } - //výkon + //vykon { - let params = getParams(PRIORITY_TYPES.high_priority); + let params = getParams(); params.type = "cmd"; params.tbname = tbname; @@ -744,14 +712,32 @@ exports.install = function(instance) { params.register = 76;//výkon params.recipient = 1;//slave params.rw = 0;//read - params.timestamp = PRIORITY_TYPES.high_priority; + params.timestamp = time + 3100; params.info = 'read power'; //params.debug = true; tasks.push(params); } + //dimming + { + let params = getParams(); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 3200; + params.info = 'read dimming'; + //params.debug = true; + + tasks.push(params); + } + } } + }, sec * 1000); } @@ -775,17 +761,12 @@ exports.install = function(instance) { if (line == nodesData[node].line || line == undefined) { let tbname = nodesData[node].tbname; - const dataToTb = { - [tbname]: [ - { - "ts": date, - "values": values - } - ] - } + let nodeStatus = nodesData[node].status; - //NOTE: we can not use sendTelemetry function, as it modifies input data (values object) - instance.send(SEND_TO.tb, dataToTb); + nodesData[node].node_status_before_offline = nodeStatus === true ? true : false; + nodesData[node].status = "OFFLINE"; + + sendTelemetry({ ...values }, tbname, date); } }, (index + 1) * 300); @@ -801,7 +782,7 @@ exports.install = function(instance) { info: info }; - logger.debug("linia", line, obj); + //logger.debug("linia", line, obj); instance.send(SEND_TO.dido_controller, obj); } @@ -884,14 +865,16 @@ exports.install = function(instance) { processLine = params.line; } - //load profiles pre vsetky linie: let now = new Date(); + //process line profiles if (processLineProfiles) { - //process line profiles + let keys = Object.keys(relaysData); + for (let i = 0; i < keys.length; i++) { - let line = parseInt(keys[i]); //line is turned off by default + + let line = parseInt(keys[i]); let profilestr = relaysData[line].profile; if (processLine != undefined) { @@ -994,13 +977,12 @@ exports.install = function(instance) { start_time.setDate(start_time.getDate() + 1); } - let params = getParams(PRIORITY_TYPES.relay_profile); + let params = getParams(); params.type = "relay"; params.line = parseInt(line); params.value = time_points[t].value; params.tbname = relaysData[line].tbname; params.timestamp = start_time.getTime(); - params.addMinutesToTimestamp = 0; // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day @@ -1028,13 +1010,13 @@ exports.install = function(instance) { monitor.info("-->time_points final", line, time_points); //ensure to turn on/off according to calculated currentValue - let params = getParams(PRIORITY_TYPES.terminal); + let params = getParams(); params.type = "relay"; params.line = parseInt(line); params.tbname = relaysData[line].tbname; params.value = currentValue; - params.timestamp = PRIORITY_TYPES.terminal; + params.timestamp = i; params.addMinutesToTimestamp = 0; params.debug = true; @@ -1050,12 +1032,14 @@ exports.install = function(instance) { } catch (error) { if (profilestr !== "") { //errLogger.error(profilestr, error); + console.log(`Cmd_manager: Unable to process line profile ${line}. Error: `, error); errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); + } else { + turnLine("off", line, "No line profile. Switching it off on startup"); } } } - //logger.debug("tasks:"); //logger.debug(tasks); } @@ -1065,60 +1049,44 @@ exports.install = function(instance) { //Time of dusk, Time of dawn, Actual Time if (processBroadcast) { - let addMinutesToTimestamp = 5; + + let d = new Date(); + let time = d.getTime(); + let sunCalcResult = calculateDuskDawn(); { //run broadcast Time of dusk - addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk - - let params = getParams(PRIORITY_TYPES.node_broadcast); - - let sunCalcResult = calculateDuskDawn(); - let dusk_hours = sunCalcResult["dusk_hours"]; - let dusk_minutes = sunCalcResult["dusk_minutes"]; + let params = getParams(); params.address = 0xffffffff;//broadcast - params.byte1 = dusk_hours;//h - params.byte2 = dusk_minutes;//m - params.byte3 = 0;//s - params.byte4 = 0; + params.byte1 = sunCalcResult["dusk_hours"]; + params.byte2 = sunCalcResult["dusk_minutes"]; params.recipient = 2;//2 broadcast, params.register = 6;//Time of dusk - Reg 6 params.rw = 1;//write - //other values params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; + params.timestamp = time + 60000; + params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk params.info = "Broadcast-duskTime"; tasks.push(params); } { - //run broadcast Time of dawn - addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn - - let params = getParams(PRIORITY_TYPES.node_broadcast); - - let sunCalcResult = calculateDuskDawn(); - let dawn_hours = sunCalcResult["dawn_hours"]; - let dawn_minutes = sunCalcResult["dawn_minutes"]; + let params = getParams(); params.address = 0xffffffff;//broadcast - params.byte1 = dawn_hours;//h - params.byte2 = dawn_minutes;//m - params.byte3 = 0;//s - params.byte4 = 0; + params.byte1 = sunCalcResult["dawn_hours"]; + params.byte2 = sunCalcResult["dawn_minutes"]; params.recipient = 2; //2 broadcast params.register = 7;//Time of dawn - Reg 6 params.rw = 1;//write - //other values params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; + params.timestamp = time + 60001; + params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn params.info = "Broadcast-dawnTime"; tasks.push(params); @@ -1126,28 +1094,18 @@ exports.install = function(instance) { { //run broadcast Actual time - addMinutesToTimestamp = 5; - - let params = getParams(PRIORITY_TYPES.node_broadcast); - - var d = new Date(); - let hours = d.getHours(); - let minutes = d.getMinutes(); - let seconds = d.getSeconds(); + let params = getParams(); params.address = 0xffffffff;//broadcast - params.byte1 = hours;//h - params.byte2 = minutes;//m - params.byte3 = seconds;//s - params.byte4 = 0; + params.byte1 = d.getHours(); + params.byte2 = d.getMinutes(); params.recipient = 2; //2 broadcast params.register = 87;//Actual time params.rw = 1;//write - //other values params.type = "cmd"; - params.timestamp = Date.now() + 60000; - params.addMinutesToTimestamp = addMinutesToTimestamp; + params.timestamp = time + 60002; + params.addMinutesToTimestamp = 5; params.info = "run broadcast: Actual time"; tasks.push(params); @@ -1158,6 +1116,9 @@ exports.install = function(instance) { //process nodes & tasks //reportovanie pre platformu if (processNodes) { + + let time = Date.now(); + for (let k in nodesData) { let address = parseInt(k); let tbname = nodesData[k].tbname; @@ -1167,37 +1128,24 @@ exports.install = function(instance) { //listOfCommands - READ for (let i = 0; i < listOfCommands.length; i++) { + register = listOfCommands[i]; - - let params = getParams(PRIORITY_TYPES.node_cmd); - - //core rpc values - params.address = address; - params.byte1 = 0; - params.byte2 = 0; - params.byte3 = 0; - params.byte4 = 0; - params.recipient = 1; - params.register = register; - params.rw = 0; - let addMinutesToTimestamp = priorities[register]; - let timestampStart = PRIORITY_TYPES.node_cmd; //run imediatelly in function runTasks - if (addMinutesToTimestamp > 1) { - timestampStart = timestampStart + addMinutesToTimestamp * 60000; - } + let params = getParams(); - //other values + params.address = address; + params.recipient = 1; + params.register = register; params.type = "cmd"; params.tbname = tbname; - params.timestamp = timestampStart; + params.timestamp = time + 5000 + i * 500 + addMinutesToTimestamp * 1000; //to make slight time difference params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "generated cmd - buildTasks (node)"; tasks.push(params); - } + } } @@ -1206,12 +1154,9 @@ exports.install = function(instance) { if (!init) return; - //Priebežne (raz za cca 5 minút) je potrebné vyčítať z Master nodu verziu jeho FW. - //Jedná sa o register 10. Rovnaká interpretácia ako pri FW verzii nodu. - //Adresa mastera je 0. V prípade že kedykoľvek nastane situácia že Master Node neodpovedá (napríklad pri vyčítaní telemetrie z nodu nevráti žiadne dáta), - //tak treba vyreportovať string "NOK". + //Master node FW version { - let params = getParams(PRIORITY_TYPES.fw_detection); + let params = getParams(); params.type = "cmd-master"; params.register = 4; params.address = 0; @@ -1220,7 +1165,6 @@ exports.install = function(instance) { params.tbname = SETTINGS.rvoTbName; params.info = "Master node FW verzia"; //params.debug = true; - //this will set SETTINGS.masterNodeIsResponding tasks.push(params); @@ -1228,7 +1172,7 @@ exports.install = function(instance) { //kazdu hodinu skontrolovat nastavenie profilov { - let params = getParams(PRIORITY_TYPES.fw_detection); + let params = getParams(); params.type = "process_profiles"; params.timestamp = Date.now() + 60000; params.addMinutesToTimestamp = 60;//60 = every hour @@ -1336,7 +1280,12 @@ exports.install = function(instance) { let data = null; - if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; + if (nodeCurrentStatus === "OFFLINE") { + data = { status: newStatus }; + nodeDbStatusModify(node, data); + return; + } + else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { data = { time_of_last_communication: now }; nodeDbStatusModify(node, data); @@ -1476,7 +1425,7 @@ exports.install = function(instance) { if (type == "process_profiles") { tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - //vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov + //na vsetky zapnutych liniach sa spracuju nespracovane profily nodov loadRelaysData(); interval = setInterval(runTasks, SHORT_INTERVAL); @@ -1517,7 +1466,7 @@ exports.install = function(instance) { turnLine(onOrOff, params.line, info); - interval = setInterval(runTasks, SHORT_INTERVAL); + interval = setInterval(runTasks, LONG_INTERVAL); return; } @@ -1554,7 +1503,6 @@ exports.install = function(instance) { const register = params.register; - //high_priority if (!SETTINGS.masterNodeIsResponding) { //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version errorHandler.sendMessageToService("Master node is not responding"); @@ -1609,8 +1557,6 @@ exports.install = function(instance) { var d = new Date(); params.byte1 = d.getHours();//h params.byte2 = d.getMinutes();//m - params.byte3 = 0;//s - params.byte4 = 0; } //SET DUSK/DAWN FOR BROADCAST @@ -1621,8 +1567,6 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDawn(); params.byte1 = sunCalcResult["dusk_hours"];//h params.byte2 = sunCalcResult["dusk_minutes"];//m - params.byte3 = 0;//s - params.byte4 = 0; //TODO astrohodiny let dusk = "Time of dusk: " + sunCalcResult["dusk"]; @@ -1635,8 +1579,6 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDawn(); params.byte1 = sunCalcResult["dawn_hours"];//h params.byte2 = sunCalcResult["dawn_minutes"];//m - params.byte3 = 0;//s - params.byte4 = 0; //TODO astrohodiny let dawn = "Time of dawn: " + sunCalcResult["dawn"]; @@ -1752,7 +1694,7 @@ exports.install = function(instance) { } else { - terminalCommandResponse(params, "ERROR", data) + terminalCommandResponse(params, "ERROR", data); handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); if (params.hasOwnProperty("debug")) { @@ -1805,9 +1747,9 @@ exports.install = function(instance) { let values = {}; - // console.log(message); let updateStatus = updateNodeStatus(node, false); + console.log("cmd_man: handleNokRsPort: ", node, register, message); //master node if (node == 0) { sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); @@ -1917,8 +1859,12 @@ exports.install = function(instance) { let nodeObj = nodesData_clone[key]; if (nodeObj.tbname == undefined) continue; - if (nodeObj.status) number_of_ok_luminaires++; + if (nodeObj.status === "OFFLINE") { + nodeObj.node_status_before_offline === true ? number_of_ok_luminaires++ : number_of_nok_luminaires++; + } + else if (nodeObj.status == true) number_of_ok_luminaires++; else number_of_nok_luminaires++; + } const values = { @@ -2376,16 +2322,11 @@ exports.install = function(instance) { relaysData[line].profile = profile; loadRelaysData(line) - - //TODO build tasks by mala bezat az ked je vsetko loadRelaysData - //spracovane, pravdepodobne treba spravit promisy logger.debug("loadRelaysData DONE for line", line); - console.log("zacina buildTasks po loadRelaysData.........") buildTasks({ processLineProfiles: true, line: line }); sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); - }); }); break; diff --git a/flow/db_init.js b/flow/db_init.js index e2f6b81..3c8cc58 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -37,69 +37,69 @@ const { initNotification } = require('./helper/notification_reporter'); exports.install = async function(instance) { - const dbNodes = TABLE("nodes"); - const dbRelays = TABLE("relays"); - const dbSettings = TABLE("settings"); - const dbPins = TABLE("pins"); - const dbNotifications = TABLE("notifications"); + const dbNodes = TABLE("nodes"); + const dbRelays = TABLE("relays"); + const dbSettings = TABLE("settings"); + const dbPins = TABLE("pins"); + const dbNotifications = TABLE("notifications"); - FLOW.GLOBALS = {}; - const dbs = FLOW.GLOBALS; + FLOW.GLOBALS = {}; + const dbs = FLOW.GLOBALS; - const responseSettings = await promisifyBuilder(dbSettings.find()); - const responseNodes = await promisifyBuilder(dbNodes.find()); - const responsePins = await promisifyBuilder(dbPins.find()); - const responseRelays = await promisifyBuilder(dbRelays.find()); - const response = await promisifyBuilder(dbNotifications.find()); + const responseSettings = await promisifyBuilder(dbSettings.find()); + const responseNodes = await promisifyBuilder(dbNodes.find()); + const responsePins = await promisifyBuilder(dbPins.find()); + const responseRelays = await promisifyBuilder(dbRelays.find()); + const response = await promisifyBuilder(dbNotifications.find()); - dbs.pinsData = makeMapFromDbResult(responsePins, "pin"); - dbs.relaysData = makeMapFromDbResult(responseRelays, "line"); - dbs.nodesData = makeMapFromDbResult(responseNodes, "node"); - dbs.notificationsData = makeMapFromDbResult(response, "key"); + dbs.pinsData = makeMapFromDbResult(responsePins, "pin"); + dbs.relaysData = makeMapFromDbResult(responseRelays, "line"); + dbs.nodesData = makeMapFromDbResult(responseNodes, "node"); + dbs.notificationsData = makeMapFromDbResult(response, "key"); - //+|354|nodesdata.....+|482|nodesdata.... - //for some reason, if last line in nodes.table is not empty, flow wrote more nodes data in one row, - //so we have to add empty line at the bottom of nodes table to avoid this. - //now, remove empty lines from nodesData database: - if(dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; + //+|354|nodesdata.....+|482|nodesdata.... + //for some reason, if last line in nodes.table is not empty, flow wrote more nodes data in one row, + //so we have to add empty line at the bottom of nodes table to avoid this. + //now, remove empty lines from nodesData database: + if (dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; - dbs.settings = { - edge_fw_version : "2025-01-13", //rok-mesiac-den - language : responseSettings[0]["lang"], - rvo_name : responseSettings[0]["rvo_name"], - project_id : responseSettings[0]["project_id"], - rvoTbName : dbs.relaysData[0]["tbname"], - temperature_address : responseSettings[0]["temperature_address"], - controller_type : responseSettings[0]["controller_type"], - serial_port : responseSettings[0]["serial_port"], - node_status_nok_time : responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000 ,// hour * minutes * - latitude : responseSettings[0]["latitude"], - longitude : responseSettings[0]["longitude"], - no_voltage : new Set(),//modbus_citysys - elektromer - backup_on_failure : responseSettings[0]["backup_on_failure"], - restore_from_backup : responseSettings[0]["restore_from_backup"], - restore_backup_wait : responseSettings[0]["restore_backup_wait"], - mqtt_host : responseSettings[0]["mqtt_host"], - mqtt_clientid : responseSettings[0]["mqtt_clientid"], - mqtt_username : responseSettings[0]["mqtt_username"], - mqtt_port : responseSettings[0]["mqtt_port"], - phases: responseSettings[0]["phases"], - cloud_topic: responseSettings[0]["cloud_topic"], - - //dynamic values - masterNodeIsResponding : true, //cmd_manager - maintenance_mode : false, - } + dbs.settings = { + edge_fw_version: "2025-01-21", //rok-mesiac-den + language: responseSettings[0]["lang"], + rvo_name: responseSettings[0]["rvo_name"], + project_id: responseSettings[0]["project_id"], + rvoTbName: dbs.relaysData[0]["tbname"], + temperature_address: responseSettings[0]["temperature_address"], + controller_type: responseSettings[0]["controller_type"], + serial_port: responseSettings[0]["serial_port"], + node_status_nok_time: responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000,// hour * minutes * + latitude: responseSettings[0]["latitude"], + longitude: responseSettings[0]["longitude"], + no_voltage: new Set(),//modbus_citysys - elektromer + backup_on_failure: responseSettings[0]["backup_on_failure"], + restore_from_backup: responseSettings[0]["restore_from_backup"], + restore_backup_wait: responseSettings[0]["restore_backup_wait"], + mqtt_host: responseSettings[0]["mqtt_host"], + mqtt_clientid: responseSettings[0]["mqtt_clientid"], + mqtt_username: responseSettings[0]["mqtt_username"], + mqtt_port: responseSettings[0]["mqtt_port"], + phases: responseSettings[0]["phases"], + cloud_topic: responseSettings[0]["cloud_topic"], - FLOW.dbLoaded = true; - initNotification(); + //dynamic values + masterNodeIsResponding: true, //cmd_manager + maintenance_mode: false, + } - setTimeout(()=> { - console.log("DB_INIT - data loaded"); - instance.send(0, "_") - }, 5000) + FLOW.dbLoaded = true; + initNotification(); -}; + setTimeout(() => { + console.log("DB_INIT - data loaded"); + instance.send(0, "_") + }, 5000) + +}; diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 745c0f6..523d39b 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -50,7 +50,6 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač const { errLogger, logger, monitor } = require('./helper/logger'); const SerialPort = require('serialport'); const WebSocket = require('ws'); -//const { exec } = require('child_process'); const { runSyncExec } = require('./helper/serialport_helper'); const { bytesToInt, resizeArray } = require('./helper/utils'); const { sendNotification } = require('./helper/notification_reporter'); @@ -71,7 +70,6 @@ let rvoTbName; let GLOBALS; //FLOW global GLOBALS let SETTINGS; // GLOBALS.settings let controller_type; -let contactorSwitchCounter = 0; //how many times line contactor switched ? let alarmStatus = "OFF"; @@ -128,7 +126,7 @@ exports.install = function(instance) { //status for calculating Statecodes let deviceStatus = { //key is device name: temperature,.... - "state_of_main_switch": "Off", //Hlavný istič + "state_of_main_switch": "Off", //Hlavný istič (po novom druhy dverovy kontakt) "rotary_switch_state": "Off", //Prevádzkový mód "door_condition": "closed", //Dverový kontakt "em": "OK", //elektromer rvo @@ -203,13 +201,10 @@ exports.install = function(instance) { sendTelemetry(values, rvoTbName); - let time = 5 * 1000; - setTimeout(function() { - instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "buildTasks" }); + instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "buildTasks" }); - sendNotification("rsPort.open()", rvoTbName, "flow_start", {}, "", SEND_TO.tb, instance); - monitor.info("-->FLOW bol spustený", rvoTbName, SETTINGS.edge_fw_version); - }, time); + sendNotification("rsPort.open()", rvoTbName, "flow_start", {}, "", SEND_TO.tb, instance); + monitor.info("-->FLOW bol spustený", rvoTbName, SETTINGS.edge_fw_version); } @@ -309,6 +304,7 @@ exports.install = function(instance) { initialSetting(); + setTimeout(function() { ws.send(JSON.stringify({ cmd: "all" })) }, 5000); // we request dev info about neuron device from evok to keep websocket connection alive // for some reason this request returns no data, but connection keeps alive startRequests = setInterval(() => { @@ -574,13 +570,13 @@ exports.install = function(instance) { sendRvoStatus(); }) - + // we expect array as flowdata.data instance.on("1", flowdata => { - console.log(flowdata.data); + //console.log(flowdata.data); if (!flowdata.data instanceof Object) return; @@ -588,11 +584,7 @@ exports.install = function(instance) { let line = obj.line; let force = obj.force; let info = obj.info; - - //how many times did the lines switched ? if all lines (except line 0) has switched, we request data from evok: - contactorSwitchCounter++; - if(contactorSwitchCounter == Object.keys(relaysData).length-1 && ws) setTimeout(function(){ws.send(JSON.stringify({cmd:"all"}))},5000); - + if (obj.command == "on") turnLine("on", line, undefined, force, info); else if (obj.command == "off") turnLine("off", line, undefined, force, info); else if (obj.command == "turnOnAlarm") turnAlarm("on"); @@ -815,26 +807,6 @@ exports.install = function(instance) { let value = "On"; if (newPinValue === 0) value = "Off"; - //Hlavný istič - //! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri - //! takze ked pride z evoku signal pre 'input1_05', handlujeme ho ako 'door_condition' - // if(type === "!!!state_of_main_switch") - // { - // if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) - // { - // sendNotification("switchLogic", rvoTbName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); - // values["status"] = "NOK"; - - // deviceStatus["state_of_main_switch"] = "Off"; - // } - // else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) - // { - // sendNotification("switchLogic", rvoTbName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch"); - - // deviceStatus["state_of_main_switch"] = "On"; - // } - // } - //Prevádzkový mód if (type == "rotary_switch_state") { // combination of these two pins required to get result From 5233aa38af074a8f74e09220b6ae011a858e05a9 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Tue, 4 Feb 2025 18:45:06 +0100 Subject: [PATCH 18/25] version 2025-01-30; send fw version once a day --- flow/cmd_manager.js | 234 ++++++++++++++------------------- flow/db_init.js | 3 +- flow/helper/DataToTbHandler.js | 49 +++++-- 3 files changed, 141 insertions(+), 145 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index aab6843..0e96693 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -83,6 +83,7 @@ exports.install = function(instance) { let interval = null;//timeout for procesing tasks let customTasksInterval = null; // interval for reportEdgeDateTimeAndNumberOfLuminaires(); let setCorrectTime = null; // interval for setting a correct edgeTime + let sendNodeReadout = null; // interval for sending agregate data from node let refFlowdataObj = {}; @@ -173,13 +174,16 @@ exports.install = function(instance) { handleRsPort(); - //to ensure, edgeDateTime will be send to tb at full minute customTasksInterval = setInterval(function() { - if (new Date().getSeconds() === 0) reportEdgeDateTimeAndNumberOfLuminaires(); - }, 1000); + reportEdgeDateTimeAndNumberOfLuminaires(); + }, 120000); + reportEdgeDateTimeAndNumberOfLuminaires(); + setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour setCorrectPlcTimeOnceADay(); + + sendNodeReadout = setInterval(sendNodesData, 150000); } @@ -280,7 +284,6 @@ exports.install = function(instance) { params.register = 8; params.rw = 1;//write params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; params.info = 'turn off/reset node profile'; cmdCounter[node] = 1; @@ -299,7 +302,6 @@ exports.install = function(instance) { params.register = 8; params.rw = 1;//write params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; params.info = 'turn off node profile'; tasksProfile.push(params); @@ -373,14 +375,13 @@ exports.install = function(instance) { logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); - let params = getParams(PRIORITY_TYPES.node_cmd); + let params = getParams(); params.type = "set_node_profile"; params.address = node; params.register = 96; params.recipient = 1; params.rw = 1;//write params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; params.info = "Threshold lux level for DUSK/DAWN"; if (nodeProfile.dusk_lux_sensor) { @@ -409,14 +410,13 @@ exports.install = function(instance) { logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); - let params = getParams(PRIORITY_TYPES.node_cmd); + let params = getParams(); params.type = "set_node_profile"; params.address = node; params.register = 97; params.recipient = 1; params.rw = 1;//write params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; params.info = "DUSK/DAWN max. adjust period"; if (nodeProfile.astro_clock) { @@ -456,7 +456,6 @@ exports.install = function(instance) { params.recipient = 1; params.rw = 1;//write params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; params.info = "Static offset"; if (nodeProfile.astro_clock) { @@ -644,7 +643,7 @@ exports.install = function(instance) { const d = new Date(); - //run broadcast //Actual time + // broadcast actual time let params = getParams(); params.address = 0xffffffff;//Broadcast params.byte1 = d.getHours(); @@ -652,10 +651,10 @@ exports.install = function(instance) { params.recipient = 2;//2 broadcast, address = 0 params.register = 87;//Actual time params.rw = 1;//write - params.type = "cmd"; + params.type = "node-onetime-write"; params.timestamp = d.getTime() + 30000; - params.addMinutesToTimestamp = 0; params.info = "run broadcast: Actual time"; + //params.debug = true; tasks.push(params); @@ -680,6 +679,8 @@ exports.install = function(instance) { nodesData[k].time_of_last_communication = time; } + nodesData[k].readout.status = status; + updateNodeStatus(k, status === "OK" ? true : false); if (nodesData[k].hasOwnProperty("node_status_before_offline")) delete nodesData[k].node_status_before_offline; sendTelemetry({ status: status }, tbname, time); @@ -689,16 +690,15 @@ exports.install = function(instance) { { let params = getParams(); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; params.register = 75;//prud params.recipient = 1;//slave params.rw = 0;//read - params.timestamp = time + 3000; + params.timestamp = time + 4000; params.info = 'read current'; //params.debug = true; - tasks.push(params); } @@ -706,13 +706,13 @@ exports.install = function(instance) { { let params = getParams(); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; params.register = 76;//výkon params.recipient = 1;//slave params.rw = 0;//read - params.timestamp = time + 3100; + params.timestamp = time + 4100; params.info = 'read power'; //params.debug = true; @@ -722,13 +722,13 @@ exports.install = function(instance) { { let params = getParams(); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; params.register = 1;//dimming params.recipient = 1;//slave params.rw = 0;//read - params.timestamp = time + 3200; + params.timestamp = time + 4200; params.info = 'read dimming'; //params.debug = true; @@ -765,6 +765,7 @@ exports.install = function(instance) { nodesData[node].node_status_before_offline = nodeStatus === true ? true : false; nodesData[node].status = "OFFLINE"; + nodesData[node].readout = {}; sendTelemetry({ ...values }, tbname, date); } @@ -837,12 +838,9 @@ exports.install = function(instance) { //BUILD TASKS// function buildTasks(params) { - //report SETTINGS.edge_fw_version as fw_version - //report date as startdate //return; console.log("buidTAaasks start ****************", params); - monitor.info("buildTasks - params", params); let processLine; //defined line @@ -983,7 +981,6 @@ exports.install = function(instance) { params.value = time_points[t].value; params.tbname = relaysData[line].tbname; params.timestamp = start_time.getTime(); - params.addMinutesToTimestamp = 0; // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day if (time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24 * 60; @@ -1015,9 +1012,7 @@ exports.install = function(instance) { params.line = parseInt(line); params.tbname = relaysData[line].tbname; params.value = currentValue; - params.timestamp = i; - params.addMinutesToTimestamp = 0; params.debug = true; //logger.debug(now.toLocaleString("sk-SK")); @@ -1045,9 +1040,7 @@ exports.install = function(instance) { } - //PROCESS DEFAULT BROADCASTS - //Time of dusk, Time of dawn, Actual Time - + //NOTE: PROCESS DEFAULT BROADCASTS - Time of dusk, Time of dawn, Actual Time if (processBroadcast) { let d = new Date(); @@ -1055,17 +1048,15 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDawn(); { - //run broadcast Time of dusk let params = getParams(); params.address = 0xffffffff;//broadcast params.byte1 = sunCalcResult["dusk_hours"]; params.byte2 = sunCalcResult["dusk_minutes"]; params.recipient = 2;//2 broadcast, - params.register = 6;//Time of dusk - Reg 6 + params.register = 6;//Time of dusk params.rw = 1;//write - - params.type = "cmd"; + params.type = "node-regular-write"; params.timestamp = time + 60000; params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk params.info = "Broadcast-duskTime"; @@ -1074,17 +1065,15 @@ exports.install = function(instance) { } { - //run broadcast Time of dawn let params = getParams(); params.address = 0xffffffff;//broadcast params.byte1 = sunCalcResult["dawn_hours"]; params.byte2 = sunCalcResult["dawn_minutes"]; params.recipient = 2; //2 broadcast - params.register = 7;//Time of dawn - Reg 6 + params.register = 7;//Time of dawn params.rw = 1;//write - - params.type = "cmd"; + params.type = "node-regular-write"; params.timestamp = time + 60001; params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn params.info = "Broadcast-dawnTime"; @@ -1093,7 +1082,6 @@ exports.install = function(instance) { } { - //run broadcast Actual time let params = getParams(); params.address = 0xffffffff;//broadcast @@ -1102,8 +1090,7 @@ exports.install = function(instance) { params.recipient = 2; //2 broadcast params.register = 87;//Actual time params.rw = 1;//write - - params.type = "cmd"; + params.type = "node-regular-write"; params.timestamp = time + 60002; params.addMinutesToTimestamp = 5; params.info = "run broadcast: Actual time"; @@ -1113,8 +1100,7 @@ exports.install = function(instance) { } - //process nodes & tasks - //reportovanie pre platformu + //process nodes & tasks - read node's data if (processNodes) { let time = Date.now(); @@ -1124,9 +1110,6 @@ exports.install = function(instance) { let tbname = nodesData[k].tbname; let register = 0; - //logger.debug("generated cmd - buildTasks for node:", address); - - //listOfCommands - READ for (let i = 0; i < listOfCommands.length; i++) { register = listOfCommands[i]; @@ -1137,11 +1120,11 @@ exports.install = function(instance) { params.address = address; params.recipient = 1; params.register = register; - params.type = "cmd"; + params.type = register == 1 ? "node-dimming-read" : "node-regular-read"; params.tbname = tbname; params.timestamp = time + 5000 + i * 500 + addMinutesToTimestamp * 1000; //to make slight time difference params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "generated cmd - buildTasks (node)"; + params.info = "Node regular read command"; tasks.push(params); } @@ -1154,7 +1137,7 @@ exports.install = function(instance) { if (!init) return; - //Master node FW version + //Master node FW version - modifies SETTINGS.masterNodeIsResponding { let params = getParams(); params.type = "cmd-master"; @@ -1165,7 +1148,6 @@ exports.install = function(instance) { params.tbname = SETTINGS.rvoTbName; params.info = "Master node FW verzia"; //params.debug = true; - //this will set SETTINGS.masterNodeIsResponding tasks.push(params); } @@ -1174,7 +1156,7 @@ exports.install = function(instance) { { let params = getParams(); params.type = "process_profiles"; - params.timestamp = Date.now() + 60000; + params.timestamp = Date.now() + 60001; params.addMinutesToTimestamp = 60;//60 = every hour params.info = "detekcia nespracovaných profilov linie a nodov"; //params.debug = true; @@ -1403,31 +1385,25 @@ exports.install = function(instance) { let node = params.address; let line = null; + let itIsNodeCommand; + + if (nodesData[node] !== undefined) { + line = nodesData[node].line; + itIsNodeCommand = true; + } - //rpc related - if (nodesData[node] !== undefined) line = nodesData[node].line; if (params.line !== undefined) line = params.line; - let repeatTask = false; - if (params.addMinutesToTimestamp > 0 || params.timePointName) repeatTask = true; - - if (repeatTask) { - if (type === "cmd" || type === "cmd-master") { - //set next start time automatically - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } - } - else { + if (params.addMinutesToTimestamp > 0 || params.timePointName) { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } else { tasks.shift(); } //kontrola nespracovanych profilov nodov if (type == "process_profiles") { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - //na vsetky zapnutych liniach sa spracuju nespracovane profily nodov loadRelaysData(); - interval = setInterval(runTasks, SHORT_INTERVAL); return; } @@ -1504,13 +1480,12 @@ exports.install = function(instance) { const register = params.register; if (!SETTINGS.masterNodeIsResponding) { - //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version + //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal cmd-master errorHandler.sendMessageToService("Master node is not responding"); let stop = true; - //fw version - register == 4 - if (type == "cmd-terminal" || register == 4) stop = false; + if (type == "cmd-terminal" || type == "cmd-master") stop = false; if (stop) { interval = setInterval(runTasks, LONG_INTERVAL); return; @@ -1567,9 +1542,6 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDawn(); params.byte1 = sunCalcResult["dusk_hours"];//h params.byte2 = sunCalcResult["dusk_minutes"];//m - - //TODO astrohodiny - let dusk = "Time of dusk: " + sunCalcResult["dusk"]; } } @@ -1579,11 +1551,7 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDawn(); params.byte1 = sunCalcResult["dawn_hours"];//h params.byte2 = sunCalcResult["dawn_minutes"];//m - - //TODO astrohodiny - let dawn = "Time of dawn: " + sunCalcResult["dawn"]; } - } //----------------------- @@ -1594,7 +1562,6 @@ exports.install = function(instance) { let saveToTb = true; if (!tbname) saveToTb = false; - let itIsNodeCommand = listOfCommands.includes(register); //reading data from node (voltage, current, dimming, status) let resp = com_generic(node, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); let readBytes = 11; @@ -1615,23 +1582,13 @@ exports.install = function(instance) { let message_type = result.type; let error = result.error; - if (params.debug != "generated cmd") { - //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug, params); + if (params.hasOwnProperty("debug")) { + if (params.debug) { + console.log("detected response:", result); + logger.debug("Cmd-mngr: writeData done " + message_type + " duration: " + timeDiff + " type: " + params.debug, params, result); + } } - // if(params.hasOwnProperty("debug")) - // { - // if(params.debug) - // { - // console.log("detected response:", result); - - // logger.debug("writeData: done " + message_typetype + " duration: " + timeDiff + " type: " + params.debug, params, result); - // } - // } - - //debug("writeData: done " + message_type + " duration: " + timeDiff + " message_type: " + params.debug); - //debug("writeData done", message_type, "duration", timeDiff, "message_type", params.debug, result); - let values = {}; //CMD FINISHED @@ -1664,6 +1621,7 @@ exports.install = function(instance) { if (itIsNodeCommand) { values.comm_status = "OK"; values.status = "OK"; + nodesData[node].readout = { ...nodesData[node].readout, ...values }; } //master node @@ -1682,8 +1640,8 @@ exports.install = function(instance) { //logger.debug("saveToTb", saveToTb, tbname, values); } - if (saveToTb) { - sendTelemetry(values, tbname) + if (saveToTb && type != "node-regular-read") { + sendTelemetry(values, tbname); } else { if (type == "cmd-terminal") { @@ -1749,7 +1707,18 @@ exports.install = function(instance) { let updateStatus = updateNodeStatus(node, false); - console.log("cmd_man: handleNokRsPort: ", node, register, message); + if (itIsNodeCommand) { + values.comm_status = "NOK"; + nodesData[node].readout.comm_status = "NOK"; + } + + if (updateStatus) { + values.status = "NOK"; + nodesData[node].readout.status = "NOK"; + } + + if (type === "node-regular-read") return; + //master node if (node == 0) { sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); @@ -1769,21 +1738,21 @@ exports.install = function(instance) { } } - if (itIsNodeCommand) { - values.comm_status = "NOK"; - } - - if (updateStatus) { - values.status = "NOK"; - } - // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); - if (saveToTb && Object.keys(values).length > 0) { - sendTelemetry(values, tbName) + if (saveToTb) { + sendTelemetry(values, tbName); } } + function sendNodesData() { + Object.keys(nodesData).forEach(node => { + if (nodesData[node]["status"] !== "OFFLINE") { + sendTelemetry(nodesData[node].readout, nodesData[node].tbname); + } + }) + } + /** * function handles requests from terminal @@ -1871,7 +1840,7 @@ exports.install = function(instance) { "number_of_luminaires": number_of_luminaires, "number_of_ok_luminaires": number_of_ok_luminaires, "number_of_nok_luminaires": number_of_nok_luminaires, - "edge_date_time": ts + "edge_date_time": ts - ts % 60000 //round to full minute }; sendTelemetry(values, SETTINGS.rvoTbName, ts); @@ -1934,11 +1903,11 @@ exports.install = function(instance) { clearInterval(interval); clearInterval(customTasksInterval); clearInterval(setCorrectTime); + clearInterval(sendNodeReadout); rsPort.close(); }); - - instance.on("0", flowdata => { + instance.on("0", _ => { main(); }) @@ -2142,20 +2111,17 @@ exports.install = function(instance) { value = parseInt(value); if (value > 0) value = value + 128; - //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime - params.type = "cmd"; + params.type = "node-onetime-write"; params.tbname = tbname; params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave + params.register = 1; + params.recipient = 1; params.byte4 = value; - params.rw = 1;//write + params.rw = 1; params.timestamp = PRIORITY_TYPES.high_priority; params.info = 'set dimming from platform'; //params.debug = true; - //ak linia je - //debug(params); logger.debug("dimming", params); @@ -2168,15 +2134,15 @@ exports.install = function(instance) { { let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read + params.register = 1; + params.recipient = 1; + params.rw = 0; params.timestamp = PRIORITY_TYPES.high_priority; params.info = 'read dimming (after set dimming from platform)'; - params.debug = true; + //params.debug = true; tasks.push(params); } @@ -2185,15 +2151,15 @@ exports.install = function(instance) { { let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; params.register = 76; - params.recipient = 1;//slave - params.rw = 0;//read + params.recipient = 1; + params.rw = 0; params.timestamp = PRIORITY_TYPES.high_priority; params.info = 'read Input Power (after set dimming from platform)'; - params.debug = true; + //params.debug = true; tasks.push(params); } @@ -2202,15 +2168,15 @@ exports.install = function(instance) { { let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; params.register = 75; - params.recipient = 1;//slave - params.rw = 0;//read + params.recipient = 1; + params.rw = 0; params.timestamp = PRIORITY_TYPES.high_priority; params.info = 'read Input Current (after set dimming from platform)'; - params.debug = true; + //params.debug = true; tasks.push(params); } @@ -2219,24 +2185,22 @@ exports.install = function(instance) { { let params = getParams(PRIORITY_TYPES.high_priority); - params.type = "cmd"; + params.type = "node-onetime-read"; params.tbname = tbname; params.address = node; params.register = 77; - params.recipient = 1;//slave - params.rw = 0;//read + params.recipient = 1; + params.rw = 0; params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read power factor - Cos phi (after set dimming from platform)'; - params.debug = true; + params.info = 'read power factor (after set dimming from platform)'; + //params.debug = true; tasks.push(params); } }, 4000); - nodeWasFound = true; - break; } } @@ -2803,7 +2767,7 @@ exports.install = function(instance) { values["dawn_time"] = timestamp; } - //FW verzia + //FW verzia nodu if (register == 89) { //formát: "Byte3: Byte2.Byte1 (Byte0)" values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; diff --git a/flow/db_init.js b/flow/db_init.js index 3c8cc58..e809037 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -62,9 +62,10 @@ exports.install = async function(instance) { //so we have to add empty line at the bottom of nodes table to avoid this. //now, remove empty lines from nodesData database: if (dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"]; + Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {}) dbs.settings = { - edge_fw_version: "2025-01-21", //rok-mesiac-den + edge_fw_version: "2025-01-30", //rok-mesiac-den language: responseSettings[0]["lang"], rvo_name: responseSettings[0]["rvo_name"], project_id: responseSettings[0]["project_id"], diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 7bd93bd..716ef7b 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -5,11 +5,10 @@ class DataToTbHandler { // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) this.timeToHoldTbValue = 30 * 60; //30 minutes - this.previousValues = {}; this.debug = false; this.messageCounter = 0; - + this.itIsNodeReadout = false; this.sender = ""; // if attribute change difference is less than limit value, we do not send to tb. @@ -25,11 +24,12 @@ class DataToTbHandler { Phase_2_power: 2, Phase_3_power: 2, total_power: 2, + total_energy: 1, Phase_1_pow_factor: 0.1, Phase_2_pow_factor: 0.1, Phase_3_pow_factor: 0.1, power_factor: 0.1, - lifetime: 0.5, + lifetime: 2, voltage: 2, power: 2, frequency: 3, @@ -59,28 +59,34 @@ class DataToTbHandler { return true; } - sendToTb(dataToTb, instance) { - let keys = Object.keys(dataToTb); + sendToTb(data, instance) { + + //not to modify data object, we do deep copy: + let dataCopy = JSON.parse(JSON.stringify(data)); + + let keys = Object.keys(dataCopy); if (keys.length == 0) { - if (this.debug) console.log("sendToTb received empty object", dataToTb); + if (this.debug) console.log("sendToTb received empty object", dataCopy); return; } let tbname = keys[0]; let ts; - let arrayOfValues = dataToTb[tbname]; + let arrayOfValues = dataCopy[tbname]; let arrayOfValuesToSend = []; for (let i = 0; i < arrayOfValues.length; i++) { + ts = arrayOfValues[i].ts; let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); if (!this.isEmptyObject(values)) { arrayOfValuesToSend.push({ ts: ts, values: values }); } + } if (arrayOfValuesToSend.length == 0) { @@ -99,15 +105,20 @@ class DataToTbHandler { instance.send(this.index, dataToTbModified); } + getDiffTimestamp(key) { //TODO set different value for given key!!! //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h return this.timeToHoldTbValue * 1000; } + prepareValuesForTb(tbname, timestamp, values) { let keys = Object.keys(values); + + if (keys.includes("lifetime")) this.itIsNodeReadout = true; + if (!this.previousValues.hasOwnProperty(tbname)) { this.previousValues[tbname] = {}; } @@ -115,6 +126,7 @@ class DataToTbHandler { //if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values); for (let i = 0; i < keys.length; i++) { + let key = keys[i]; let value = values[key]; @@ -123,15 +135,34 @@ class DataToTbHandler { continue; } - // attributeData ==> voltage: {ts:333333, value:5} + // attributeData ==> {voltage: {ts:333333, value:5}} let attributeData = this.previousValues[tbname][key]; let attributeToChange = false; if (key in this.attributeChangeLimit) attributeToChange = true; let limit = this.attributeChangeLimit[key]; + let timestampDiffToRemoveKey; + + //this will ensure "node statecode" will be sent just once an hour + if (this.itIsNodeReadout && key === "statecode") { + attributeData.value = value; + this.itIsNodeReadout = false; + timestampDiffToRemoveKey = 1 * 60 * 60 * 1000; // 1 hour + } + + if (key === "twilight_sensor" && value > 100) { + attributeData.value = value; + } + + //if edge, master or node version do not change, send just once a day: + if (["edge_fw_version", "master_node_version", "fw_version"].includes(key)) { + timestampDiffToRemoveKey = 24 * 60 * 60 * 1000; + } if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { + let diff = timestamp - attributeData.ts; - let timestampDiffToRemoveKey = this.getDiffTimestamp(key); + if (!timestampDiffToRemoveKey) timestampDiffToRemoveKey = this.getDiffTimestamp(key); + if (diff > timestampDiffToRemoveKey) { attributeData.ts = Date.now(); //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); From d97d90cf9517b5891f60d107a04c88e5d6e6fb35 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Tue, 6 May 2025 11:58:26 +0200 Subject: [PATCH 19/25] Fake accelerometer and handle hasMainSwitch --- addSwitch.py | 36 + config | 2 +- databases/accelerometer_db.js | 3055 +++++++++++++++++++++++++++++++++ databases/settings.table | 4 +- flow/cmd_manager.js | 115 +- flow/db_init.js | 3 +- flow/designer.json | 716 ++++---- flow/dido_controller.js | 114 +- flow/modbus_reader.js | 151 +- flow/show_dbdata.js | 22 +- flow/thermometer.js | 23 +- 11 files changed, 3692 insertions(+), 549 deletions(-) create mode 100644 addSwitch.py create mode 100644 databases/accelerometer_db.js diff --git a/addSwitch.py b/addSwitch.py new file mode 100644 index 0000000..e58f7f4 --- /dev/null +++ b/addSwitch.py @@ -0,0 +1,36 @@ +import os + +def process_set_file(): + """ + Checks if /root/flowserver exists, reads set.txt, and modifies the second line. + """ + default_folder = "/root/flowserver" if os.path.exists("/root/flowserver") else "/home/unipi/flowserver" + flag = 1 if default_folder == "/root/flowserver" else 0 + + try: + with open("/home/unipi/flowserver/databases/settings.table", "r") as f: + lines = f.readlines() + + if len(lines) >= 2: + lines[0] = lines[0].rstrip('\n') + "|has_main_switch:boolean\n" + second_line = lines[1].strip() # remove trailing newline + last_pipe_index = second_line.rfind("|") + + if last_pipe_index != -1: + modified_line = second_line[:last_pipe_index + 1] + str(flag) + "|" + second_line[last_pipe_index + 1:] + lines[1] = modified_line + else: + print("Warning: No '|' character found in the second line of set.txt") + + with open("/home/unipi/flowserver/databases/settings.table", "w") as f: + f.writelines(lines) + else: + print("Warning: settings.table has less than two lines.") + + except FileNotFoundError: + print("Error: settings.table not found.") + except Exception as e: + print(e) + +# if __name__ == "__main__": +process_set_file() diff --git a/config b/config index 6913e51..9472cb0 100644 --- a/config +++ b/config @@ -7,6 +7,6 @@ package#flow (Object) : { url: '/' } table.relays : line:number|tbname:string|contactor:number|profile:string table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number -table.settings : rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string +table.settings : rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string|has_main_switch:boolean table.pins : pin:string|type:string|line:number table.notifications : key:string|weight:string|sk:string|en:string diff --git a/databases/accelerometer_db.js b/databases/accelerometer_db.js new file mode 100644 index 0000000..fdfc7fd --- /dev/null +++ b/databases/accelerometer_db.js @@ -0,0 +1,3055 @@ +let naklony = { + "3993": { "label": "1/3A", "naklon": 0 }, + "3989": { "label": "1/3B", "naklon": 15 }, + "3976": { "label": "1/1", "naklon": 0 }, + "4181": { "label": "1/2", "naklon": 0 }, + "4376": { "label": "1/4", "naklon": 15 }, + "3868": { "label": "1/5", "naklon": 15 }, + "3728": { "label": "1/6", "naklon": 15 }, + "3730": { "label": "1/7", "naklon": 0 }, + "4374": { "label": "1/8", "naklon": 0 }, + "3718": { "label": "1/9", "naklon": 15 }, + "4375": { "label": "1/10", "naklon": 15 }, + "4172": { "label": "1/11", "naklon": 15 }, + "4176": { "label": "1/12", "naklon": 15 }, + "3800": { "label": "2/1", "naklon": 5 }, + "3823": { "label": "2/2", "naklon": 5 }, + "3905": { "label": "2/3", "naklon": 5 }, + "3803": { "label": "2/4", "naklon": 5 }, + "3817": { "label": "2/5", "naklon": 5 }, + "3818": { "label": "2/6", "naklon": 5 }, + "3811": { "label": "2/7", "naklon": 5 }, + "3915": { "label": "2/8", "naklon": 5 }, + "3843": { "label": "2/9", "naklon": 10 }, + "3827": { "label": "2/10", "naklon": 10 }, + "4220": { "label": "2/11", "naklon": 10 }, + "3826": { "label": "2/12", "naklon": 10 }, + "3834": { "label": "2/13", "naklon": 10 }, + "3838": { "label": "2/14", "naklon": 10 }, + "2823": { "label": "2/15", "naklon": 10 }, + "3750": { "label": "2/16", "naklon": 10 }, + "3378": { "label": "2/17", "naklon": 0 }, + "3376": { "label": "2/18", "naklon": 0 }, + "3379": { "label": "2/19", "naklon": 0 }, + "3285": { "label": "2/20", "naklon": 0 }, + "3382": { "label": "2/21", "naklon": 0 }, + "3383": { "label": "2/22", "naklon": 0 }, + "3375": { "label": "2/24", "naklon": 0 }, + "3381": { "label": "2/25", "naklon": 0 }, + "3380": { "label": "2/26", "naklon": 0 }, + "3828": { "label": "2/27", "naklon": 10 }, + "3377": { "label": "2/28", "naklon": 0 }, + "3591": { "label": "7/1", "naklon": 0 }, + "3587": { "label": "7/2", "naklon": 0 }, + "3468": { "label": "7/3", "naklon": 0 }, + "3466": { "label": "7/4", "naklon": 0 }, + "3474": { "label": "7/5", "naklon": 0 }, + "3481": { "label": "7/6", "naklon": 0 }, + "3589": { "label": "7/7", "naklon": 0 }, + "3472": { "label": "7/8", "naklon": 0 }, + "3473": { "label": "7/9", "naklon": 0 }, + "3584": { "label": "7/10", "naklon": 0 }, + "3571": { "label": "7/11", "naklon": 0 }, + "3476": { "label": "7/12", "naklon": 0 }, + "3590": { "label": "7/13", "naklon": 0 }, + "3478": { "label": "7/14", "naklon": 0 }, + "3471": { "label": "7/15", "naklon": 0 }, + "3467": { "label": "7/16", "naklon": 0 }, + "3479": { "label": "7/17", "naklon": 0 }, + "3480": { "label": "7/18", "naklon": 0 }, + "3477": { "label": "7/19", "naklon": 0 }, + "3362": { "label": "12/1", "naklon": 0 }, + "3355": { "label": "12/2", "naklon": 0 }, + "3356": { "label": "12/3", "naklon": 0 }, + "3349": { "label": "12/4", "naklon": 0 }, + "3357": { "label": "12/5", "naklon": 0 }, + "3350": { "label": "12/6", "naklon": 0 }, + "3353": { "label": "12/7", "naklon": 0 }, + "3539": { "label": "12/8", "naklon": 0 }, + "3050": { "label": "12/9", "naklon": 0 }, + "3361": { "label": "12/10", "naklon": 0 }, + "3360": { "label": "12/11", "naklon": 0 }, + "3358": { "label": "12/12", "naklon": 0 }, + "3541": { "label": "12/13", "naklon": 0 }, + "3551": { "label": "12/14", "naklon": 0 }, + "3514": { "label": "12/15", "naklon": 0 }, + "3547": { "label": "12/16", "naklon": 0 }, + "3562": { "label": "12/17", "naklon": 0 }, + "3525": { "label": "12/18", "naklon": 0 }, + "3558": { "label": "12/19", "naklon": 0 }, + "3511": { "label": "12/20", "naklon": 0 }, + "3058": { "label": "12/21", "naklon": 0 }, + "3062": { "label": "12/22", "naklon": 0 }, + "3070": { "label": "12/23", "naklon": 0 }, + "3447": { "label": "12/24", "naklon": 0 }, + "3056": { "label": "12/25", "naklon": 0 }, + "3057": { "label": "12/26", "naklon": 0 }, + "3071": { "label": "12/27", "naklon": 0 }, + "3061": { "label": "12/28", "naklon": 0 }, + "3069": { "label": "12/29", "naklon": 0 }, + "3077": { "label": "12/30", "naklon": 0 }, + "3080": { "label": "12/31", "naklon": 0 }, + "3081": { "label": "12/32", "naklon": 0 }, + "3078": { "label": "12/33", "naklon": 0 }, + "4037": { "label": "12/34", "naklon": 0 }, + "3067": { "label": "12/35", "naklon": 0 }, + "3051": { "label": "12/36", "naklon": 0 }, + "2924": { "label": "12/37", "naklon": 0 }, + "3060": { "label": "12/38", "naklon": 0 }, + "3073": { "label": "12/39", "naklon": 0 }, + "3469": { "label": "12/40", "naklon": 0 }, + "2923": { "label": "12/41", "naklon": 0 }, + "2925": { "label": "12/42", "naklon": 0 }, + "3359": { "label": "12/43", "naklon": 0 }, + "3055": { "label": "12/44", "naklon": 0 }, + "2922": { "label": "12/45", "naklon": 0 }, + "3084": { "label": "12/46", "naklon": 0 }, + "3064": { "label": "12/47", "naklon": 0 }, + "3066": { "label": "12/48", "naklon": 0 }, + "3072": { "label": "12/49", "naklon": 0 }, + "3415": { "label": "13/13A", "naklon": 0 }, + "3409": { "label": "13/13B", "naklon": 0 }, + "3399": { "label": "13/13C", "naklon": 0 }, + "3410": { "label": "13/29A", "naklon": 0 }, + "3407": { "label": "13/29B", "naklon": 0 }, + "3405": { "label": "13/34A", "naklon": 0 }, + "3404": { "label": "13/34B", "naklon": 0 }, + "3392": { "label": "13/45A", "naklon": 0 }, + "3386": { "label": "13/45B", "naklon": 0 }, + "3303": { "label": "13/60A", "naklon": 3 }, + "3302": { "label": "13/60B", "naklon": 3 }, + "3319": { "label": "13/70A", "naklon": 6 }, + "3317": { "label": "13/70B", "naklon": 6 }, + "3326": { "label": "13/70C", "naklon": 6 }, + "3351": { "label": "13/73A", "naklon": 6 }, + "3313": { "label": "13/73B", "naklon": 6 }, + "3316": { "label": "13/83A", "naklon": 0 }, + "3320": { "label": "13/83B", "naklon": 0 }, + "3336": { "label": "13/95A", "naklon": 0 }, + "3323": { "label": "13/95B", "naklon": 0 }, + "3339": { "label": "13/98A", "naklon": 0 }, + "3333": { "label": "13/98B", "naklon": 0 }, + "3425": { "label": "13/1", "naklon": 0 }, + "3419": { "label": "13/2", "naklon": 0 }, + "3416": { "label": "13/3", "naklon": 0 }, + "3332": { "label": "13/4", "naklon": 0 }, + "3422": { "label": "13/5", "naklon": 0 }, + "3417": { "label": "13/6", "naklon": 0 }, + "3418": { "label": "13/7", "naklon": 0 }, + "3424": { "label": "13/8", "naklon": 0 }, + "3334": { "label": "13/9", "naklon": 0 }, + "4363": { "label": "13/10", "naklon": 0 }, + "3423": { "label": "13/11", "naklon": 0 }, + "3406": { "label": "13/12", "naklon": 0 }, + "3411": { "label": "13/14", "naklon": 0 }, + "3412": { "label": "13/15", "naklon": 0 }, + "3413": { "label": "13/16", "naklon": 0 }, + "3414": { "label": "13/17", "naklon": 0 }, + "3391": { "label": "13/18", "naklon": 0 }, + "3370": { "label": "13/19", "naklon": 0 }, + "3369": { "label": "13/20", "naklon": 0 }, + "3408": { "label": "13/21", "naklon": 0 }, + "3393": { "label": "13/22", "naklon": 0 }, + "3128": { "label": "13/23", "naklon": 0 }, + "3197": { "label": "13/24", "naklon": 0 }, + "4364": { "label": "13/25", "naklon": 0 }, + "3155": { "label": "13/26", "naklon": 0 }, + "3196": { "label": "13/27", "naklon": 0 }, + "3144": { "label": "13/28", "naklon": 0 }, + "3150": { "label": "13/30", "naklon": 0 }, + "3390": { "label": "13/31", "naklon": 0 }, + "3401": { "label": "13/32", "naklon": 0 }, + "3275": { "label": "13/33", "naklon": 0 }, + "3371": { "label": "13/35", "naklon": 0 }, + "3389": { "label": "13/36", "naklon": 0 }, + "3388": { "label": "13/37", "naklon": 0 }, + "3365": { "label": "13/38", "naklon": 0 }, + "3363": { "label": "13/39", "naklon": 0 }, + "3397": { "label": "13/40", "naklon": 0 }, + "3567": { "label": "13/40/1", "naklon": 0 }, + "3387": { "label": "13/41", "naklon": 0 }, + "3403": { "label": "13/42", "naklon": 0 }, + "3147": { "label": "13/43", "naklon": 0 }, + "3141": { "label": "13/44", "naklon": 0 }, + "3364": { "label": "13/46", "naklon": 0 }, + "3367": { "label": "13/47", "naklon": 0 }, + "3368": { "label": "13/48", "naklon": 0 }, + "3402": { "label": "13/49", "naklon": 0 }, + "3366": { "label": "13/50", "naklon": 0 }, + "3395": { "label": "13/51", "naklon": 0 }, + "3372": { "label": "13/52", "naklon": 0 }, + "3394": { "label": "13/53", "naklon": 0 }, + "3308": { "label": "13/54", "naklon": 0 }, + "3847": { "label": "13/54/1", "naklon": 0 }, + "3312": { "label": "13/55", "naklon": 3 }, + "3309": { "label": "13/56", "naklon": 3 }, + "3311": { "label": "13/57", "naklon": 3 }, + "3310": { "label": "13/58", "naklon": 3 }, + "4365": { "label": "13/59", "naklon": 3 }, + "4093": { "label": "13/60/1", "naklon": 3 }, + "3304": { "label": "13/61", "naklon": 3 }, + "3307": { "label": "13/62", "naklon": 3 }, + "3271": { "label": "13/63", "naklon": 3 }, + "3301": { "label": "13/64", "naklon": 3 }, + "4366": { "label": "13/65", "naklon": 3 }, + "3300": { "label": "13/66", "naklon": 3 }, + "3185": { "label": "13/67", "naklon": 0 }, + "3184": { "label": "13/68", "naklon": 0 }, + "3188": { "label": "13/69", "naklon": 0 }, + "3343": { "label": "13/71", "naklon": 6 }, + "3819": { "label": "13/71/1", "naklon": 6 }, + "3880": { "label": "13/71/2", "naklon": 6 }, + "3348": { "label": "13/72", "naklon": 6 }, + "3344": { "label": "13/74", "naklon": 6 }, + "3346": { "label": "13/75", "naklon": 6 }, + "3597": { "label": "13/75/1", "naklon": 6 }, + "3345": { "label": "13/76", "naklon": 6 }, + "3322": { "label": "13/77", "naklon": 0 }, + "3325": { "label": "13/78", "naklon": 0 }, + "4310": { "label": "13/78/1", "naklon": 0 }, + "3315": { "label": "13/79", "naklon": 0 }, + "3328": { "label": "13/80", "naklon": 0 }, + "3321": { "label": "13/81", "naklon": 0 }, + "3324": { "label": "13/82", "naklon": 0 }, + "3140": { "label": "13/84", "naklon": 0 }, + "3153": { "label": "13/85", "naklon": 0 }, + "3145": { "label": "13/86", "naklon": 0 }, + "3134": { "label": "13/87", "naklon": 0 }, + "3151": { "label": "13/88", "naklon": 0 }, + "3143": { "label": "13/89", "naklon": 0 }, + "3318": { "label": "13/90", "naklon": 0 }, + "3347": { "label": "13/91", "naklon": 0 }, + "3314": { "label": "13/94", "naklon": 0 }, + "3331": { "label": "13/96", "naklon": 0 }, + "3327": { "label": "13/97", "naklon": 0 }, + "3329": { "label": "13/99", "naklon": 0 }, + "3337": { "label": "13/100", "naklon": 0 }, + "3330": { "label": "13/101", "naklon": 0 }, + "3335": { "label": "13/102", "naklon": 0 }, + "3420": { "label": "13/103", "naklon": 0 }, + "3421": { "label": "13/104", "naklon": 0 }, + "4361": { "label": "13/105", "naklon": 0 }, + "4362": { "label": "13/106", "naklon": 0 }, + "3526": { "label": "13/107", "naklon": 0 }, + "4424": { "label": "13/108", "naklon": null }, + "3561": { "label": "13/109", "naklon": null }, + "3527": { "label": "13/110", "naklon": null }, + "3521": { "label": "13/111", "naklon": null }, + "3552": { "label": "13/112", "naklon": null }, + "3544": { "label": "13/113", "naklon": null }, + "3534": { "label": "13/114", "naklon": null }, + "3516": { "label": "13/115", "naklon": null }, + "3513": { "label": "13/116", "naklon": null }, + "3565": { "label": "13/117", "naklon": null }, + "3866": { "label": "13/118", "naklon": null }, + "3136": { "label": "14/1", "naklon": 0 }, + "3137": { "label": "14/2", "naklon": 0 }, + "3135": { "label": "14/3", "naklon": 0 }, + "3149": { "label": "14/4", "naklon": 0 }, + "3142": { "label": "14/5", "naklon": 0 }, + "3342": { "label": "14/6", "naklon": 0 }, + "3340": { "label": "14/7", "naklon": 0 }, + "3152": { "label": "14/8", "naklon": 0 }, + "3886": { "label": "14/9", "naklon": 0 }, + "3341": { "label": "14/10", "naklon": null }, + "3352": { "label": "14/11", "naklon": null }, + "3396": { "label": "15/1", "naklon": 5 }, + "2784": { "label": "15/2", "naklon": 5 }, + "2788": { "label": "15/3", "naklon": 5 }, + "2801": { "label": "15/4", "naklon": 5 }, + "2815": { "label": "15/5", "naklon": 5 }, + "2829": { "label": "15/6", "naklon": 5 }, + "2796": { "label": "15/7", "naklon": 5 }, + "2803": { "label": "15/8", "naklon": 5 }, + "4304": { "label": "15/8/1", "naklon": 5 }, + "4311": { "label": "15/8/2", "naklon": 5 }, + "2821": { "label": "15/9", "naklon": 5 }, + "3400": { "label": "15/10", "naklon": 5 }, + "2799": { "label": "15/11", "naklon": 5 }, + "2804": { "label": "15/12", "naklon": 5 }, + "4303": { "label": "15/12/1", "naklon": 5 }, + "4309": { "label": "15/12/2", "naklon": 5 }, + "2806": { "label": "15/13", "naklon": 5 }, + "2787": { "label": "15/14", "naklon": 5 }, + "2809": { "label": "15/15", "naklon": 5 }, + "2814": { "label": "15/16", "naklon": 0 }, + "3934": { "label": "15/17", "naklon": 0 }, + "3192": { "label": "15/18", "naklon": 0 }, + "2812": { "label": "15/19", "naklon": 0 }, + "4070": { "label": "15/20", "naklon": 0 }, + "2826": { "label": "15/21", "naklon": 0 }, + "2808": { "label": "15/22", "naklon": 3 }, + "2795": { "label": "15/23", "naklon": 3 }, + "2807": { "label": "15/24", "naklon": 3 }, + "2831": { "label": "15/25", "naklon": 0 }, + "2862": { "label": "15/26", "naklon": null }, + "2861": { "label": "15/27", "naklon": 0 }, + "2866": { "label": "15/28", "naklon": 0 }, + "3546": { "label": "15/28/1", "naklon": 0 }, + "4146": { "label": "15/29", "naklon": 0 }, + "3001": { "label": "15/29/1", "naklon": 0 }, + "2835": { "label": "15/30", "naklon": 0 }, + "2874": { "label": "15/31", "naklon": 0 }, + "2852": { "label": "15/32", "naklon": 0 }, + "2851": { "label": "15/33", "naklon": 0 }, + "3560": { "label": "15/33/1", "naklon": 0 }, + "2858": { "label": "15/34", "naklon": 0 }, + "4306": { "label": "15/34/1", "naklon": 0 }, + "2856": { "label": "15/35", "naklon": 0 }, + "2847": { "label": "15/36", "naklon": 0 }, + "3556": { "label": "15/36/1", "naklon": 0 }, + "3950": { "label": "15/37", "naklon": 0 }, + "2848": { "label": "15/38", "naklon": 0 }, + "2863": { "label": "15/39", "naklon": 0 }, + "4313": { "label": "15/39/1", "naklon": 0 }, + "2873": { "label": "15/40", "naklon": 0 }, + "2984": { "label": "15/40/1", "naklon": 0 }, + "2854": { "label": "15/41", "naklon": 0 }, + "2859": { "label": "15/42", "naklon": 0 }, + "2872": { "label": "15/43", "naklon": 0 }, + "2849": { "label": "15/44", "naklon": 0 }, + "2864": { "label": "15/45", "naklon": 0 }, + "3553": { "label": "15/46", "naklon": null }, + "3021": { "label": "15/47", "naklon": null }, + "3008": { "label": "15/47/1", "naklon": null }, + "3017": { "label": "15/48", "naklon": null }, + "3014": { "label": "15/49", "naklon": null }, + "2981": { "label": "15/50", "naklon": null }, + "3020": { "label": "15/51", "naklon": null }, + "3027": { "label": "15/52", "naklon": null }, + "3006": { "label": "15/53", "naklon": null }, + "3012": { "label": "15/54", "naklon": null }, + "2738": { "label": "15/55", "naklon": 0 }, + "2725": { "label": "15/56", "naklon": 0 }, + "3016": { "label": "15/57", "naklon": null }, + "3005": { "label": "15/58", "naklon": null }, + "3009": { "label": "15/58/1", "naklon": null }, + "3010": { "label": "15/59", "naklon": 0 }, + "3538": { "label": "15/59/1", "naklon": 0 }, + "4145": { "label": "15/59/2", "naklon": 0 }, + "2713": { "label": "15/60", "naklon": 0 }, + "2789": { "label": "15/61", "naklon": 0 }, + "2736": { "label": "15/62", "naklon": 0 }, + "2739": { "label": "15/63", "naklon": 0 }, + "4346": { "label": "15/64", "naklon": 0 }, + "2731": { "label": "15/65", "naklon": 0 }, + "2818": { "label": "15/66", "naklon": 0 }, + "3557": { "label": "15/66/1", "naklon": 0 }, + "2822": { "label": "15/67", "naklon": 0 }, + "2833": { "label": "15/68", "naklon": 0 }, + "2834": { "label": "15/69", "naklon": 0 }, + "3097": { "label": "15/70", "naklon": 0 }, + "2824": { "label": "15/71", "naklon": 0 }, + "2828": { "label": "15/72", "naklon": 0 }, + "2600": { "label": "15/73", "naklon": 0 }, + "4314": { "label": "15/73/1", "naklon": 0 }, + "2832": { "label": "15/74", "naklon": 0 }, + "4360": { "label": "15/75", "naklon": 0 }, + "2846": { "label": "15/76", "naklon": 0 }, + "2584": { "label": "15/77", "naklon": 0 }, + "2843": { "label": "15/78", "naklon": 0 }, + "4308": { "label": "15/78/1", "naklon": 0 }, + "4144": { "label": "15/79", "naklon": null }, + "2844": { "label": "15/80", "naklon": 0 }, + "2842": { "label": "15/81", "naklon": 0 }, + "2838": { "label": "15/82", "naklon": 0 }, + "2837": { "label": "15/83", "naklon": null }, + "3555": { "label": "15/83/1", "naklon": null }, + "2839": { "label": "15/84", "naklon": 0 }, + "2830": { "label": "15/85", "naklon": 0 }, + "4066": { "label": "15/86", "naklon": 0 }, + "2836": { "label": "15/87", "naklon": 0 }, + "2911": { "label": "15/88", "naklon": 0 }, + "2825": { "label": "15/89", "naklon": 0 }, + "2811": { "label": "15/90", "naklon": 0 }, + "3740": { "label": "15/94", "naklon": 15 }, + "2734": { "label": "15/95", "naklon": 15 }, + "3105": { "label": "15/96", "naklon": 15 }, + "2732": { "label": "15/97", "naklon": 15 }, + "2727": { "label": "15/98", "naklon": 15 }, + "2735": { "label": "15/99", "naklon": 15 }, + "3926": { "label": "15/100", "naklon": 15 }, + "2918": { "label": "15/101", "naklon": 0 }, + "2705": { "label": "15/102", "naklon": 10 }, + "2708": { "label": "15/103", "naklon": 10 }, + "2721": { "label": "15/104", "naklon": 10 }, + "2719": { "label": "15/105", "naklon": 10 }, + "2919": { "label": "15/106", "naklon": 10 }, + "3101": { "label": "15/107", "naklon": 10 }, + "2853": { "label": "15/108", "naklon": 10 }, + "2715": { "label": "15/109", "naklon": 10 }, + "2712": { "label": "15/110", "naklon": 0 }, + "2711": { "label": "15/111", "naklon": 0 }, + "2709": { "label": "15/112", "naklon": 0 }, + "2710": { "label": "15/113", "naklon": 0 }, + "2907": { "label": "15/114", "naklon": 0 }, + "2906": { "label": "15/115", "naklon": 0 }, + "2908": { "label": "15/116", "naklon": 0 }, + "2901": { "label": "15/117", "naklon": 0 }, + "2913": { "label": "15/118", "naklon": 0 }, + "3656": { "label": "15/119", "naklon": 0 }, + "2781": { "label": "15/120", "naklon": 0 }, + "2802": { "label": "15/121", "naklon": 0 }, + "2791": { "label": "15/122", "naklon": 0 }, + "2783": { "label": "15/123", "naklon": 0 }, + "2794": { "label": "15/124", "naklon": 0 }, + "3139": { "label": "15/125", "naklon": 0 }, + "3229": { "label": "15/126", "naklon": 5 }, + "3239": { "label": "15/127", "naklon": 5 }, + "3230": { "label": "15/128", "naklon": 5 }, + "4016": { "label": "15/129", "naklon": null }, + "3608": { "label": "16/16A", "naklon": 0 }, + "4151": { "label": "16/16B", "naklon": 0 }, + "3710": { "label": "16/1", "naklon": 0 }, + "3726": { "label": "16/2", "naklon": 0 }, + "3743": { "label": "16/3", "naklon": 0 }, + "3724": { "label": "16/4", "naklon": 0 }, + "3712": { "label": "16/5", "naklon": 0 }, + "3739": { "label": "16/6", "naklon": 0 }, + "3742": { "label": "16/7", "naklon": 0 }, + "3731": { "label": "16/8", "naklon": 0 }, + "3727": { "label": "16/9", "naklon": 0 }, + "3729": { "label": "16/10", "naklon": 0 }, + "3736": { "label": "16/11", "naklon": 0 }, + "3713": { "label": "16/12", "naklon": 0 }, + "3715": { "label": "16/13", "naklon": 0 }, + "3733": { "label": "16/14", "naklon": 0 }, + "3732": { "label": "16/15", "naklon": 0 }, + "3634": { "label": "16/17", "naklon": 0 }, + "3610": { "label": "16/18", "naklon": 0 }, + "3626": { "label": "16/19", "naklon": 0 }, + "3618": { "label": "16/20", "naklon": 0 }, + "3619": { "label": "16/21", "naklon": 0 }, + "3628": { "label": "16/22", "naklon": 0 }, + "3615": { "label": "16/23", "naklon": 0 }, + "3609": { "label": "16/24", "naklon": 0 }, + "3606": { "label": "16/25", "naklon": 0 }, + "3624": { "label": "16/26", "naklon": 0 }, + "3627": { "label": "16/27", "naklon": 0 }, + "3607": { "label": "16/28", "naklon": 0 }, + "4370": { "label": "16/29", "naklon": 0 }, + "3692": { "label": "16/30", "naklon": 0 }, + "3614": { "label": "16/31", "naklon": 0 }, + "3623": { "label": "16/32", "naklon": 0 }, + "4373": { "label": "16/33", "naklon": 0 }, + "3620": { "label": "16/34", "naklon": 0 }, + "3685": { "label": "16/35", "naklon": 0 }, + "3616": { "label": "16/36", "naklon": 0 }, + "3686": { "label": "16/37", "naklon": 0 }, + "3688": { "label": "16/38", "naklon": 0 }, + "3684": { "label": "16/39", "naklon": 0 }, + "3825": { "label": "16/40", "naklon": 0 }, + "3865": { "label": "16/41", "naklon": 0 }, + "3824": { "label": "16/42", "naklon": 0 }, + "3871": { "label": "16/43", "naklon": 0 }, + "3801": { "label": "16/44", "naklon": 0 }, + "3862": { "label": "16/45", "naklon": 0 }, + "3876": { "label": "16/46", "naklon": 0 }, + "3861": { "label": "16/47", "naklon": 0 }, + "4215": { "label": "16/48", "naklon": 0 }, + "3605": { "label": "16/49", "naklon": 10 }, + "3603": { "label": "16/50", "naklon": 10 }, + "3592": { "label": "16/51", "naklon": 10 }, + "3598": { "label": "16/52", "naklon": 10 }, + "3594": { "label": "16/53", "naklon": 10 }, + "3593": { "label": "16/54", "naklon": 10 }, + "3630": { "label": "16/55", "naklon": 10 }, + "3763": { "label": "16/56", "naklon": 10 }, + "3746": { "label": "16/57", "naklon": 10 }, + "3699": { "label": "16/58", "naklon": 0 }, + "3698": { "label": "16/59", "naklon": 0 }, + "3709": { "label": "16/60", "naklon": null }, + "3708": { "label": "16/61", "naklon": 0 }, + "3703": { "label": "16/62", "naklon": null }, + "3700": { "label": "16/63", "naklon": 0 }, + "3701": { "label": "16/64", "naklon": null }, + "3704": { "label": "16/65", "naklon": 0 }, + "3707": { "label": "16/66", "naklon": null }, + "3810": { "label": "16/67", "naklon": 0 }, + "4208": { "label": "16/68", "naklon": null }, + "3867": { "label": "16/69", "naklon": 0 }, + "4372": { "label": "16/70", "naklon": null }, + "3881": { "label": "16/71", "naklon": 0 }, + "4204": { "label": "16/72", "naklon": null }, + "3869": { "label": "16/73", "naklon": 0 }, + "4202": { "label": "16/74", "naklon": null }, + "3830": { "label": "16/75", "naklon": 0 }, + "3870": { "label": "16/76", "naklon": null }, + "3964": { "label": "16/77", "naklon": 0 }, + "3849": { "label": "16/78", "naklon": 0 }, + "3694": { "label": "16/79", "naklon": null }, + "3717": { "label": "16/80", "naklon": 0 }, + "3877": { "label": "16/81", "naklon": null }, + "3755": { "label": "16/82", "naklon": 0 }, + "3725": { "label": "16/83", "naklon": null }, + "3716": { "label": "16/84", "naklon": 0 }, + "3696": { "label": "16/85", "naklon": null }, + "3702": { "label": "16/86", "naklon": 0 }, + "3706": { "label": "16/87", "naklon": null }, + "3850": { "label": "16/88", "naklon": 0 }, + "3848": { "label": "16/89", "naklon": null }, + "3737": { "label": "16/90", "naklon": null }, + "3705": { "label": "16/91", "naklon": 0 }, + "3723": { "label": "16/92", "naklon": null }, + "3842": { "label": "16/93", "naklon": 0 }, + "3735": { "label": "16/94", "naklon": null }, + "3719": { "label": "16/95", "naklon": 0 }, + "3697": { "label": "16/96", "naklon": 0 }, + "3711": { "label": "16/97", "naklon": 0 }, + "4107": { "label": "16/98", "naklon": null }, + "4112": { "label": "16/99", "naklon": 5 }, + "4106": { "label": "16/100", "naklon": 5 }, + "4111": { "label": "16/101", "naklon": 5 }, + "4091": { "label": "16/102", "naklon": null }, + "4104": { "label": "16/103", "naklon": 5 }, + "4094": { "label": "16/104", "naklon": null }, + "4089": { "label": "16/105", "naklon": 5 }, + "4095": { "label": "16/106", "naklon": null }, + "4102": { "label": "16/107", "naklon": 5 }, + "4105": { "label": "16/108", "naklon": null }, + "3859": { "label": "16/109", "naklon": null }, + "4090": { "label": "16/110", "naklon": 5 }, + "4110": { "label": "16/111", "naklon": null }, + "4097": { "label": "16/112", "naklon": 5 }, + "4099": { "label": "16/113", "naklon": null }, + "4096": { "label": "16/114", "naklon": null }, + "4101": { "label": "16/115", "naklon": 5 }, + "4109": { "label": "16/116", "naklon": null }, + "4092": { "label": "16/117", "naklon": 5 }, + "3631": { "label": "16/118", "naklon": null }, + "3632": { "label": "16/119", "naklon": 0 }, + "3641": { "label": "16/120", "naklon": null }, + "3638": { "label": "16/121", "naklon": 0 }, + "3637": { "label": "16/122", "naklon": null }, + "4142": { "label": "16/123", "naklon": null }, + "4147": { "label": "16/124", "naklon": 5 }, + "4139": { "label": "16/125", "naklon": 5 }, + "3172": { "label": "20/30A", "naklon": 2 }, + "3164": { "label": "20/30B", "naklon": 2 }, + "3247": { "label": "20/1", "naklon": 10 }, + "3296": { "label": "20/2", "naklon": 10 }, + "3246": { "label": "20/3", "naklon": 10 }, + "3270": { "label": "20/4", "naklon": 10 }, + "3294": { "label": "20/5", "naklon": 10 }, + "3298": { "label": "20/6", "naklon": 10 }, + "3297": { "label": "20/7", "naklon": 10 }, + "3293": { "label": "20/8", "naklon": 10 }, + "3299": { "label": "20/9", "naklon": 10 }, + "3295": { "label": "20/10", "naklon": 10 }, + "3244": { "label": "20/11", "naklon": 10 }, + "3249": { "label": "20/12", "naklon": 10 }, + "3268": { "label": "20/13", "naklon": 10 }, + "3245": { "label": "20/14", "naklon": 10 }, + "3278": { "label": "20/15", "naklon": 10 }, + "3280": { "label": "20/16", "naklon": 10 }, + "3292": { "label": "20/17", "naklon": 10 }, + "3287": { "label": "20/18", "naklon": 10 }, + "3282": { "label": "20/19", "naklon": 10 }, + "3273": { "label": "20/20", "naklon": 0 }, + "3157": { "label": "20/21", "naklon": 0 }, + "3283": { "label": "20/22", "naklon": 0 }, + "3159": { "label": "20/23", "naklon": 0 }, + "3289": { "label": "20/24", "naklon": 0 }, + "3163": { "label": "20/25", "naklon": 0 }, + "3126": { "label": "20/26", "naklon": 0 }, + "3171": { "label": "20/27", "naklon": 0 }, + "3162": { "label": "20/28", "naklon": 0 }, + "3279": { "label": "20/29", "naklon": 10 }, + "3173": { "label": "20/31", "naklon": 0 }, + "3127": { "label": "20/32", "naklon": 0 }, + "3131": { "label": "20/33", "naklon": 0 }, + "3129": { "label": "20/34", "naklon": 0 }, + "3276": { "label": "20/35", "naklon": 2 }, + "3290": { "label": "20/36", "naklon": 2 }, + "3132": { "label": "20/37", "naklon": 0 }, + "3138": { "label": "20/38", "naklon": 0 }, + "3272": { "label": "20/39", "naklon": 0 }, + "3291": { "label": "20/40", "naklon": 0 }, + "3277": { "label": "20/41", "naklon": 10 }, + "3286": { "label": "20/42", "naklon": 10 }, + "3281": { "label": "20/43", "naklon": 10 }, + "3288": { "label": "20/44", "naklon": 10 }, + "3284": { "label": "20/45", "naklon": 10 }, + "3167": { "label": "20/46", "naklon": 10 }, + "3872": { "label": "20/47", "naklon": 0 }, + "3124": { "label": "20/48", "naklon": 0 }, + "3133": { "label": "20/49", "naklon": 0 }, + "3158": { "label": "20/50", "naklon": 0 }, + "3169": { "label": "20/51", "naklon": 0 }, + "3146": { "label": "20/52", "naklon": 0 }, + "3160": { "label": "20/53", "naklon": 0 }, + "3248": { "label": "20/54", "naklon": 0 }, + "3156": { "label": "20/55", "naklon": 0 }, + "3161": { "label": "20/56", "naklon": 0 }, + "3170": { "label": "20/57", "naklon": 0 }, + "3168": { "label": "20/58", "naklon": 0 }, + "3125": { "label": "20/59", "naklon": 0 }, + "3166": { "label": "20/60", "naklon": 0 }, + "3130": { "label": "20/61", "naklon": 0 }, + "3563": { "label": "20/62", "naklon": 0 }, + "3550": { "label": "20/63", "naklon": null }, + "3269": { "label": "20/67", "naklon": 0 }, + "2921": { "label": "21/1", "naklon": 0 }, + "2707": { "label": "21/2", "naklon": 0 }, + "2723": { "label": "21/3", "naklon": 0 }, + "2716": { "label": "21/4", "naklon": 0 }, + "2717": { "label": "21/5", "naklon": 0 }, + "2720": { "label": "21/6", "naklon": 0 }, + "2722": { "label": "21/7", "naklon": 0 }, + "2718": { "label": "21/8", "naklon": 0 }, + "2724": { "label": "21/9", "naklon": 0 }, + "2742": { "label": "21/10", "naklon": 0 }, + "2743": { "label": "21/11", "naklon": 0 }, + "2733": { "label": "21/12", "naklon": 0 }, + "2903": { "label": "21/14", "naklon": 0 }, + "2917": { "label": "21/15", "naklon": 0 }, + "2897": { "label": "21/16", "naklon": 0 }, + "2914": { "label": "21/17", "naklon": 0 }, + "2898": { "label": "21/18", "naklon": 0 }, + "2773": { "label": "21/19", "naklon": 0 }, + "2895": { "label": "21/20", "naklon": 0 }, + "2896": { "label": "21/21", "naklon": 0 }, + "2771": { "label": "21/22", "naklon": 0 }, + "2772": { "label": "21/23", "naklon": 0 }, + "2793": { "label": "21/24", "naklon": 0 }, + "2902": { "label": "21/25", "naklon": 0 }, + "2910": { "label": "21/26", "naklon": 0 }, + "2909": { "label": "21/27", "naklon": 0 }, + "2920": { "label": "21/28", "naklon": 0 }, + "2916": { "label": "21/29", "naklon": 0 }, + "2904": { "label": "21/30", "naklon": 0 }, + "2915": { "label": "21/31", "naklon": 0 }, + "2905": { "label": "21/32", "naklon": 0 }, + "2900": { "label": "21/33", "naklon": 0 }, + "2899": { "label": "21/34", "naklon": 0 }, + "2741": { "label": "21/36", "naklon": 0 }, + "2714": { "label": "21/37", "naklon": 0 }, + "2701": { "label": "21/38", "naklon": 0 }, + "2703": { "label": "21/39", "naklon": 0 }, + "2704": { "label": "21/40", "naklon": 0 }, + "2702": { "label": "21/41", "naklon": 0 }, + "2706": { "label": "21/42", "naklon": 0 }, + "2776": { "label": "21/43", "naklon": 0 }, + "2770": { "label": "21/44", "naklon": 0 }, + "2786": { "label": "21/45", "naklon": 0 }, + "2779": { "label": "21/46", "naklon": 0 }, + "2782": { "label": "21/47", "naklon": 0 }, + "2785": { "label": "21/48", "naklon": 0 }, + "2769": { "label": "21/49", "naklon": 0 }, + "3354": { "label": "21/50", "naklon": 0 }, + "2778": { "label": "21/51", "naklon": 0 }, + "2775": { "label": "21/52", "naklon": 0 }, + "2780": { "label": "21/53", "naklon": 0 }, + "2790": { "label": "21/54", "naklon": 0 }, + "2792": { "label": "21/55", "naklon": 0 }, + "2774": { "label": "21/56", "naklon": 0 }, + "2630": { "label": "22/1", "naklon": 0 }, + "2631": { "label": "22/2", "naklon": 0 }, + "2632": { "label": "22/3", "naklon": 0 }, + "2633": { "label": "22/4", "naklon": 0 }, + "2634": { "label": "22/5", "naklon": 0 }, + "2636": { "label": "22/6", "naklon": 0 }, + "4367": { "label": "22/7", "naklon": 0 }, + "2637": { "label": "22/8", "naklon": 0 }, + "2638": { "label": "22/9", "naklon": 0 }, + "2639": { "label": "22/10", "naklon": 0 }, + "2640": { "label": "22/11", "naklon": 0 }, + "2641": { "label": "22/12", "naklon": 0 }, + "2642": { "label": "22/13", "naklon": 0 }, + "2643": { "label": "22/14", "naklon": 0 }, + "2644": { "label": "22/15", "naklon": 0 }, + "2645": { "label": "22/16", "naklon": 0 }, + "2646": { "label": "22/17", "naklon": 0 }, + "2647": { "label": "22/18", "naklon": 0 }, + "2648": { "label": "22/19", "naklon": 0 }, + "2649": { "label": "22/20", "naklon": 0 }, + "2650": { "label": "22/21", "naklon": 0 }, + "2651": { "label": "22/22", "naklon": 0 }, + "4103": { "label": "22/23", "naklon": 0 }, + "2653": { "label": "22/24", "naklon": 0 }, + "2654": { "label": "22/25", "naklon": 0 }, + "2655": { "label": "22/26", "naklon": 0 }, + "2656": { "label": "22/27", "naklon": 0 }, + "2657": { "label": "22/28", "naklon": 0 }, + "2658": { "label": "22/29", "naklon": 0 }, + "2659": { "label": "22/30", "naklon": 0 }, + "2660": { "label": "22/31", "naklon": 0 }, + "2661": { "label": "22/32", "naklon": 0 }, + "3015": { "label": "22/32/1", "naklon": 0 }, + "2662": { "label": "22/33", "naklon": 0 }, + "2663": { "label": "22/34", "naklon": 0 }, + "2664": { "label": "22/35", "naklon": 0 }, + "2665": { "label": "22/36", "naklon": 0 }, + "2666": { "label": "22/37", "naklon": 0 }, + "2667": { "label": "22/38", "naklon": 0 }, + "2668": { "label": "22/39", "naklon": 0 }, + "2669": { "label": "22/40", "naklon": null }, + "2670": { "label": "22/41", "naklon": null }, + "2671": { "label": "22/42", "naklon": null }, + "3087": { "label": "23/9A", "naklon": 10 }, + "3090": { "label": "23/9B", "naklon": 10 }, + "3075": { "label": "23/1", "naklon": 0 }, + "3089": { "label": "23/2", "naklon": 0 }, + "3088": { "label": "23/3", "naklon": 0 }, + "3228": { "label": "23/4", "naklon": 10 }, + "2751": { "label": "23/5", "naklon": 10 }, + "3243": { "label": "23/6", "naklon": 10 }, + "4349": { "label": "23/7", "naklon": 10 }, + "3102": { "label": "23/8", "naklon": 10 }, + "3099": { "label": "23/10", "naklon": 10 }, + "3103": { "label": "23/11", "naklon": 10 }, + "3093": { "label": "23/12", "naklon": 10 }, + "3098": { "label": "23/13", "naklon": 10 }, + "3092": { "label": "23/14", "naklon": 10 }, + "3233": { "label": "23/15", "naklon": 0 }, + "3095": { "label": "23/16", "naklon": 0 }, + "4074": { "label": "23/17", "naklon": 0 }, + "2729": { "label": "23/18", "naklon": 0 }, + "3234": { "label": "23/19", "naklon": 0 }, + "3094": { "label": "23/20", "naklon": 0 }, + "2817": { "label": "23/21", "naklon": 0 }, + "3226": { "label": "23/22", "naklon": 0 }, + "3225": { "label": "23/23", "naklon": 0 }, + "3237": { "label": "23/24", "naklon": 0 }, + "4250": { "label": "23/25", "naklon": 0 }, + "3059": { "label": "23/26", "naklon": 10 }, + "3238": { "label": "23/27", "naklon": 10 }, + "3242": { "label": "23/28", "naklon": 10 }, + "3236": { "label": "23/29", "naklon": 10 }, + "3112": { "label": "23/30", "naklon": 10 }, + "3224": { "label": "23/31", "naklon": 0 }, + "4298": { "label": "23/32", "naklon": 10 }, + "4150": { "label": "23/33", "naklon": 10 }, + "3091": { "label": "23/34", "naklon": 10 }, + "3771": { "label": "25/1", "naklon": 5 }, + "3777": { "label": "25/2", "naklon": 5 }, + "3749": { "label": "25/3", "naklon": 5 }, + "3769": { "label": "25/4", "naklon": 5 }, + "3765": { "label": "25/5", "naklon": 5 }, + "3785": { "label": "25/6", "naklon": 5 }, + "3761": { "label": "25/7", "naklon": 5 }, + "3758": { "label": "25/8", "naklon": 5 }, + "3766": { "label": "25/9", "naklon": 5 }, + "3779": { "label": "25/10", "naklon": 5 }, + "3899": { "label": "25/11", "naklon": 10 }, + "3922": { "label": "25/12", "naklon": 10 }, + "3912": { "label": "25/13", "naklon": 10 }, + "3933": { "label": "25/14", "naklon": 10 }, + "3921": { "label": "25/15", "naklon": 10 }, + "4300": { "label": "25/16", "naklon": 10 }, + "3927": { "label": "25/17", "naklon": 10 }, + "3212": { "label": "25/18", "naklon": 10 }, + "3208": { "label": "25/19", "naklon": 10 }, + "3207": { "label": "25/20", "naklon": 10 }, + "4294": { "label": "25/21", "naklon": 10 }, + "3209": { "label": "25/22", "naklon": 10 }, + "3745": { "label": "25/23", "naklon": 10 }, + "3762": { "label": "25/24", "naklon": 10 }, + "3215": { "label": "25/25", "naklon": 10 }, + "3210": { "label": "25/26", "naklon": 10 }, + "3211": { "label": "25/27", "naklon": 10 }, + "3206": { "label": "25/28", "naklon": 10 }, + "3918": { "label": "25/29", "naklon": 5 }, + "3928": { "label": "25/30", "naklon": 5 }, + "3917": { "label": "25/31", "naklon": 5 }, + "4029": { "label": "25/32", "naklon": 0 }, + "4038": { "label": "25/33", "naklon": 0 }, + "4022": { "label": "25/34", "naklon": 0 }, + "3937": { "label": "25/35", "naklon": 0 }, + "3190": { "label": "25/36", "naklon": 0 }, + "3835": { "label": "25/37", "naklon": 0 }, + "3201": { "label": "25/38", "naklon": 0 }, + "3193": { "label": "25/39", "naklon": 0 }, + "3191": { "label": "25/40", "naklon": 0 }, + "3203": { "label": "25/41", "naklon": 0 }, + "4301": { "label": "25/42", "naklon": 0 }, + "3223": { "label": "25/43", "naklon": 0 }, + "3202": { "label": "25/44", "naklon": 0 }, + "3205": { "label": "25/45", "naklon": 0 }, + "3194": { "label": "25/46", "naklon": 0 }, + "3198": { "label": "25/47", "naklon": 0 }, + "3204": { "label": "25/48", "naklon": 0 }, + "3200": { "label": "25/49", "naklon": 0 }, + "3216": { "label": "25/50", "naklon": 0 }, + "3932": { "label": "25/51", "naklon": 5 }, + "3923": { "label": "25/52", "naklon": 5 }, + "3913": { "label": "25/53", "naklon": 5 }, + "3900": { "label": "25/54", "naklon": 5 }, + "4189": { "label": "25/55", "naklon": 10 }, + "4191": { "label": "25/56", "naklon": 10 }, + "3790": { "label": "25/57", "naklon": 10 }, + "4190": { "label": "25/58", "naklon": 10 }, + "4302": { "label": "25/59", "naklon": 0 }, + "4351": { "label": "25/60", "naklon": 0 }, + "4075": { "label": "25/61", "naklon": 0 }, + "4071": { "label": "25/62", "naklon": 0 }, + "3784": { "label": "25/63", "naklon": 0 }, + "4065": { "label": "25/64", "naklon": 0 }, + "4297": { "label": "25/65", "naklon": 0 }, + "4295": { "label": "25/66", "naklon": 0 }, + "4299": { "label": "25/67", "naklon": 0 }, + "3772": { "label": "25/68", "naklon": 0 }, + "3941": { "label": "25/69", "naklon": 0 }, + "3782": { "label": "25/70", "naklon": 0 }, + "4068": { "label": "25/71", "naklon": 0 }, + "4073": { "label": "25/72", "naklon": 0 }, + "3901": { "label": "25/73", "naklon": 5 }, + "4296": { "label": "25/74", "naklon": 5 }, + "3910": { "label": "25/75", "naklon": 5 }, + "4293": { "label": "25/76", "naklon": 5 }, + "3919": { "label": "25/77", "naklon": 5 }, + "3924": { "label": "25/78", "naklon": 5 }, + "3909": { "label": "25/79", "naklon": 5 }, + "3896": { "label": "25/80", "naklon": 5 }, + "3911": { "label": "25/81", "naklon": 5 }, + "2819": { "label": "25/82", "naklon": 15 }, + "3776": { "label": "25/83", "naklon": 15 }, + "3752": { "label": "25/84", "naklon": 15 }, + "4353": { "label": "25/85", "naklon": 15 }, + "3791": { "label": "25/86", "naklon": 0 }, + "3770": { "label": "25/87", "naklon": 0 }, + "3760": { "label": "25/88", "naklon": 0 }, + "3115": { "label": "25/89", "naklon": 0 }, + "3738": { "label": "25/90", "naklon": 0 }, + "3748": { "label": "25/91", "naklon": 0 }, + "3753": { "label": "25/92", "naklon": 0 }, + "3775": { "label": "25/93", "naklon": 0 }, + "3778": { "label": "25/94", "naklon": 5 }, + "4078": { "label": "25/95", "naklon": 5 }, + "4088": { "label": "25/96", "naklon": 5 }, + "3856": { "label": "25/97", "naklon": 5 }, + "4084": { "label": "25/98", "naklon": 5 }, + "4069": { "label": "25/99", "naklon": 5 }, + "4188": { "label": "25/100", "naklon": 5 }, + "4064": { "label": "25/101", "naklon": 5 }, + "3792": { "label": "25/102", "naklon": 5 }, + "3780": { "label": "25/103", "naklon": 5 }, + "4034": { "label": "25/104", "naklon": 5 }, + "4072": { "label": "25/105", "naklon": 5 }, + "3884": { "label": "25/106", "naklon": 5 }, + "3793": { "label": "25/107", "naklon": 5 }, + "3863": { "label": "25/108", "naklon": 5 }, + "3855": { "label": "25/109", "naklon": 5 }, + "3846": { "label": "33/1", "naklon": 0 }, + "3943": { "label": "33/2", "naklon": 0 }, + "3903": { "label": "33/3", "naklon": 0 }, + "3982": { "label": "33/4", "naklon": 0 }, + "3879": { "label": "33/5", "naklon": 0 }, + "3887": { "label": "33/6", "naklon": 0 }, + "3920": { "label": "33/7", "naklon": 5 }, + "3942": { "label": "33/8", "naklon": 5 }, + "3829": { "label": "33/9", "naklon": null }, + "3452": { "label": "34/79A", "naklon": 6 }, + "3460": { "label": "34/79B", "naklon": 6 }, + "3443": { "label": "34/90A", "naklon": 6 }, + "3445": { "label": "34/90B", "naklon": 6 }, + "3457": { "label": "34/90C", "naklon": 6 }, + "3461": { "label": "34/97A", "naklon": 6 }, + "3463": { "label": "34/97B", "naklon": 6 }, + "3426": { "label": "34/98A", "naklon": 6 }, + "3444": { "label": "34/98B", "naklon": 6 }, + "3453": { "label": "34/99A", "naklon": 6 }, + "3455": { "label": "34/99B", "naklon": 6 }, + "4009": { "label": "34/1", "naklon": 0 }, + "3602": { "label": "34/2", "naklon": 0 }, + "3441": { "label": "34/3", "naklon": 0 }, + "3464": { "label": "34/4", "naklon": 0 }, + "3883": { "label": "34/5", "naklon": 0 }, + "4098": { "label": "34/6", "naklon": 0 }, + "4003": { "label": "34/7", "naklon": 0 }, + "4004": { "label": "34/8", "naklon": 0 }, + "3888": { "label": "34/9", "naklon": 0 }, + "3997": { "label": "34/10", "naklon": 0 }, + "4006": { "label": "34/11", "naklon": 0 }, + "4000": { "label": "34/12", "naklon": 0 }, + "4011": { "label": "34/13", "naklon": 0 }, + "3999": { "label": "34/14", "naklon": 0 }, + "3981": { "label": "34/15", "naklon": 0 }, + "3988": { "label": "34/16", "naklon": 0 }, + "3986": { "label": "34/17", "naklon": 0 }, + "3998": { "label": "34/18", "naklon": 0 }, + "3983": { "label": "34/19", "naklon": 0 }, + "4005": { "label": "34/20", "naklon": 0 }, + "4001": { "label": "34/21", "naklon": 0 }, + "4008": { "label": "34/22", "naklon": 0 }, + "4002": { "label": "34/23", "naklon": 0 }, + "3996": { "label": "34/24", "naklon": 0 }, + "4007": { "label": "34/25", "naklon": 0 }, + "4113": { "label": "34/26", "naklon": 15 }, + "3595": { "label": "34/27", "naklon": 5 }, + "3599": { "label": "34/28", "naklon": 5 }, + "3601": { "label": "34/29", "naklon": 5 }, + "4205": { "label": "34/30", "naklon": 0 }, + "3987": { "label": "34/31", "naklon": null }, + "4214": { "label": "34/32", "naklon": 0 }, + "4226": { "label": "34/33", "naklon": 0 }, + "4209": { "label": "34/34", "naklon": 0 }, + "4201": { "label": "34/35", "naklon": 0 }, + "3440": { "label": "34/36", "naklon": 0 }, + "3465": { "label": "34/37", "naklon": 0 }, + "3446": { "label": "34/38", "naklon": 0 }, + "4039": { "label": "34/39", "naklon": 5 }, + "4057": { "label": "34/40", "naklon": 5 }, + "3985": { "label": "34/41", "naklon": 0 }, + "3882": { "label": "34/42", "naklon": 0 }, + "4040": { "label": "34/43", "naklon": 5 }, + "3231": { "label": "34/44", "naklon": 0 }, + "3227": { "label": "34/45", "naklon": 0 }, + "3431": { "label": "34/46", "naklon": 0 }, + "4013": { "label": "34/47", "naklon": 0 }, + "4045": { "label": "34/48", "naklon": 0 }, + "3949": { "label": "34/49", "naklon": 0 }, + "3953": { "label": "34/50", "naklon": 0 }, + "3952": { "label": "34/51", "naklon": 0 }, + "3995": { "label": "34/52", "naklon": 0 }, + "3992": { "label": "34/53", "naklon": 0 }, + "4198": { "label": "34/54", "naklon": 0 }, + "3935": { "label": "34/55", "naklon": 0 }, + "4050": { "label": "34/56", "naklon": 0 }, + "4049": { "label": "34/57", "naklon": 0 }, + "4054": { "label": "34/58", "naklon": 0 }, + "3663": { "label": "34/59", "naklon": 6 }, + "3661": { "label": "34/60", "naklon": 6 }, + "3664": { "label": "34/61", "naklon": 6 }, + "3660": { "label": "34/62", "naklon": 6 }, + "3662": { "label": "34/63", "naklon": 6 }, + "3665": { "label": "34/65", "naklon": 6 }, + "3667": { "label": "34/66", "naklon": 6 }, + "3675": { "label": "34/67", "naklon": 6 }, + "3666": { "label": "34/68", "naklon": 6 }, + "3652": { "label": "34/69", "naklon": 6 }, + "3458": { "label": "34/70", "naklon": 6 }, + "4425": { "label": "34/70/1", "naklon": 6 }, + "3459": { "label": "34/71", "naklon": 6 }, + "3435": { "label": "34/72", "naklon": 6 }, + "3448": { "label": "34/73", "naklon": 6 }, + "3451": { "label": "34/74", "naklon": 6 }, + "3442": { "label": "34/75", "naklon": 6 }, + "3449": { "label": "34/76", "naklon": 6 }, + "3450": { "label": "34/77", "naklon": 6 }, + "3433": { "label": "34/78", "naklon": 6 }, + "3432": { "label": "34/80", "naklon": 12 }, + "3470": { "label": "34/81", "naklon": 12 }, + "3438": { "label": "34/82", "naklon": 12 }, + "3439": { "label": "34/83", "naklon": 12 }, + "4028": { "label": "34/84", "naklon": 5 }, + "3596": { "label": "34/85", "naklon": 5 }, + "4021": { "label": "34/86", "naklon": 5 }, + "3437": { "label": "34/87", "naklon": 12 }, + "3428": { "label": "34/88", "naklon": 12 }, + "3430": { "label": "34/89", "naklon": 12 }, + "3454": { "label": "34/91", "naklon": 6 }, + "3492": { "label": "34/92", "naklon": 6 }, + "3462": { "label": "34/93", "naklon": 6 }, + "3427": { "label": "34/94", "naklon": 6 }, + "3434": { "label": "34/95", "naklon": 6 }, + "3456": { "label": "34/96", "naklon": 6 }, + "4307": { "label": "34/98/1", "naklon": 6 }, + "3968": { "label": "35/1", "naklon": 0 }, + "3969": { "label": "35/2", "naklon": 0 }, + "3947": { "label": "35/3", "naklon": 0 }, + "3979": { "label": "35/4", "naklon": 0 }, + "3959": { "label": "35/5", "naklon": 0 }, + "3948": { "label": "35/6", "naklon": 0 }, + "3961": { "label": "35/7", "naklon": 0 }, + "3956": { "label": "35/8", "naklon": 0 }, + "3604": { "label": "35/9", "naklon": 0 }, + "3944": { "label": "35/10", "naklon": 0 }, + "3960": { "label": "35/11", "naklon": 0 }, + "3958": { "label": "35/12", "naklon": 0 }, + "4036": { "label": "35/13", "naklon": 5 }, + "3893": { "label": "35/18", "naklon": 5 }, + "4108": { "label": "35/19", "naklon": 5 }, + "4017": { "label": "35/20", "naklon": 5 }, + "3963": { "label": "35/21", "naklon": 0 }, + "3658": { "label": "35/22", "naklon": 0 }, + "3975": { "label": "35/23", "naklon": 0 }, + "3980": { "label": "35/24", "naklon": 0 }, + "4012": { "label": "35/25", "naklon": 0 }, + "3655": { "label": "35/26", "naklon": 0 }, + "3962": { "label": "35/27", "naklon": 0 }, + "3945": { "label": "35/28", "naklon": 0 }, + "3569": { "label": "35/29", "naklon": 0 }, + "3429": { "label": "35/30", "naklon": 0 }, + "3657": { "label": "35/31", "naklon": 0 }, + "3646": { "label": "35/32", "naklon": 0 }, + "3622": { "label": "35/33", "naklon": 0 }, + "3612": { "label": "35/34", "naklon": 0 }, + "3955": { "label": "35/35", "naklon": 0 }, + "3878": { "label": "35/37", "naklon": 0 }, + "3965": { "label": "35/38", "naklon": 0 }, + "3970": { "label": "35/39", "naklon": 0 }, + "3611": { "label": "35/40", "naklon": 0 }, + "2635": { "label": "35/41", "naklon": 0 }, + "3978": { "label": "35/42", "naklon": 0 }, + "4213": { "label": "35/43", "naklon": 0 }, + "3984": { "label": "35/44", "naklon": 0 }, + "4218": { "label": "35/45", "naklon": 0 }, + "3973": { "label": "35/46", "naklon": 0 }, + "3974": { "label": "35/47", "naklon": 0 }, + "3759": { "label": "35/48", "naklon": 0 }, + "3844": { "label": "35/49", "naklon": 0 }, + "3804": { "label": "35/50", "naklon": 0 }, + "3831": { "label": "35/51", "naklon": 0 }, + "3643": { "label": "35/52", "naklon": 0 }, + "3966": { "label": "35/53", "naklon": 0 }, + "3971": { "label": "35/54", "naklon": 0 }, + "3650": { "label": "35/55", "naklon": 6 }, + "3651": { "label": "35/56", "naklon": 6 }, + "3669": { "label": "35/57", "naklon": 6 }, + "3648": { "label": "35/58", "naklon": 6 }, + "3645": { "label": "35/59", "naklon": 6 }, + "3659": { "label": "35/60", "naklon": 6 }, + "3647": { "label": "35/61", "naklon": 6 }, + "3644": { "label": "35/62", "naklon": 6 }, + "3671": { "label": "35/63", "naklon": 6 }, + "3682": { "label": "35/64", "naklon": 6 }, + "3672": { "label": "35/65", "naklon": 6 }, + "3683": { "label": "35/66", "naklon": 6 }, + "3673": { "label": "35/67", "naklon": 6 }, + "3674": { "label": "35/68", "naklon": 6 }, + "3679": { "label": "35/69", "naklon": 6 }, + "3649": { "label": "35/70", "naklon": 6 }, + "3687": { "label": "35/71", "naklon": 6 }, + "3677": { "label": "35/72", "naklon": 6 }, + "3690": { "label": "35/73", "naklon": 6 }, + "3691": { "label": "35/74", "naklon": 6 }, + "3676": { "label": "35/75", "naklon": 6 }, + "3680": { "label": "35/76", "naklon": 6 }, + "3668": { "label": "35/77", "naklon": 6 }, + "3681": { "label": "35/78", "naklon": 6 }, + "3670": { "label": "35/79", "naklon": 6 }, + "3689": { "label": "35/80", "naklon": 6 }, + "3678": { "label": "35/81", "naklon": 6 }, + "3693": { "label": "35/82", "naklon": 6 }, + "3654": { "label": "35/83", "naklon": null }, + "4086": { "label": "36/1", "naklon": 0 }, + "4085": { "label": "36/2", "naklon": 0 }, + "4083": { "label": "36/3", "naklon": 0 }, + "4087": { "label": "36/4", "naklon": 0 }, + "4077": { "label": "36/5", "naklon": 0 }, + "4082": { "label": "36/6", "naklon": 0 }, + "4186": { "label": "36/7", "naklon": 0 }, + "4206": { "label": "36/8", "naklon": 0 }, + "4080": { "label": "36/9", "naklon": 0 }, + "4081": { "label": "36/10", "naklon": 0 }, + "4210": { "label": "36/11", "naklon": 0 }, + "4079": { "label": "36/12", "naklon": 0 }, + "4076": { "label": "36/13", "naklon": 0 }, + "3852": { "label": "36/14", "naklon": 0 }, + "2813": { "label": "36/15", "naklon": 0 }, + "4219": { "label": "36/16", "naklon": 0 }, + "3104": { "label": "37/1A", "naklon": 15 }, + "3121": { "label": "37/1B", "naklon": 15 }, + "3113": { "label": "37/2", "naklon": 10 }, + "3118": { "label": "37/3", "naklon": 10 }, + "3111": { "label": "37/4", "naklon": 10 }, + "4020": { "label": "37/5", "naklon": 10 }, + "3119": { "label": "37/6", "naklon": 10 }, + "3110": { "label": "37/7", "naklon": 10 }, + "3108": { "label": "37/8", "naklon": 10 }, + "3107": { "label": "37/12", "naklon": 0 }, + "3199": { "label": "37/13", "naklon": 0 }, + "3120": { "label": "37/14", "naklon": 0 }, + "3122": { "label": "37/15", "naklon": 0 }, + "3908": { "label": "37/16", "naklon": 0 }, + "3114": { "label": "37/17", "naklon": 0 }, + "3105": { "label": "37/18", "naklon": 0 }, + "3116": { "label": "37/19", "naklon": 0 }, + "3106": { "label": "37/20", "naklon": 0 }, + "2887": { "label": "37/21", "naklon": 5 }, + "2888": { "label": "37/22", "naklon": 5 }, + "2877": { "label": "37/23", "naklon": 5 }, + "2891": { "label": "37/24", "naklon": 5 }, + "2886": { "label": "37/25", "naklon": 5 }, + "2892": { "label": "37/26", "naklon": 5 }, + "2882": { "label": "37/27", "naklon": 5 }, + "2883": { "label": "37/28", "naklon": 5 }, + "2876": { "label": "37/29", "naklon": 5 }, + "3109": { "label": "37/30", "naklon": 5 }, + "4152": { "label": "37/31", "naklon": 5 }, + "3117": { "label": "37/32", "naklon": 5 }, + "2870": { "label": "37/33", "naklon": 5 }, + "2867": { "label": "37/34", "naklon": 0 }, + "2865": { "label": "37/35", "naklon": 0 }, + "3096": { "label": "37/36", "naklon": 0 }, + "2871": { "label": "37/37", "naklon": 0 }, + "2884": { "label": "37/38", "naklon": 0 }, + "2855": { "label": "37/39", "naklon": 0 }, + "2878": { "label": "37/40", "naklon": 0 }, + "2889": { "label": "37/41", "naklon": 0 }, + "2879": { "label": "37/42", "naklon": 0 }, + "2850": { "label": "37/43", "naklon": 0 }, + "2894": { "label": "37/44", "naklon": 0 }, + "2880": { "label": "37/45", "naklon": 0 }, + "2893": { "label": "37/46", "naklon": 0 }, + "2890": { "label": "37/47", "naklon": 0 }, + "4355": { "label": "37/48", "naklon": 0 }, + "2885": { "label": "37/49", "naklon": 0 }, + "2875": { "label": "37/50", "naklon": 0 }, + "3486": { "label": "38/30A", "naklon": 5 }, + "3714": { "label": "38/30B", "naklon": 0 }, + "3483": { "label": "38/32A", "naklon": 5 }, + "3496": { "label": "38/32B", "naklon": 0 }, + "3487": { "label": "38/35A", "naklon": 5 }, + "3498": { "label": "38/35B", "naklon": 5 }, + "3578": { "label": "38/38A", "naklon": 5 }, + "3508": { "label": "38/38B", "naklon": 5 }, + "3787": { "label": "38/59A", "naklon": 5 }, + "3491": { "label": "38/59B", "naklon": 5 }, + "3568": { "label": "38/1", "naklon": null }, + "3502": { "label": "38/2", "naklon": 0 }, + "3836": { "label": "38/3", "naklon": 0 }, + "3588": { "label": "38/4", "naklon": 0 }, + "3493": { "label": "38/5", "naklon": 0 }, + "3580": { "label": "38/6", "naklon": 0 }, + "3572": { "label": "38/7", "naklon": 0 }, + "3506": { "label": "38/8", "naklon": 0 }, + "3503": { "label": "38/9", "naklon": 0 }, + "3579": { "label": "38/10", "naklon": 0 }, + "3586": { "label": "38/11", "naklon": 0 }, + "3495": { "label": "38/12", "naklon": 5 }, + "3492": { "label": "38/13", "naklon": 5 }, + "3532": { "label": "38/14", "naklon": 5 }, + "3574": { "label": "38/15", "naklon": 5 }, + "4140": { "label": "38/16", "naklon": 5 }, + "3795": { "label": "38/17", "naklon": 5 }, + "3788": { "label": "38/18", "naklon": 5 }, + "3796": { "label": "38/19", "naklon": 5 }, + "3797": { "label": "38/20", "naklon": 5 }, + "3798": { "label": "38/21", "naklon": 5 }, + "3786": { "label": "38/22", "naklon": 5 }, + "4067": { "label": "38/23", "naklon": 5 }, + "3773": { "label": "38/24", "naklon": 5 }, + "3774": { "label": "38/25", "naklon": 5 }, + "3789": { "label": "38/26", "naklon": 5 }, + "3520": { "label": "38/27", "naklon": null }, + "3577": { "label": "38/29", "naklon": 5 }, + "3530": { "label": "38/31", "naklon": null }, + "3488": { "label": "38/33", "naklon": 5 }, + "3505": { "label": "38/34", "naklon": 5 }, + "3497": { "label": "38/36", "naklon": 5 }, + "3490": { "label": "38/37", "naklon": 5 }, + "3585": { "label": "38/39", "naklon": 5 }, + "3582": { "label": "38/40", "naklon": 5 }, + "3507": { "label": "38/41", "naklon": 5 }, + "3484": { "label": "38/42", "naklon": 5 }, + "3501": { "label": "38/43", "naklon": 5 }, + "3581": { "label": "38/44", "naklon": 5 }, + "4192": { "label": "38/45", "naklon": 5 }, + "3576": { "label": "38/46", "naklon": 5 }, + "3489": { "label": "38/47", "naklon": 5 }, + "3573": { "label": "38/48", "naklon": 5 }, + "3504": { "label": "38/49", "naklon": 5 }, + "3794": { "label": "38/50", "naklon": 5 }, + "3485": { "label": "38/51", "naklon": 5 }, + "3482": { "label": "38/52", "naklon": 5 }, + "3575": { "label": "38/53", "naklon": 5 }, + "3499": { "label": "38/54", "naklon": 5 }, + "3494": { "label": "38/55", "naklon": 5 }, + "3583": { "label": "38/56", "naklon": 5 }, + "3570": { "label": "38/57", "naklon": 5 }, + "3500": { "label": "38/58", "naklon": 0 }, + "3510": { "label": "38/63", "naklon": null }, + "3512": { "label": "38/64", "naklon": null }, + "3535": { "label": "38/67", "naklon": null }, + "3531": { "label": "38/68", "naklon": null }, + "3537": { "label": "38/69", "naklon": null }, + "3522": { "label": "39/1", "naklon": 10 }, + "4018": { "label": "39/2", "naklon": 10 }, + "4019": { "label": "39/3", "naklon": 10 }, + "4154": { "label": "39/4", "naklon": 5 }, + "3907": { "label": "39/5", "naklon": 5 }, + "4148": { "label": "39/6", "naklon": 0 }, + "4153": { "label": "39/7", "naklon": 0 }, + "3938": { "label": "39/8", "naklon": 0 }, + "3802": { "label": "39/9", "naklon": 0 }, + "4015": { "label": "39/10", "naklon": 0 }, + "3929": { "label": "39/11", "naklon": 0 }, + "3946": { "label": "39/12", "naklon": 0 }, + "4014": { "label": "39/13", "naklon": 0 }, + "4155": { "label": "39/14", "naklon": 0 }, + "4149": { "label": "39/15", "naklon": 0 }, + "3642": { "label": "39/16", "naklon": 0 }, + "3636": { "label": "39/17", "naklon": 5 }, + "3991": { "label": "39/18", "naklon": 0 }, + "3994": { "label": "39/19", "naklon": 0 }, + "3990": { "label": "39/20", "naklon": 0 }, + "3967": { "label": "39/21", "naklon": 0 }, + "3977": { "label": "39/22", "naklon": 0 }, + "3757": { "label": "39/23", "naklon": 15 }, + "3633": { "label": "39/24", "naklon": 15 }, + "3744": { "label": "39/25", "naklon": 15 }, + "4023": { "label": "39/26", "naklon": 5 }, + "3720": { "label": "39/27", "naklon": 5 }, + "3734": { "label": "39/28", "naklon": 5 }, + "3741": { "label": "39/29", "naklon": 5 }, + "3721": { "label": "39/30", "naklon": 5 }, + "3845": { "label": "39/31", "naklon": 0 }, + "3840": { "label": "39/32", "naklon": 0 }, + "3837": { "label": "39/33", "naklon": null }, + "3839": { "label": "39/34", "naklon": 0 }, + "3542": { "label": "41/1", "naklon": null }, + "3566": { "label": "41/2", "naklon": null }, + "3548": { "label": "41/3", "naklon": null }, + "3515": { "label": "41/4", "naklon": null }, + "3559": { "label": "41/5", "naklon": null }, + "3509": { "label": "41/6", "naklon": null }, + "3524": { "label": "41/7", "naklon": null }, + "3518": { "label": "41/8", "naklon": null }, + "3182": { "label": "41/9", "naklon": null }, + "3187": { "label": "41/10", "naklon": null }, + "3195": { "label": "41/11", "naklon": 0 }, + "3857": { "label": "41/12", "naklon": 0 }, + "3183": { "label": "41/13", "naklon": 0 }, + "3177": { "label": "41/14", "naklon": 0 }, + "3189": { "label": "41/15", "naklon": 0 }, + "3186": { "label": "41/16", "naklon": 0 }, + "3178": { "label": "42/1", "naklon": 0 }, + "3219": { "label": "42/2", "naklon": 0 }, + "3475": { "label": "42/3", "naklon": 0 }, + "3175": { "label": "42/4", "naklon": 0 }, + "3222": { "label": "42/5", "naklon": 0 }, + "3221": { "label": "42/6", "naklon": 0 }, + "3181": { "label": "42/7", "naklon": 0 }, + "3180": { "label": "42/8", "naklon": 0 }, + "3218": { "label": "42/9", "naklon": 0 }, + "3176": { "label": "42/10", "naklon": 0 }, + "3174": { "label": "42/11", "naklon": 0 }, + "3220": { "label": "42/12", "naklon": 0 }, + "3533": { "label": "42/13", "naklon": null }, + "3543": { "label": "42/14", "naklon": null }, + "3554": { "label": "42/15", "naklon": null }, + "3549": { "label": "42/16", "naklon": null }, + "2857": { "label": "43/1", "naklon": 5 }, + "2752": { "label": "43/2", "naklon": 5 }, + "2753": { "label": "43/3", "naklon": 5 }, + "3024": { "label": "43/4", "naklon": 5 }, + "2754": { "label": "43/5", "naklon": 5 }, + "4423": { "label": "43/6", "naklon": 5 }, + "4368": { "label": "43/7", "naklon": 5 }, + "2978": { "label": "43/8", "naklon": 5 }, + "2868": { "label": "43/9", "naklon": 5 }, + "4377": { "label": "43/10", "naklon": 5 }, + "2757": { "label": "43/11", "naklon": 5 }, + "2970": { "label": "43/12", "naklon": 5 }, + "2758": { "label": "43/13", "naklon": 5 }, + "4378": { "label": "43/14", "naklon": 5 }, + "2759": { "label": "43/15", "naklon": 5 }, + "4342": { "label": "43/16", "naklon": 5 }, + "3026": { "label": "43/17", "naklon": null }, + "2986": { "label": "43/18", "naklon": null }, + "4343": { "label": "43/19", "naklon": null }, + "2760": { "label": "43/20", "naklon": null }, + "4379": { "label": "43/21", "naklon": null }, + "2971": { "label": "43/22", "naklon": null }, + "2761": { "label": "43/23", "naklon": null }, + "2762": { "label": "43/24", "naklon": null }, + "4339": { "label": "43/25", "naklon": null }, + "4380": { "label": "43/26", "naklon": null }, + "2975": { "label": "43/27", "naklon": null }, + "3003": { "label": "43/28", "naklon": null }, + "4338": { "label": "43/29", "naklon": null }, + "3002": { "label": "43/30", "naklon": null }, + "4345": { "label": "43/31", "naklon": null }, + "2979": { "label": "43/32", "naklon": null }, + "2989": { "label": "43/33", "naklon": null }, + "2996": { "label": "43/34", "naklon": null }, + "2972": { "label": "43/35", "naklon": null }, + "2988": { "label": "43/36", "naklon": null }, + "4344": { "label": "43/37", "naklon": null }, + "3022": { "label": "43/38", "naklon": null }, + "4341": { "label": "43/39", "naklon": null }, + "2983": { "label": "43/40", "naklon": null }, + "2995": { "label": "43/41", "naklon": null }, + "2763": { "label": "43/42", "naklon": null }, + "2764": { "label": "43/43", "naklon": null }, + "2765": { "label": "43/44", "naklon": null }, + "2766": { "label": "43/45", "naklon": null }, + "2767": { "label": "43/46", "naklon": null }, + "2768": { "label": "43/47", "naklon": null }, + "3018": { "label": "43/48", "naklon": null }, + "2982": { "label": "43/49", "naklon": null }, + "3019": { "label": "43/50", "naklon": 0 }, + "3013": { "label": "43/51", "naklon": null }, + "3007": { "label": "43/52", "naklon": null }, + "2980": { "label": "43/53", "naklon": null }, + "3011": { "label": "43/54", "naklon": null }, + "2672": { "label": "43/55", "naklon": null }, + "2673": { "label": "43/56", "naklon": null }, + "2674": { "label": "43/57", "naklon": null }, + "2675": { "label": "43/58", "naklon": null }, + "2676": { "label": "43/59", "naklon": null }, + "2677": { "label": "43/60", "naklon": null }, + "2678": { "label": "43/61", "naklon": null }, + "2679": { "label": "43/62", "naklon": null }, + "2680": { "label": "43/63", "naklon": null }, + "2840": { "label": "43/64", "naklon": null }, + "2682": { "label": "43/65", "naklon": null }, + "2683": { "label": "43/66", "naklon": null }, + "2684": { "label": "43/67", "naklon": 0 }, + "2685": { "label": "43/68", "naklon": null }, + "2686": { "label": "43/69", "naklon": null }, + "2687": { "label": "43/70", "naklon": null }, + "2688": { "label": "43/71", "naklon": null }, + "2689": { "label": "43/72", "naklon": null }, + "2690": { "label": "43/73", "naklon": null }, + "2691": { "label": "43/74", "naklon": null }, + "2692": { "label": "43/75", "naklon": null }, + "2693": { "label": "43/76", "naklon": 0 }, + "2694": { "label": "43/77", "naklon": null }, + "2695": { "label": "43/78", "naklon": null }, + "2696": { "label": "43/79", "naklon": 0 }, + "2697": { "label": "43/80", "naklon": null }, + "2698": { "label": "43/81", "naklon": 0 }, + "2699": { "label": "43/82", "naklon": null }, + "2700": { "label": "43/83", "naklon": 0 }, + "2744": { "label": "43/84", "naklon": null }, + "2745": { "label": "43/85", "naklon": 0 }, + "2746": { "label": "43/86", "naklon": null }, + "2747": { "label": "43/87", "naklon": 0 }, + "2748": { "label": "43/88", "naklon": null }, + "2749": { "label": "43/89", "naklon": 0 }, + "2841": { "label": "43/90", "naklon": null }, + "4237": { "label": "45/1", "naklon": 5 }, + "4226": { "label": "45/2", "naklon": 5 }, + "4242": { "label": "45/3", "naklon": 5 }, + "4179": { "label": "45/4", "naklon": 5 }, + "4247": { "label": "45/5", "naklon": 5 }, + "4245": { "label": "45/6", "naklon": 5 }, + "3872": { "label": "45/7", "naklon": 5 }, + "4175": { "label": "45/8", "naklon": 5 }, + "4236": { "label": "45/9", "naklon": 5 }, + "4170": { "label": "45/10", "naklon": 5 }, + "4238": { "label": "45/11", "naklon": 5 }, + "4246": { "label": "45/12", "naklon": 5 }, + "4166": { "label": "45/13", "naklon": 5 }, + "4227": { "label": "45/14", "naklon": 5 }, + "4171": { "label": "45/15", "naklon": 5 }, + "4180": { "label": "45/16", "naklon": 5 }, + "4244": { "label": "45/17", "naklon": 5 }, + "4199": { "label": "45/18", "naklon": 5 }, + "4239": { "label": "45/19", "naklon": 5 }, + "4207": { "label": "45/20", "naklon": 5 }, + "4223": { "label": "45/21", "naklon": 5 }, + "4230": { "label": "45/22", "naklon": 5 }, + "4233": { "label": "45/23", "naklon": 5 }, + "4222": { "label": "45/24", "naklon": 5 }, + "4217": { "label": "45/25", "naklon": 5 }, + "4243": { "label": "45/26", "naklon": 5 }, + "4200": { "label": "45/27", "naklon": 5 }, + "4228": { "label": "45/28", "naklon": 5 }, + "4211": { "label": "45/29", "naklon": 5 }, + "4234": { "label": "45/30", "naklon": 5 }, + "4235": { "label": "45/31", "naklon": 5 }, + "4203": { "label": "45/32", "naklon": 5 }, + "4240": { "label": "45/33", "naklon": 5 }, + "4224": { "label": "45/34", "naklon": 5 }, + "4232": { "label": "45/35", "naklon": 5 }, + "4231": { "label": "45/36", "naklon": 5 }, + "4161": { "label": "45/37", "naklon": 5 }, + "4225": { "label": "45/38", "naklon": 5 }, + "4241": { "label": "45/39", "naklon": 5 }, + "4229": { "label": "45/40", "naklon": 5 }, + "4221": { "label": "45/41", "naklon": 5 }, + "4212": { "label": "45/42", "naklon": 5 }, + "4216": { "label": "45/43", "naklon": 5 }, + "3864": { "label": "45/44", "naklon": null }, + "3806": { "label": "46/1", "naklon": 5 }, + "3813": { "label": "46/2", "naklon": 5 }, + "3815": { "label": "46/3", "naklon": 5 }, + "3807": { "label": "46/4", "naklon": 5 }, + "3895": { "label": "46/5", "naklon": 5 }, + "3809": { "label": "46/6", "naklon": 5 }, + "3820": { "label": "46/7", "naklon": 5 }, + "3805": { "label": "46/8", "naklon": 5 }, + "3894": { "label": "46/9", "naklon": 0 }, + "4055": { "label": "46/10", "naklon": 0 }, + "3812": { "label": "46/11", "naklon": 5 }, + "3822": { "label": "46/12", "naklon": 5 }, + "3821": { "label": "46/13", "naklon": 5 }, + "3808": { "label": "46/14", "naklon": 5 }, + "3799": { "label": "46/15", "naklon": 5 }, + "3816": { "label": "46/16", "naklon": 5 }, + "3751": { "label": "46/17", "naklon": 5 }, + "3814": { "label": "46/18", "naklon": 5 }, + "4044": { "label": "46/19", "naklon": 0 }, + "3931": { "label": "46/20", "naklon": 0 }, + "3902": { "label": "46/21", "naklon": 5 }, + "3916": { "label": "46/22", "naklon": 5 }, + "3892": { "label": "46/23", "naklon": 5 }, + "3914": { "label": "46/24", "naklon": 5 }, + "3898": { "label": "46/25", "naklon": 5 }, + "3897": { "label": "46/26", "naklon": 5 }, + "3885": { "label": "46/27", "naklon": 5 }, + "3906": { "label": "46/28", "naklon": 5 }, + "3930": { "label": "46/29", "naklon": 0 }, + "3936": { "label": "46/30", "naklon": 0 }, + "3860": { "label": "46/31", "naklon": 5 }, + "3841": { "label": "46/32", "naklon": 5 }, + "3873": { "label": "46/33", "naklon": 5 }, + "3854": { "label": "46/34", "naklon": 5 }, + "3939": { "label": "46/35", "naklon": 5 }, + "3858": { "label": "46/36", "naklon": 5 }, + "3940": { "label": "46/37", "naklon": 5 }, + "4035": { "label": "46/38", "naklon": 5 }, + "3373": { "label": "47/1", "naklon": 0 }, + "3374": { "label": "47/2", "naklon": 0 }, + "3384": { "label": "47/3", "naklon": 0 }, + "3385": { "label": "47/4", "naklon": 0 }, + "4051": { "label": "48/1", "naklon": 5 }, + "4041": { "label": "48/2", "naklon": 5 }, + "4056": { "label": "48/3", "naklon": 5 }, + "3875": { "label": "48/4", "naklon": 5 }, + "4061": { "label": "48/5", "naklon": 5 }, + "4047": { "label": "48/6", "naklon": 5 }, + "4060": { "label": "48/7", "naklon": 5 }, + "4059": { "label": "48/8", "naklon": 5 }, + "4058": { "label": "48/9", "naklon": 5 }, + "3635": { "label": "48/10", "naklon": 5 }, + "3640": { "label": "48/11", "naklon": 5 }, + "3747": { "label": "48/12", "naklon": 5 }, + "3629": { "label": "48/13", "naklon": 5 }, + "4048": { "label": "48/14", "naklon": 5 }, + "4063": { "label": "48/15", "naklon": 5 }, + "4062": { "label": "48/16", "naklon": 5 }, + "4043": { "label": "48/17", "naklon": 5 }, + "4052": { "label": "48/18", "naklon": 5 }, + "4042": { "label": "48/19", "naklon": 5 }, + "4053": { "label": "48/20", "naklon": 5 }, + "4167": { "label": "50/15A", "naklon": null }, + "4159": { "label": "50/15B", "naklon": null }, + "4183": { "label": "50/1", "naklon": 0 }, + "4157": { "label": "50/2", "naklon": 0 }, + "4160": { "label": "50/3", "naklon": 0 }, + "4173": { "label": "50/4", "naklon": 0 }, + "4177": { "label": "50/5", "naklon": 0 }, + "4174": { "label": "50/6", "naklon": 0 }, + "4178": { "label": "50/7", "naklon": 0 }, + "4195": { "label": "50/8", "naklon": 0 }, + "4169": { "label": "50/9", "naklon": 0 }, + "4162": { "label": "50/10", "naklon": 0 }, + "4158": { "label": "50/11", "naklon": 0 }, + "4182": { "label": "50/12", "naklon": 0 }, + "4194": { "label": "50/13", "naklon": 0 }, + "4184": { "label": "50/14", "naklon": 0 }, + "4197": { "label": "50/16", "naklon": null }, + "4164": { "label": "50/17", "naklon": null }, + "3306": { "label": "50/18", "naklon": null }, + "4168": { "label": "50/19", "naklon": null }, + "4193": { "label": "50/20", "naklon": null }, + "4187": { "label": "50/21", "naklon": null }, + "4163": { "label": "50/22", "naklon": null }, + "4165": { "label": "50/23", "naklon": null }, + "4185": { "label": "50/24", "naklon": null }, + "3904": { "label": "50/25", "naklon": null }, + "2619": { "label": "53/1", "naklon": 0 }, + "2623": { "label": "53/2", "naklon": 0 }, + "2617": { "label": "53/3", "naklon": 0 }, + "2622": { "label": "53/4", "naklon": 0 }, + "2618": { "label": "53/5", "naklon": 0 }, + "2626": { "label": "53/6", "naklon": 0 }, + "2869": { "label": "53/7", "naklon": 0 }, + "2613": { "label": "53/8", "naklon": 0 }, + "2608": { "label": "53/9", "naklon": 0 }, + "2611": { "label": "53/10", "naklon": 0 }, + "2598": { "label": "53/11", "naklon": 0 }, + "3528": { "label": "53/12", "naklon": 0 }, + "3545": { "label": "53/13", "naklon": 0 }, + "2620": { "label": "53/14", "naklon": 0 }, + "2612": { "label": "53/15", "naklon": 0 }, + "2615": { "label": "53/16", "naklon": 0 }, + "3722": { "label": "53/17", "naklon": 0 }, + "2624": { "label": "53/18", "naklon": 0 }, + "2609": { "label": "53/19", "naklon": 0 }, + "2625": { "label": "53/20", "naklon": 0 }, + "2602": { "label": "53/21", "naklon": 0 }, + "2610": { "label": "53/22", "naklon": 0 }, + "2599": { "label": "53/23", "naklon": 0 }, + "2605": { "label": "53/24", "naklon": 0 }, + "2593": { "label": "53/25", "naklon": 0 }, + "3074": { "label": "53/26", "naklon": 0 }, + "2740": { "label": "53/27", "naklon": 0 }, + "2614": { "label": "53/28", "naklon": 0 }, + "2601": { "label": "53/29", "naklon": 0 }, + "4196": { "label": "53/30", "naklon": 0 }, + "2607": { "label": "53/31", "naklon": 0 }, + "2582": { "label": "53/32", "naklon": 0 }, + "2594": { "label": "53/33", "naklon": 0 }, + "2589": { "label": "53/34", "naklon": 0 }, + "2596": { "label": "53/35", "naklon": 0 }, + "2603": { "label": "53/36", "naklon": 0 }, + "2597": { "label": "53/37", "naklon": 0 }, + "2590": { "label": "53/38", "naklon": 0 }, + "2581": { "label": "53/39", "naklon": 0 }, + "2583": { "label": "53/40", "naklon": 0 }, + "2580": { "label": "53/41", "naklon": 0 }, + "2579": { "label": "53/42", "naklon": 0 }, + "2797": { "label": "53/43", "naklon": 0 }, + "2577": { "label": "53/44", "naklon": 0 }, + "2586": { "label": "53/45", "naklon": 0 }, + "2588": { "label": "53/46", "naklon": 0 }, + "2591": { "label": "53/47", "naklon": 0 }, + "2592": { "label": "53/48", "naklon": 0 }, + "2573": { "label": "53/49", "naklon": 0 }, + "2595": { "label": "53/50", "naklon": 0 }, + "2587": { "label": "53/51", "naklon": 0 }, + "2578": { "label": "53/52", "naklon": 0 }, + "2574": { "label": "53/53", "naklon": 0 }, + "2571": { "label": "53/54", "naklon": 0 }, + "2570": { "label": "53/55", "naklon": 0 }, + "2576": { "label": "53/56", "naklon": 0 }, + "2881": { "label": "53/57", "naklon": 0 }, + "2575": { "label": "53/58", "naklon": 0 }, + "2572": { "label": "53/59", "naklon": 0 }, + "2569": { "label": "53/60", "naklon": 0 }, + "4324": { "label": "55/1", "naklon": 0 }, + "4319": { "label": "55/2", "naklon": 0 }, + "4327": { "label": "55/3", "naklon": 0 }, + "4333": { "label": "55/4", "naklon": 0 }, + "4332": { "label": "55/5", "naklon": 0 }, + "4320": { "label": "55/6", "naklon": 0 }, + "4317": { "label": "55/7", "naklon": 0 }, + "4318": { "label": "55/8", "naklon": 0 }, + "4328": { "label": "55/9", "naklon": 0 }, + "4323": { "label": "55/10", "naklon": 0 }, + "4325": { "label": "55/11", "naklon": 0 }, + "4326": { "label": "55/12", "naklon": 0 }, + "4316": { "label": "55/13", "naklon": 0 }, + "4322": { "label": "55/14", "naklon": 0 }, + "4331": { "label": "55/15", "naklon": 0 }, + "4321": { "label": "55/16", "naklon": 0 }, + "4315": { "label": "55/17", "naklon": 0 }, + "4329": { "label": "55/18", "naklon": 0 }, + "4334": { "label": "55/19", "naklon": 0 }, + "4330": { "label": "55/20", "naklon": 0 } +}; + + +let svetlaSorted = [{ "label": "1/3A", "node": 3993, "naklon": 0 }, +{ "label": "1/3B", "node": 3989, "naklon": 15 }, +{ "label": "1/1", "node": 3976, "naklon": 0 }, +{ "label": "1/2", "node": 4181, "naklon": 0 }, +{ "label": "1/4", "node": 4376, "naklon": 15 }, +{ "label": "1/5", "node": 3868, "naklon": 15 }, +{ "label": "1/6", "node": 3728, "naklon": 15 }, +{ "label": "1/7", "node": 3730, "naklon": 0 }, +{ "label": "1/8", "node": 4374, "naklon": 0 }, +{ "label": "1/9", "node": 3718, "naklon": 15 }, +{ "label": "1/10", "node": 4375, "naklon": 15 }, +{ "label": "1/11", "node": 4172, "naklon": 15 }, +{ "label": "1/12", "node": 4176, "naklon": 15 }, +{ "label": "2/1", "node": 3800, "naklon": 5 }, +{ "label": "2/2", "node": 3823, "naklon": 5 }, +{ "label": "2/3", "node": 3905, "naklon": 5 }, +{ "label": "2/4", "node": 3803, "naklon": 5 }, +{ "label": "2/5", "node": 3817, "naklon": 5 }, +{ "label": "2/6", "node": 3818, "naklon": 5 }, +{ "label": "2/7", "node": 3811, "naklon": 5 }, +{ "label": "2/8", "node": 3915, "naklon": 5 }, +{ "label": "2/9", "node": 3843, "naklon": 10 }, +{ "label": "2/10", "node": 3827, "naklon": 10 }, +{ "label": "2/11", "node": 4220, "naklon": 10 }, +{ "label": "2/12", "node": 3826, "naklon": 10 }, +{ "label": "2/13", "node": 3834, "naklon": 10 }, +{ "label": "2/14", "node": 3838, "naklon": 10 }, +{ "label": "2/15", "node": 2823, "naklon": 10 }, +{ "label": "2/16", "node": 3750, "naklon": 10 }, +{ "label": "2/17", "node": 3378, "naklon": 0 }, +{ "label": "2/18", "node": 3376, "naklon": 0 }, +{ "label": "2/19", "node": 3379, "naklon": 0 }, +{ "label": "2/20", "node": 3285, "naklon": 0 }, +{ "label": "2/21", "node": 3382, "naklon": 0 }, +{ "label": "2/22", "node": 3383, "naklon": 0 }, +{ "label": "2/24", "node": 3375, "naklon": 0 }, +{ "label": "2/25", "node": 3381, "naklon": 0 }, +{ "label": "2/26", "node": 3380, "naklon": 0 }, +{ "label": "2/27", "node": 3828, "naklon": 10 }, +{ "label": "2/28", "node": 3377, "naklon": 0 }, +{ "label": "7/1", "node": 3591, "naklon": 0 }, +{ "label": "7/2", "node": 3587, "naklon": 0 }, +{ "label": "7/3", "node": 3468, "naklon": 0 }, +{ "label": "7/4", "node": 3466, "naklon": 0 }, +{ "label": "7/5", "node": 3474, "naklon": 0 }, +{ "label": "7/6", "node": 3481, "naklon": 0 }, +{ "label": "7/7", "node": 3589, "naklon": 0 }, +{ "label": "7/8", "node": 3472, "naklon": 0 }, +{ "label": "7/9", "node": 3473, "naklon": 0 }, +{ "label": "7/10", "node": 3584, "naklon": 0 }, +{ "label": "7/11", "node": 3571, "naklon": 0 }, +{ "label": "7/12", "node": 3476, "naklon": 0 }, +{ "label": "7/13", "node": 3590, "naklon": 0 }, +{ "label": "7/14", "node": 3478, "naklon": 0 }, +{ "label": "7/15", "node": 3471, "naklon": 0 }, +{ "label": "7/16", "node": 3467, "naklon": 0 }, +{ "label": "7/17", "node": 3479, "naklon": 0 }, +{ "label": "7/18", "node": 3480, "naklon": 0 }, +{ "label": "7/19", "node": 3477, "naklon": 0 }, +{ "label": "12/1", "node": 3362, "naklon": 0 }, +{ "label": "12/2", "node": 3355, "naklon": 0 }, +{ "label": "12/3", "node": 3356, "naklon": 0 }, +{ "label": "12/4", "node": 3349, "naklon": 0 }, +{ "label": "12/5", "node": 3357, "naklon": 0 }, +{ "label": "12/6", "node": 3350, "naklon": 0 }, +{ "label": "12/7", "node": 3353, "naklon": 0 }, +{ "label": "12/8", "node": 3539, "naklon": 0 }, +{ "label": "12/9", "node": 3050, "naklon": 0 }, +{ "label": "12/10", "node": 3361, "naklon": 0 }, +{ "label": "12/11", "node": 3360, "naklon": 0 }, +{ "label": "12/12", "node": 3358, "naklon": 0 }, +{ "label": "12/13", "node": 3541, "naklon": 0 }, +{ "label": "12/14", "node": 3551, "naklon": 0 }, +{ "label": "12/15", "node": 3514, "naklon": 0 }, +{ "label": "12/16", "node": 3547, "naklon": 0 }, +{ "label": "12/17", "node": 3562, "naklon": 0 }, +{ "label": "12/18", "node": 3525, "naklon": 0 }, +{ "label": "12/19", "node": 3558, "naklon": 0 }, +{ "label": "12/20", "node": 3511, "naklon": 0 }, +{ "label": "12/21", "node": 3058, "naklon": 0 }, +{ "label": "12/22", "node": 3062, "naklon": 0 }, +{ "label": "12/23", "node": 3070, "naklon": 0 }, +{ "label": "12/24", "node": 3447, "naklon": 0 }, +{ "label": "12/25", "node": 3056, "naklon": 0 }, +{ "label": "12/26", "node": 3057, "naklon": 0 }, +{ "label": "12/27", "node": 3071, "naklon": 0 }, +{ "label": "12/28", "node": 3061, "naklon": 0 }, +{ "label": "12/29", "node": 3069, "naklon": 0 }, +{ "label": "12/30", "node": 3077, "naklon": 0 }, +{ "label": "12/31", "node": 3080, "naklon": 0 }, +{ "label": "12/32", "node": 3081, "naklon": 0 }, +{ "label": "12/33", "node": 3078, "naklon": 0 }, +{ "label": "12/34", "node": 4037, "naklon": 0 }, +{ "label": "12/35", "node": 3067, "naklon": 0 }, +{ "label": "12/36", "node": 3051, "naklon": 0 }, +{ "label": "12/37", "node": 2924, "naklon": 0 }, +{ "label": "12/38", "node": 3060, "naklon": 0 }, +{ "label": "12/39", "node": 3073, "naklon": 0 }, +{ "label": "12/40", "node": 3469, "naklon": 0 }, +{ "label": "12/41", "node": 2923, "naklon": 0 }, +{ "label": "12/42", "node": 2925, "naklon": 0 }, +{ "label": "12/43", "node": 3359, "naklon": 0 }, +{ "label": "12/44", "node": 3055, "naklon": 0 }, +{ "label": "12/45", "node": 2922, "naklon": 0 }, +{ "label": "12/46", "node": 3084, "naklon": 0 }, +{ "label": "12/47", "node": 3064, "naklon": 0 }, +{ "label": "12/48", "node": 3066, "naklon": 0 }, +{ "label": "12/49", "node": 3072, "naklon": 0 }, +{ "label": "13/13A", "node": 3415, "naklon": 0 }, +{ "label": "13/13B", "node": 3409, "naklon": 0 }, +{ "label": "13/13C", "node": 3399, "naklon": 0 }, +{ "label": "13/29A", "node": 3410, "naklon": 0 }, +{ "label": "13/29B", "node": 3407, "naklon": 0 }, +{ "label": "13/34A", "node": 3405, "naklon": 0 }, +{ "label": "13/34B", "node": 3404, "naklon": 0 }, +{ "label": "13/45A", "node": 3392, "naklon": 0 }, +{ "label": "13/45B", "node": 3386, "naklon": 0 }, +{ "label": "13/60A", "node": 3303, "naklon": 3 }, +{ "label": "13/60B", "node": 3302, "naklon": 3 }, +{ "label": "13/70A", "node": 3319, "naklon": 6 }, +{ "label": "13/70B", "node": 3317, "naklon": 6 }, +{ "label": "13/70C", "node": 3326, "naklon": 6 }, +{ "label": "13/73A", "node": 3351, "naklon": 6 }, +{ "label": "13/73B", "node": 3313, "naklon": 6 }, +{ "label": "13/83A", "node": 3316, "naklon": 0 }, +{ "label": "13/83B", "node": 3320, "naklon": 0 }, +{ "label": "13/95A", "node": 3336, "naklon": 0 }, +{ "label": "13/95B", "node": 3323, "naklon": 0 }, +{ "label": "13/98A", "node": 3339, "naklon": 0 }, +{ "label": "13/98B", "node": 3333, "naklon": 0 }, +{ "label": "13/1", "node": 3425, "naklon": 0 }, +{ "label": "13/2", "node": 3419, "naklon": 0 }, +{ "label": "13/3", "node": 3416, "naklon": 0 }, +{ "label": "13/4", "node": 3332, "naklon": 0 }, +{ "label": "13/5", "node": 3422, "naklon": 0 }, +{ "label": "13/6", "node": 3417, "naklon": 0 }, +{ "label": "13/7", "node": 3418, "naklon": 0 }, +{ "label": "13/8", "node": 3424, "naklon": 0 }, +{ "label": "13/9", "node": 3334, "naklon": 0 }, +{ "label": "13/10", "node": 4363, "naklon": 0 }, +{ "label": "13/11", "node": 3423, "naklon": 0 }, +{ "label": "13/12", "node": 3406, "naklon": 0 }, +{ "label": "13/14", "node": 3411, "naklon": 0 }, +{ "label": "13/15", "node": 3412, "naklon": 0 }, +{ "label": "13/16", "node": 3413, "naklon": 0 }, +{ "label": "13/17", "node": 3414, "naklon": 0 }, +{ "label": "13/18", "node": 3391, "naklon": 0 }, +{ "label": "13/19", "node": 3370, "naklon": 0 }, +{ "label": "13/20", "node": 3369, "naklon": 0 }, +{ "label": "13/21", "node": 3408, "naklon": 0 }, +{ "label": "13/22", "node": 3393, "naklon": 0 }, +{ "label": "13/23", "node": 3128, "naklon": 0 }, +{ "label": "13/24", "node": 3197, "naklon": 0 }, +{ "label": "13/25", "node": 4364, "naklon": 0 }, +{ "label": "13/26", "node": 3155, "naklon": 0 }, +{ "label": "13/27", "node": 3196, "naklon": 0 }, +{ "label": "13/28", "node": 3144, "naklon": 0 }, +{ "label": "13/30", "node": 3150, "naklon": 0 }, +{ "label": "13/31", "node": 3390, "naklon": 0 }, +{ "label": "13/32", "node": 3401, "naklon": 0 }, +{ "label": "13/33", "node": 3275, "naklon": 0 }, +{ "label": "13/35", "node": 3371, "naklon": 0 }, +{ "label": "13/36", "node": 3389, "naklon": 0 }, +{ "label": "13/37", "node": 3388, "naklon": 0 }, +{ "label": "13/38", "node": 3365, "naklon": 0 }, +{ "label": "13/39", "node": 3363, "naklon": 0 }, +{ "label": "13/40", "node": 3397, "naklon": 0 }, +{ "label": "13/40/1", "node": 3567, "naklon": 0 }, +{ "label": "13/41", "node": 3387, "naklon": 0 }, +{ "label": "13/42", "node": 3403, "naklon": 0 }, +{ "label": "13/43", "node": 3147, "naklon": 0 }, +{ "label": "13/44", "node": 3141, "naklon": 0 }, +{ "label": "13/46", "node": 3364, "naklon": 0 }, +{ "label": "13/47", "node": 3367, "naklon": 0 }, +{ "label": "13/48", "node": 3368, "naklon": 0 }, +{ "label": "13/49", "node": 3402, "naklon": 0 }, +{ "label": "13/50", "node": 3366, "naklon": 0 }, +{ "label": "13/51", "node": 3395, "naklon": 0 }, +{ "label": "13/52", "node": 3372, "naklon": 0 }, +{ "label": "13/53", "node": 3394, "naklon": 0 }, +{ "label": "13/54", "node": 3308, "naklon": 0 }, +{ "label": "13/54/1", "node": 3847, "naklon": 0 }, +{ "label": "13/55", "node": 3312, "naklon": 3 }, +{ "label": "13/56", "node": 3309, "naklon": 3 }, +{ "label": "13/57", "node": 3311, "naklon": 3 }, +{ "label": "13/58", "node": 3310, "naklon": 3 }, +{ "label": "13/59", "node": 4365, "naklon": 3 }, +{ "label": "13/60/1", "node": 4093, "naklon": 3 }, +{ "label": "13/61", "node": 3304, "naklon": 3 }, +{ "label": "13/62", "node": 3307, "naklon": 3 }, +{ "label": "13/63", "node": 3271, "naklon": 3 }, +{ "label": "13/64", "node": 3301, "naklon": 3 }, +{ "label": "13/65", "node": 4366, "naklon": 3 }, +{ "label": "13/66", "node": 3300, "naklon": 3 }, +{ "label": "13/67", "node": 3185, "naklon": 0 }, +{ "label": "13/68", "node": 3184, "naklon": 0 }, +{ "label": "13/69", "node": 3188, "naklon": 0 }, +{ "label": "13/71", "node": 3343, "naklon": 6 }, +{ "label": "13/71/1", "node": 3819, "naklon": 6 }, +{ "label": "13/71/2", "node": 3880, "naklon": 6 }, +{ "label": "13/72", "node": 3348, "naklon": 6 }, +{ "label": "13/74", "node": 3344, "naklon": 6 }, +{ "label": "13/75", "node": 3346, "naklon": 6 }, +{ "label": "13/75/1", "node": 3597, "naklon": 6 }, +{ "label": "13/76", "node": 3345, "naklon": 6 }, +{ "label": "13/77", "node": 3322, "naklon": 0 }, +{ "label": "13/78", "node": 3325, "naklon": 0 }, +{ "label": "13/78/1", "node": 4310, "naklon": 0 }, +{ "label": "13/79", "node": 3315, "naklon": 0 }, +{ "label": "13/80", "node": 3328, "naklon": 0 }, +{ "label": "13/81", "node": 3321, "naklon": 0 }, +{ "label": "13/82", "node": 3324, "naklon": 0 }, +{ "label": "13/84", "node": 3140, "naklon": 0 }, +{ "label": "13/85", "node": 3153, "naklon": 0 }, +{ "label": "13/86", "node": 3145, "naklon": 0 }, +{ "label": "13/87", "node": 3134, "naklon": 0 }, +{ "label": "13/88", "node": 3151, "naklon": 0 }, +{ "label": "13/89", "node": 3143, "naklon": 0 }, +{ "label": "13/90", "node": 3318, "naklon": 0 }, +{ "label": "13/91", "node": 3347, "naklon": 0 }, +{ "label": "13/94", "node": 3314, "naklon": 0 }, +{ "label": "13/96", "node": 3331, "naklon": 0 }, +{ "label": "13/97", "node": 3327, "naklon": 0 }, +{ "label": "13/99", "node": 3329, "naklon": 0 }, +{ "label": "13/100", "node": 3337, "naklon": 0 }, +{ "label": "13/101", "node": 3330, "naklon": 0 }, +{ "label": "13/102", "node": 3335, "naklon": 0 }, +{ "label": "13/103", "node": 3420, "naklon": 0 }, +{ "label": "13/104", "node": 3421, "naklon": 0 }, +{ "label": "13/105", "node": 4361, "naklon": 0 }, +{ "label": "13/106", "node": 4362, "naklon": 0 }, +{ "label": "13/107", "node": 3526, "naklon": 0 }, +{ "label": "13/108", "node": 4424, "naklon": null }, +{ "label": "13/109", "node": 3561, "naklon": null }, +{ "label": "13/110", "node": 3527, "naklon": null }, +{ "label": "13/111", "node": 3521, "naklon": null }, +{ "label": "13/112", "node": 3552, "naklon": null }, +{ "label": "13/113", "node": 3544, "naklon": null }, +{ "label": "13/114", "node": 3534, "naklon": null }, +{ "label": "13/115", "node": 3516, "naklon": null }, +{ "label": "13/116", "node": 3513, "naklon": null }, +{ "label": "13/117", "node": 3565, "naklon": null }, +{ "label": "13/118", "node": 3866, "naklon": null }, +{ "label": "14/1", "node": 3136, "naklon": 0 }, +{ "label": "14/2", "node": 3137, "naklon": 0 }, +{ "label": "14/3", "node": 3135, "naklon": 0 }, +{ "label": "14/4", "node": 3149, "naklon": 0 }, +{ "label": "14/5", "node": 3142, "naklon": 0 }, +{ "label": "14/6", "node": 3342, "naklon": 0 }, +{ "label": "14/7", "node": 3340, "naklon": 0 }, +{ "label": "14/8", "node": 3152, "naklon": 0 }, +{ "label": "14/9", "node": 3886, "naklon": 0 }, +{ "label": "14/10", "node": 3341, "naklon": null }, +{ "label": "14/11", "node": 3352, "naklon": null }, +{ "label": "15/1", "node": 3396, "naklon": 5 }, +{ "label": "15/2", "node": 2784, "naklon": 5 }, +{ "label": "15/3", "node": 2788, "naklon": 5 }, +{ "label": "15/4", "node": 2801, "naklon": 5 }, +{ "label": "15/5", "node": 2815, "naklon": 5 }, +{ "label": "15/6", "node": 2829, "naklon": 5 }, +{ "label": "15/7", "node": 2796, "naklon": 5 }, +{ "label": "15/8", "node": 2803, "naklon": 5 }, +{ "label": "15/8/1", "node": 4304, "naklon": 5 }, +{ "label": "15/8/2", "node": 4311, "naklon": 5 }, +{ "label": "15/9", "node": 2821, "naklon": 5 }, +{ "label": "15/10", "node": 3400, "naklon": 5 }, +{ "label": "15/11", "node": 2799, "naklon": 5 }, +{ "label": "15/12", "node": 2804, "naklon": 5 }, +{ "label": "15/12/1", "node": 4303, "naklon": 5 }, +{ "label": "15/12/2", "node": 4309, "naklon": 5 }, +{ "label": "15/13", "node": 2806, "naklon": 5 }, +{ "label": "15/14", "node": 2787, "naklon": 5 }, +{ "label": "15/15", "node": 2809, "naklon": 5 }, +{ "label": "15/16", "node": 2814, "naklon": 0 }, +{ "label": "15/17", "node": 3934, "naklon": 0 }, +{ "label": "15/18", "node": 3192, "naklon": 0 }, +{ "label": "15/19", "node": 2812, "naklon": 0 }, +{ "label": "15/20", "node": 4070, "naklon": 0 }, +{ "label": "15/21", "node": 2826, "naklon": 0 }, +{ "label": "15/22", "node": 2808, "naklon": 3 }, +{ "label": "15/23", "node": 2795, "naklon": 3 }, +{ "label": "15/24", "node": 2807, "naklon": 3 }, +{ "label": "15/25", "node": 2831, "naklon": 0 }, +{ "label": "15/26", "node": 2862, "naklon": null }, +{ "label": "15/27", "node": 2861, "naklon": 0 }, +{ "label": "15/28", "node": 2866, "naklon": 0 }, +{ "label": "15/28/1", "node": 3546, "naklon": 0 }, +{ "label": "15/29", "node": 4146, "naklon": 0 }, +{ "label": "15/29/1", "node": 3001, "naklon": 0 }, +{ "label": "15/30", "node": 2835, "naklon": 0 }, +{ "label": "15/31", "node": 2874, "naklon": 0 }, +{ "label": "15/32", "node": 2852, "naklon": 0 }, +{ "label": "15/33", "node": 2851, "naklon": 0 }, +{ "label": "15/33/1", "node": 3560, "naklon": 0 }, +{ "label": "15/34", "node": 2858, "naklon": 0 }, +{ "label": "15/34/1", "node": 4306, "naklon": 0 }, +{ "label": "15/35", "node": 2856, "naklon": 0 }, +{ "label": "15/36", "node": 2847, "naklon": 0 }, +{ "label": "15/36/1", "node": 3556, "naklon": 0 }, +{ "label": "15/37", "node": 3950, "naklon": 0 }, +{ "label": "15/38", "node": 2848, "naklon": 0 }, +{ "label": "15/39", "node": 2863, "naklon": 0 }, +{ "label": "15/39/1", "node": 4313, "naklon": 0 }, +{ "label": "15/40", "node": 2873, "naklon": 0 }, +{ "label": "15/40/1", "node": 2984, "naklon": 0 }, +{ "label": "15/41", "node": 2854, "naklon": 0 }, +{ "label": "15/42", "node": 2859, "naklon": 0 }, +{ "label": "15/43", "node": 2872, "naklon": 0 }, +{ "label": "15/44", "node": 2849, "naklon": 0 }, +{ "label": "15/45", "node": 2864, "naklon": 0 }, +{ "label": "15/46", "node": 3553, "naklon": null }, +{ "label": "15/47", "node": 3021, "naklon": null }, +{ "label": "15/47/1", "node": 3008, "naklon": null }, +{ "label": "15/48", "node": 3017, "naklon": null }, +{ "label": "15/49", "node": 3014, "naklon": null }, +{ "label": "15/50", "node": 2981, "naklon": null }, +{ "label": "15/51", "node": 3020, "naklon": null }, +{ "label": "15/52", "node": 3027, "naklon": null }, +{ "label": "15/53", "node": 3006, "naklon": null }, +{ "label": "15/54", "node": 3012, "naklon": null }, +{ "label": "15/55", "node": 2738, "naklon": 0 }, +{ "label": "15/56", "node": 2725, "naklon": 0 }, +{ "label": "15/57", "node": 3016, "naklon": null }, +{ "label": "15/58", "node": 3005, "naklon": null }, +{ "label": "15/58/1", "node": 3009, "naklon": null }, +{ "label": "15/59", "node": 3010, "naklon": 0 }, +{ "label": "15/59/1", "node": 3538, "naklon": 0 }, +{ "label": "15/59/2", "node": 4145, "naklon": 0 }, +{ "label": "15/60", "node": 2713, "naklon": 0 }, +{ "label": "15/61", "node": 2789, "naklon": 0 }, +{ "label": "15/62", "node": 2736, "naklon": 0 }, +{ "label": "15/63", "node": 2739, "naklon": 0 }, +{ "label": "15/64", "node": 4346, "naklon": 0 }, +{ "label": "15/65", "node": 2731, "naklon": 0 }, +{ "label": "15/66", "node": 2818, "naklon": 0 }, +{ "label": "15/66/1", "node": 3557, "naklon": 0 }, +{ "label": "15/67", "node": 2822, "naklon": 0 }, +{ "label": "15/68", "node": 2833, "naklon": 0 }, +{ "label": "15/69", "node": 2834, "naklon": 0 }, +{ "label": "15/70", "node": 3097, "naklon": 0 }, +{ "label": "15/71", "node": 2824, "naklon": 0 }, +{ "label": "15/72", "node": 2828, "naklon": 0 }, +{ "label": "15/73", "node": 2600, "naklon": 0 }, +{ "label": "15/73/1", "node": 4314, "naklon": 0 }, +{ "label": "15/74", "node": 2832, "naklon": 0 }, +{ "label": "15/75", "node": 4360, "naklon": 0 }, +{ "label": "15/76", "node": 2846, "naklon": 0 }, +{ "label": "15/77", "node": 2584, "naklon": 0 }, +{ "label": "15/78", "node": 2843, "naklon": 0 }, +{ "label": "15/78/1", "node": 4308, "naklon": 0 }, +{ "label": "15/79", "node": 4144, "naklon": null }, +{ "label": "15/80", "node": 2844, "naklon": 0 }, +{ "label": "15/81", "node": 2842, "naklon": 0 }, +{ "label": "15/82", "node": 2838, "naklon": 0 }, +{ "label": "15/83", "node": 2837, "naklon": null }, +{ "label": "15/83/1", "node": 3555, "naklon": null }, +{ "label": "15/84", "node": 2839, "naklon": 0 }, +{ "label": "15/85", "node": 2830, "naklon": 0 }, +{ "label": "15/86", "node": 4066, "naklon": 0 }, +{ "label": "15/87", "node": 2836, "naklon": 0 }, +{ "label": "15/88", "node": 2911, "naklon": 0 }, +{ "label": "15/89", "node": 2825, "naklon": 0 }, +{ "label": "15/90", "node": 2811, "naklon": 0 }, +{ "label": "15/94", "node": 3740, "naklon": 15 }, +{ "label": "15/95", "node": 2734, "naklon": 15 }, +{ "label": "15/96", "node": 3105, "naklon": 15 }, +{ "label": "15/97", "node": 2732, "naklon": 15 }, +{ "label": "15/98", "node": 2727, "naklon": 15 }, +{ "label": "15/99", "node": 2735, "naklon": 15 }, +{ "label": "15/100", "node": 3926, "naklon": 15 }, +{ "label": "15/101", "node": 2918, "naklon": 0 }, +{ "label": "15/102", "node": 2705, "naklon": 10 }, +{ "label": "15/103", "node": 2708, "naklon": 10 }, +{ "label": "15/104", "node": 2721, "naklon": 10 }, +{ "label": "15/105", "node": 2719, "naklon": 10 }, +{ "label": "15/106", "node": 2919, "naklon": 10 }, +{ "label": "15/107", "node": 3101, "naklon": 10 }, +{ "label": "15/108", "node": 2853, "naklon": 10 }, +{ "label": "15/109", "node": 2715, "naklon": 10 }, +{ "label": "15/110", "node": 2712, "naklon": 0 }, +{ "label": "15/111", "node": 2711, "naklon": 0 }, +{ "label": "15/112", "node": 2709, "naklon": 0 }, +{ "label": "15/113", "node": 2710, "naklon": 0 }, +{ "label": "15/114", "node": 2907, "naklon": 0 }, +{ "label": "15/115", "node": 2906, "naklon": 0 }, +{ "label": "15/116", "node": 2908, "naklon": 0 }, +{ "label": "15/117", "node": 2901, "naklon": 0 }, +{ "label": "15/118", "node": 2913, "naklon": 0 }, +{ "label": "15/119", "node": 3656, "naklon": 0 }, +{ "label": "15/120", "node": 2781, "naklon": 0 }, +{ "label": "15/121", "node": 2802, "naklon": 0 }, +{ "label": "15/122", "node": 2791, "naklon": 0 }, +{ "label": "15/123", "node": 2783, "naklon": 0 }, +{ "label": "15/124", "node": 2794, "naklon": 0 }, +{ "label": "15/125", "node": 3139, "naklon": 0 }, +{ "label": "15/126", "node": 3229, "naklon": 5 }, +{ "label": "15/127", "node": 3239, "naklon": 5 }, +{ "label": "15/128", "node": 3230, "naklon": 5 }, +{ "label": "15/129", "node": 4016, "naklon": null }, +{ "label": "16/16A", "node": 3608, "naklon": 0 }, +{ "label": "16/16B", "node": 4151, "naklon": 0 }, +{ "label": "16/1", "node": 3710, "naklon": 0 }, +{ "label": "16/2", "node": 3726, "naklon": 0 }, +{ "label": "16/3", "node": 3743, "naklon": 0 }, +{ "label": "16/4", "node": 3724, "naklon": 0 }, +{ "label": "16/5", "node": 3712, "naklon": 0 }, +{ "label": "16/6", "node": 3739, "naklon": 0 }, +{ "label": "16/7", "node": 3742, "naklon": 0 }, +{ "label": "16/8", "node": 3731, "naklon": 0 }, +{ "label": "16/9", "node": 3727, "naklon": 0 }, +{ "label": "16/10", "node": 3729, "naklon": 0 }, +{ "label": "16/11", "node": 3736, "naklon": 0 }, +{ "label": "16/12", "node": 3713, "naklon": 0 }, +{ "label": "16/13", "node": 3715, "naklon": 0 }, +{ "label": "16/14", "node": 3733, "naklon": 0 }, +{ "label": "16/15", "node": 3732, "naklon": 0 }, +{ "label": "16/17", "node": 3634, "naklon": 0 }, +{ "label": "16/18", "node": 3610, "naklon": 0 }, +{ "label": "16/19", "node": 3626, "naklon": 0 }, +{ "label": "16/20", "node": 3618, "naklon": 0 }, +{ "label": "16/21", "node": 3619, "naklon": 0 }, +{ "label": "16/22", "node": 3628, "naklon": 0 }, +{ "label": "16/23", "node": 3615, "naklon": 0 }, +{ "label": "16/24", "node": 3609, "naklon": 0 }, +{ "label": "16/25", "node": 3606, "naklon": 0 }, +{ "label": "16/26", "node": 3624, "naklon": 0 }, +{ "label": "16/27", "node": 3627, "naklon": 0 }, +{ "label": "16/28", "node": 3607, "naklon": 0 }, +{ "label": "16/29", "node": 4370, "naklon": 0 }, +{ "label": "16/30", "node": 3692, "naklon": 0 }, +{ "label": "16/31", "node": 3614, "naklon": 0 }, +{ "label": "16/32", "node": 3623, "naklon": 0 }, +{ "label": "16/33", "node": 4373, "naklon": 0 }, +{ "label": "16/34", "node": 3620, "naklon": 0 }, +{ "label": "16/35", "node": 3685, "naklon": 0 }, +{ "label": "16/36", "node": 3616, "naklon": 0 }, +{ "label": "16/37", "node": 3686, "naklon": 0 }, +{ "label": "16/38", "node": 3688, "naklon": 0 }, +{ "label": "16/39", "node": 3684, "naklon": 0 }, +{ "label": "16/40", "node": 3825, "naklon": 0 }, +{ "label": "16/41", "node": 3865, "naklon": 0 }, +{ "label": "16/42", "node": 3824, "naklon": 0 }, +{ "label": "16/43", "node": 3871, "naklon": 0 }, +{ "label": "16/44", "node": 3801, "naklon": 0 }, +{ "label": "16/45", "node": 3862, "naklon": 0 }, +{ "label": "16/46", "node": 3876, "naklon": 0 }, +{ "label": "16/47", "node": 3861, "naklon": 0 }, +{ "label": "16/48", "node": 4215, "naklon": 0 }, +{ "label": "16/49", "node": 3605, "naklon": 10 }, +{ "label": "16/50", "node": 3603, "naklon": 10 }, +{ "label": "16/51", "node": 3592, "naklon": 10 }, +{ "label": "16/52", "node": 3598, "naklon": 10 }, +{ "label": "16/53", "node": 3594, "naklon": 10 }, +{ "label": "16/54", "node": 3593, "naklon": 10 }, +{ "label": "16/55", "node": 3630, "naklon": 10 }, +{ "label": "16/56", "node": 3763, "naklon": 10 }, +{ "label": "16/57", "node": 3746, "naklon": 10 }, +{ "label": "16/58", "node": 3699, "naklon": 0 }, +{ "label": "16/59", "node": 3698, "naklon": 0 }, +{ "label": "16/60", "node": 3709, "naklon": null }, +{ "label": "16/61", "node": 3708, "naklon": 0 }, +{ "label": "16/62", "node": 3703, "naklon": null }, +{ "label": "16/63", "node": 3700, "naklon": 0 }, +{ "label": "16/64", "node": 3701, "naklon": null }, +{ "label": "16/65", "node": 3704, "naklon": 0 }, +{ "label": "16/66", "node": 3707, "naklon": null }, +{ "label": "16/67", "node": 3810, "naklon": 0 }, +{ "label": "16/68", "node": 4208, "naklon": null }, +{ "label": "16/69", "node": 3867, "naklon": 0 }, +{ "label": "16/70", "node": 4372, "naklon": null }, +{ "label": "16/71", "node": 3881, "naklon": 0 }, +{ "label": "16/72", "node": 4204, "naklon": null }, +{ "label": "16/73", "node": 3869, "naklon": 0 }, +{ "label": "16/74", "node": 4202, "naklon": null }, +{ "label": "16/75", "node": 3830, "naklon": 0 }, +{ "label": "16/76", "node": 3870, "naklon": null }, +{ "label": "16/77", "node": 3964, "naklon": 0 }, +{ "label": "16/78", "node": 3849, "naklon": 0 }, +{ "label": "16/79", "node": 3694, "naklon": null }, +{ "label": "16/80", "node": 3717, "naklon": 0 }, +{ "label": "16/81", "node": 3877, "naklon": null }, +{ "label": "16/82", "node": 3755, "naklon": 0 }, +{ "label": "16/83", "node": 3725, "naklon": null }, +{ "label": "16/84", "node": 3716, "naklon": 0 }, +{ "label": "16/85", "node": 3696, "naklon": null }, +{ "label": "16/86", "node": 3702, "naklon": 0 }, +{ "label": "16/87", "node": 3706, "naklon": null }, +{ "label": "16/88", "node": 3850, "naklon": 0 }, +{ "label": "16/89", "node": 3848, "naklon": null }, +{ "label": "16/90", "node": 3737, "naklon": null }, +{ "label": "16/91", "node": 3705, "naklon": 0 }, +{ "label": "16/92", "node": 3723, "naklon": null }, +{ "label": "16/93", "node": 3842, "naklon": 0 }, +{ "label": "16/94", "node": 3735, "naklon": null }, +{ "label": "16/95", "node": 3719, "naklon": 0 }, +{ "label": "16/96", "node": 3697, "naklon": 0 }, +{ "label": "16/97", "node": 3711, "naklon": 0 }, +{ "label": "16/98", "node": 4107, "naklon": null }, +{ "label": "16/99", "node": 4112, "naklon": 5 }, +{ "label": "16/100", "node": 4106, "naklon": 5 }, +{ "label": "16/101", "node": 4111, "naklon": 5 }, +{ "label": "16/102", "node": 4091, "naklon": null }, +{ "label": "16/103", "node": 4104, "naklon": 5 }, +{ "label": "16/104", "node": 4094, "naklon": null }, +{ "label": "16/105", "node": 4089, "naklon": 5 }, +{ "label": "16/106", "node": 4095, "naklon": null }, +{ "label": "16/107", "node": 4102, "naklon": 5 }, +{ "label": "16/108", "node": 4105, "naklon": null }, +{ "label": "16/109", "node": 3859, "naklon": null }, +{ "label": "16/110", "node": 4090, "naklon": 5 }, +{ "label": "16/111", "node": 4110, "naklon": null }, +{ "label": "16/112", "node": 4097, "naklon": 5 }, +{ "label": "16/113", "node": 4099, "naklon": null }, +{ "label": "16/114", "node": 4096, "naklon": null }, +{ "label": "16/115", "node": 4101, "naklon": 5 }, +{ "label": "16/116", "node": 4109, "naklon": null }, +{ "label": "16/117", "node": 4092, "naklon": 5 }, +{ "label": "16/118", "node": 3631, "naklon": null }, +{ "label": "16/119", "node": 3632, "naklon": 0 }, +{ "label": "16/120", "node": 3641, "naklon": null }, +{ "label": "16/121", "node": 3638, "naklon": 0 }, +{ "label": "16/122", "node": 3637, "naklon": null }, +{ "label": "16/123", "node": 4142, "naklon": null }, +{ "label": "16/124", "node": 4147, "naklon": 5 }, +{ "label": "16/125", "node": 4139, "naklon": 5 }, +{ "label": "20/30A", "node": 3172, "naklon": 2 }, +{ "label": "20/30B", "node": 3164, "naklon": 2 }, +{ "label": "20/1", "node": 3247, "naklon": 10 }, +{ "label": "20/2", "node": 3296, "naklon": 10 }, +{ "label": "20/3", "node": 3246, "naklon": 10 }, +{ "label": "20/4", "node": 3270, "naklon": 10 }, +{ "label": "20/5", "node": 3294, "naklon": 10 }, +{ "label": "20/6", "node": 3298, "naklon": 10 }, +{ "label": "20/7", "node": 3297, "naklon": 10 }, +{ "label": "20/8", "node": 3293, "naklon": 10 }, +{ "label": "20/9", "node": 3299, "naklon": 10 }, +{ "label": "20/10", "node": 3295, "naklon": 10 }, +{ "label": "20/11", "node": 3244, "naklon": 10 }, +{ "label": "20/12", "node": 3249, "naklon": 10 }, +{ "label": "20/13", "node": 3268, "naklon": 10 }, +{ "label": "20/14", "node": 3245, "naklon": 10 }, +{ "label": "20/15", "node": 3278, "naklon": 10 }, +{ "label": "20/16", "node": 3280, "naklon": 10 }, +{ "label": "20/17", "node": 3292, "naklon": 10 }, +{ "label": "20/18", "node": 3287, "naklon": 10 }, +{ "label": "20/19", "node": 3282, "naklon": 10 }, +{ "label": "20/20", "node": 3273, "naklon": 0 }, +{ "label": "20/21", "node": 3157, "naklon": 0 }, +{ "label": "20/22", "node": 3283, "naklon": 0 }, +{ "label": "20/23", "node": 3159, "naklon": 0 }, +{ "label": "20/24", "node": 3289, "naklon": 0 }, +{ "label": "20/25", "node": 3163, "naklon": 0 }, +{ "label": "20/26", "node": 3126, "naklon": 0 }, +{ "label": "20/27", "node": 3171, "naklon": 0 }, +{ "label": "20/28", "node": 3162, "naklon": 0 }, +{ "label": "20/29", "node": 3279, "naklon": 10 }, +{ "label": "20/31", "node": 3173, "naklon": 0 }, +{ "label": "20/32", "node": 3127, "naklon": 0 }, +{ "label": "20/33", "node": 3131, "naklon": 0 }, +{ "label": "20/34", "node": 3129, "naklon": 0 }, +{ "label": "20/35", "node": 3276, "naklon": 2 }, +{ "label": "20/36", "node": 3290, "naklon": 2 }, +{ "label": "20/37", "node": 3132, "naklon": 0 }, +{ "label": "20/38", "node": 3138, "naklon": 0 }, +{ "label": "20/39", "node": 3272, "naklon": 0 }, +{ "label": "20/40", "node": 3291, "naklon": 0 }, +{ "label": "20/41", "node": 3277, "naklon": 10 }, +{ "label": "20/42", "node": 3286, "naklon": 10 }, +{ "label": "20/43", "node": 3281, "naklon": 10 }, +{ "label": "20/44", "node": 3288, "naklon": 10 }, +{ "label": "20/45", "node": 3284, "naklon": 10 }, +{ "label": "20/46", "node": 3167, "naklon": 10 }, +{ "label": "20/47", "node": 3872, "naklon": 0 }, +{ "label": "20/48", "node": 3124, "naklon": 0 }, +{ "label": "20/49", "node": 3133, "naklon": 0 }, +{ "label": "20/50", "node": 3158, "naklon": 0 }, +{ "label": "20/51", "node": 3169, "naklon": 0 }, +{ "label": "20/52", "node": 3146, "naklon": 0 }, +{ "label": "20/53", "node": 3160, "naklon": 0 }, +{ "label": "20/54", "node": 3248, "naklon": 0 }, +{ "label": "20/55", "node": 3156, "naklon": 0 }, +{ "label": "20/56", "node": 3161, "naklon": 0 }, +{ "label": "20/57", "node": 3170, "naklon": 0 }, +{ "label": "20/58", "node": 3168, "naklon": 0 }, +{ "label": "20/59", "node": 3125, "naklon": 0 }, +{ "label": "20/60", "node": 3166, "naklon": 0 }, +{ "label": "20/61", "node": 3130, "naklon": 0 }, +{ "label": "20/62", "node": 3563, "naklon": 0 }, +{ "label": "20/63", "node": 3550, "naklon": null }, +{ "label": "20/67", "node": 3269, "naklon": 0 }, +{ "label": "21/1", "node": 2921, "naklon": 0 }, +{ "label": "21/2", "node": 2707, "naklon": 0 }, +{ "label": "21/3", "node": 2723, "naklon": 0 }, +{ "label": "21/4", "node": 2716, "naklon": 0 }, +{ "label": "21/5", "node": 2717, "naklon": 0 }, +{ "label": "21/6", "node": 2720, "naklon": 0 }, +{ "label": "21/7", "node": 2722, "naklon": 0 }, +{ "label": "21/8", "node": 2718, "naklon": 0 }, +{ "label": "21/9", "node": 2724, "naklon": 0 }, +{ "label": "21/10", "node": 2742, "naklon": 0 }, +{ "label": "21/11", "node": 2743, "naklon": 0 }, +{ "label": "21/12", "node": 2733, "naklon": 0 }, +{ "label": "21/14", "node": 2903, "naklon": 0 }, +{ "label": "21/15", "node": 2917, "naklon": 0 }, +{ "label": "21/16", "node": 2897, "naklon": 0 }, +{ "label": "21/17", "node": 2914, "naklon": 0 }, +{ "label": "21/18", "node": 2898, "naklon": 0 }, +{ "label": "21/19", "node": 2773, "naklon": 0 }, +{ "label": "21/20", "node": 2895, "naklon": 0 }, +{ "label": "21/21", "node": 2896, "naklon": 0 }, +{ "label": "21/22", "node": 2771, "naklon": 0 }, +{ "label": "21/23", "node": 2772, "naklon": 0 }, +{ "label": "21/24", "node": 2793, "naklon": 0 }, +{ "label": "21/25", "node": 2902, "naklon": 0 }, +{ "label": "21/26", "node": 2910, "naklon": 0 }, +{ "label": "21/27", "node": 2909, "naklon": 0 }, +{ "label": "21/28", "node": 2920, "naklon": 0 }, +{ "label": "21/29", "node": 2916, "naklon": 0 }, +{ "label": "21/30", "node": 2904, "naklon": 0 }, +{ "label": "21/31", "node": 2915, "naklon": 0 }, +{ "label": "21/32", "node": 2905, "naklon": 0 }, +{ "label": "21/33", "node": 2900, "naklon": 0 }, +{ "label": "21/34", "node": 2899, "naklon": 0 }, +{ "label": "21/36", "node": 2741, "naklon": 0 }, +{ "label": "21/37", "node": 2714, "naklon": 0 }, +{ "label": "21/38", "node": 2701, "naklon": 0 }, +{ "label": "21/39", "node": 2703, "naklon": 0 }, +{ "label": "21/40", "node": 2704, "naklon": 0 }, +{ "label": "21/41", "node": 2702, "naklon": 0 }, +{ "label": "21/42", "node": 2706, "naklon": 0 }, +{ "label": "21/43", "node": 2776, "naklon": 0 }, +{ "label": "21/44", "node": 2770, "naklon": 0 }, +{ "label": "21/45", "node": 2786, "naklon": 0 }, +{ "label": "21/46", "node": 2779, "naklon": 0 }, +{ "label": "21/47", "node": 2782, "naklon": 0 }, +{ "label": "21/48", "node": 2785, "naklon": 0 }, +{ "label": "21/49", "node": 2769, "naklon": 0 }, +{ "label": "21/50", "node": 3354, "naklon": 0 }, +{ "label": "21/51", "node": 2778, "naklon": 0 }, +{ "label": "21/52", "node": 2775, "naklon": 0 }, +{ "label": "21/53", "node": 2780, "naklon": 0 }, +{ "label": "21/54", "node": 2790, "naklon": 0 }, +{ "label": "21/55", "node": 2792, "naklon": 0 }, +{ "label": "21/56", "node": 2774, "naklon": 0 }, +{ "label": "22/1", "node": 2630, "naklon": 0 }, +{ "label": "22/2", "node": 2631, "naklon": 0 }, +{ "label": "22/3", "node": 2632, "naklon": 0 }, +{ "label": "22/4", "node": 2633, "naklon": 0 }, +{ "label": "22/5", "node": 2634, "naklon": 0 }, +{ "label": "22/6", "node": 2636, "naklon": 0 }, +{ "label": "22/7", "node": 4367, "naklon": 0 }, +{ "label": "22/8", "node": 2637, "naklon": 0 }, +{ "label": "22/9", "node": 2638, "naklon": 0 }, +{ "label": "22/10", "node": 2639, "naklon": 0 }, +{ "label": "22/11", "node": 2640, "naklon": 0 }, +{ "label": "22/12", "node": 2641, "naklon": 0 }, +{ "label": "22/13", "node": 2642, "naklon": 0 }, +{ "label": "22/14", "node": 2643, "naklon": 0 }, +{ "label": "22/15", "node": 2644, "naklon": 0 }, +{ "label": "22/16", "node": 2645, "naklon": 0 }, +{ "label": "22/17", "node": 2646, "naklon": 0 }, +{ "label": "22/18", "node": 2647, "naklon": 0 }, +{ "label": "22/19", "node": 2648, "naklon": 0 }, +{ "label": "22/20", "node": 2649, "naklon": 0 }, +{ "label": "22/21", "node": 2650, "naklon": 0 }, +{ "label": "22/22", "node": 2651, "naklon": 0 }, +{ "label": "22/23", "node": 4103, "naklon": 0 }, +{ "label": "22/24", "node": 2653, "naklon": 0 }, +{ "label": "22/25", "node": 2654, "naklon": 0 }, +{ "label": "22/26", "node": 2655, "naklon": 0 }, +{ "label": "22/27", "node": 2656, "naklon": 0 }, +{ "label": "22/28", "node": 2657, "naklon": 0 }, +{ "label": "22/29", "node": 2658, "naklon": 0 }, +{ "label": "22/30", "node": 2659, "naklon": 0 }, +{ "label": "22/31", "node": 2660, "naklon": 0 }, +{ "label": "22/32", "node": 2661, "naklon": 0 }, +{ "label": "22/32/1", "node": 3015, "naklon": 0 }, +{ "label": "22/33", "node": 2662, "naklon": 0 }, +{ "label": "22/34", "node": 2663, "naklon": 0 }, +{ "label": "22/35", "node": 2664, "naklon": 0 }, +{ "label": "22/36", "node": 2665, "naklon": 0 }, +{ "label": "22/37", "node": 2666, "naklon": 0 }, +{ "label": "22/38", "node": 2667, "naklon": 0 }, +{ "label": "22/39", "node": 2668, "naklon": 0 }, +{ "label": "22/40", "node": 2669, "naklon": null }, +{ "label": "22/41", "node": 2670, "naklon": null }, +{ "label": "22/42", "node": 2671, "naklon": null }, +{ "label": "23/9A", "node": 3087, "naklon": 10 }, +{ "label": "23/9B", "node": 3090, "naklon": 10 }, +{ "label": "23/1", "node": 3075, "naklon": 0 }, +{ "label": "23/2", "node": 3089, "naklon": 0 }, +{ "label": "23/3", "node": 3088, "naklon": 0 }, +{ "label": "23/4", "node": 3228, "naklon": 10 }, +{ "label": "23/5", "node": 2751, "naklon": 10 }, +{ "label": "23/6", "node": 3243, "naklon": 10 }, +{ "label": "23/7", "node": 4349, "naklon": 10 }, +{ "label": "23/8", "node": 3102, "naklon": 10 }, +{ "label": "23/10", "node": 3099, "naklon": 10 }, +{ "label": "23/11", "node": 3103, "naklon": 10 }, +{ "label": "23/12", "node": 3093, "naklon": 10 }, +{ "label": "23/13", "node": 3098, "naklon": 10 }, +{ "label": "23/14", "node": 3092, "naklon": 10 }, +{ "label": "23/15", "node": 3233, "naklon": 0 }, +{ "label": "23/16", "node": 3095, "naklon": 0 }, +{ "label": "23/17", "node": 4074, "naklon": 0 }, +{ "label": "23/18", "node": 2729, "naklon": 0 }, +{ "label": "23/19", "node": 3234, "naklon": 0 }, +{ "label": "23/20", "node": 3094, "naklon": 0 }, +{ "label": "23/21", "node": 2817, "naklon": 0 }, +{ "label": "23/22", "node": 3226, "naklon": 0 }, +{ "label": "23/23", "node": 3225, "naklon": 0 }, +{ "label": "23/24", "node": 3237, "naklon": 0 }, +{ "label": "23/25", "node": 4250, "naklon": 0 }, +{ "label": "23/26", "node": 3059, "naklon": 10 }, +{ "label": "23/27", "node": 3238, "naklon": 10 }, +{ "label": "23/28", "node": 3242, "naklon": 10 }, +{ "label": "23/29", "node": 3236, "naklon": 10 }, +{ "label": "23/30", "node": 3112, "naklon": 10 }, +{ "label": "23/31", "node": 3224, "naklon": 0 }, +{ "label": "23/32", "node": 4298, "naklon": 10 }, +{ "label": "23/33", "node": 4150, "naklon": 10 }, +{ "label": "23/34", "node": 3091, "naklon": 10 }, +{ "label": "25/1", "node": 3771, "naklon": 5 }, +{ "label": "25/2", "node": 3777, "naklon": 5 }, +{ "label": "25/3", "node": 3749, "naklon": 5 }, +{ "label": "25/4", "node": 3769, "naklon": 5 }, +{ "label": "25/5", "node": 3765, "naklon": 5 }, +{ "label": "25/6", "node": 3785, "naklon": 5 }, +{ "label": "25/7", "node": 3761, "naklon": 5 }, +{ "label": "25/8", "node": 3758, "naklon": 5 }, +{ "label": "25/9", "node": 3766, "naklon": 5 }, +{ "label": "25/10", "node": 3779, "naklon": 5 }, +{ "label": "25/11", "node": 3899, "naklon": 10 }, +{ "label": "25/12", "node": 3922, "naklon": 10 }, +{ "label": "25/13", "node": 3912, "naklon": 10 }, +{ "label": "25/14", "node": 3933, "naklon": 10 }, +{ "label": "25/15", "node": 3921, "naklon": 10 }, +{ "label": "25/16", "node": 4300, "naklon": 10 }, +{ "label": "25/17", "node": 3927, "naklon": 10 }, +{ "label": "25/18", "node": 3212, "naklon": 10 }, +{ "label": "25/19", "node": 3208, "naklon": 10 }, +{ "label": "25/20", "node": 3207, "naklon": 10 }, +{ "label": "25/21", "node": 4294, "naklon": 10 }, +{ "label": "25/22", "node": 3209, "naklon": 10 }, +{ "label": "25/23", "node": 3745, "naklon": 10 }, +{ "label": "25/24", "node": 3762, "naklon": 10 }, +{ "label": "25/25", "node": 3215, "naklon": 10 }, +{ "label": "25/26", "node": 3210, "naklon": 10 }, +{ "label": "25/27", "node": 3211, "naklon": 10 }, +{ "label": "25/28", "node": 3206, "naklon": 10 }, +{ "label": "25/29", "node": 3918, "naklon": 5 }, +{ "label": "25/30", "node": 3928, "naklon": 5 }, +{ "label": "25/31", "node": 3917, "naklon": 5 }, +{ "label": "25/32", "node": 4029, "naklon": 0 }, +{ "label": "25/33", "node": 4038, "naklon": 0 }, +{ "label": "25/34", "node": 4022, "naklon": 0 }, +{ "label": "25/35", "node": 3937, "naklon": 0 }, +{ "label": "25/36", "node": 3190, "naklon": 0 }, +{ "label": "25/37", "node": 3835, "naklon": 0 }, +{ "label": "25/38", "node": 3201, "naklon": 0 }, +{ "label": "25/39", "node": 3193, "naklon": 0 }, +{ "label": "25/40", "node": 3191, "naklon": 0 }, +{ "label": "25/41", "node": 3203, "naklon": 0 }, +{ "label": "25/42", "node": 4301, "naklon": 0 }, +{ "label": "25/43", "node": 3223, "naklon": 0 }, +{ "label": "25/44", "node": 3202, "naklon": 0 }, +{ "label": "25/45", "node": 3205, "naklon": 0 }, +{ "label": "25/46", "node": 3194, "naklon": 0 }, +{ "label": "25/47", "node": 3198, "naklon": 0 }, +{ "label": "25/48", "node": 3204, "naklon": 0 }, +{ "label": "25/49", "node": 3200, "naklon": 0 }, +{ "label": "25/50", "node": 3216, "naklon": 0 }, +{ "label": "25/51", "node": 3932, "naklon": 5 }, +{ "label": "25/52", "node": 3923, "naklon": 5 }, +{ "label": "25/53", "node": 3913, "naklon": 5 }, +{ "label": "25/54", "node": 3900, "naklon": 5 }, +{ "label": "25/55", "node": 4189, "naklon": 10 }, +{ "label": "25/56", "node": 4191, "naklon": 10 }, +{ "label": "25/57", "node": 3790, "naklon": 10 }, +{ "label": "25/58", "node": 4190, "naklon": 10 }, +{ "label": "25/59", "node": 4302, "naklon": 0 }, +{ "label": "25/60", "node": 4351, "naklon": 0 }, +{ "label": "25/61", "node": 4075, "naklon": 0 }, +{ "label": "25/62", "node": 4071, "naklon": 0 }, +{ "label": "25/63", "node": 3784, "naklon": 0 }, +{ "label": "25/64", "node": 4065, "naklon": 0 }, +{ "label": "25/65", "node": 4297, "naklon": 0 }, +{ "label": "25/66", "node": 4295, "naklon": 0 }, +{ "label": "25/67", "node": 4299, "naklon": 0 }, +{ "label": "25/68", "node": 3772, "naklon": 0 }, +{ "label": "25/69", "node": 3941, "naklon": 0 }, +{ "label": "25/70", "node": 3782, "naklon": 0 }, +{ "label": "25/71", "node": 4068, "naklon": 0 }, +{ "label": "25/72", "node": 4073, "naklon": 0 }, +{ "label": "25/73", "node": 3901, "naklon": 5 }, +{ "label": "25/74", "node": 4296, "naklon": 5 }, +{ "label": "25/75", "node": 3910, "naklon": 5 }, +{ "label": "25/76", "node": 4293, "naklon": 5 }, +{ "label": "25/77", "node": 3919, "naklon": 5 }, +{ "label": "25/78", "node": 3924, "naklon": 5 }, +{ "label": "25/79", "node": 3909, "naklon": 5 }, +{ "label": "25/80", "node": 3896, "naklon": 5 }, +{ "label": "25/81", "node": 3911, "naklon": 5 }, +{ "label": "25/82", "node": 2819, "naklon": 15 }, +{ "label": "25/83", "node": 3776, "naklon": 15 }, +{ "label": "25/84", "node": 3752, "naklon": 15 }, +{ "label": "25/85", "node": 4353, "naklon": 15 }, +{ "label": "25/86", "node": 3791, "naklon": 0 }, +{ "label": "25/87", "node": 3770, "naklon": 0 }, +{ "label": "25/88", "node": 3760, "naklon": 0 }, +{ "label": "25/89", "node": 3115, "naklon": 0 }, +{ "label": "25/90", "node": 3738, "naklon": 0 }, +{ "label": "25/91", "node": 3748, "naklon": 0 }, +{ "label": "25/92", "node": 3753, "naklon": 0 }, +{ "label": "25/93", "node": 3775, "naklon": 0 }, +{ "label": "25/94", "node": 3778, "naklon": 5 }, +{ "label": "25/95", "node": 4078, "naklon": 5 }, +{ "label": "25/96", "node": 4088, "naklon": 5 }, +{ "label": "25/97", "node": 3856, "naklon": 5 }, +{ "label": "25/98", "node": 4084, "naklon": 5 }, +{ "label": "25/99", "node": 4069, "naklon": 5 }, +{ "label": "25/100", "node": 4188, "naklon": 5 }, +{ "label": "25/101", "node": 4064, "naklon": 5 }, +{ "label": "25/102", "node": 3792, "naklon": 5 }, +{ "label": "25/103", "node": 3780, "naklon": 5 }, +{ "label": "25/104", "node": 4034, "naklon": 5 }, +{ "label": "25/105", "node": 4072, "naklon": 5 }, +{ "label": "25/106", "node": 3884, "naklon": 5 }, +{ "label": "25/107", "node": 3793, "naklon": 5 }, +{ "label": "25/108", "node": 3863, "naklon": 5 }, +{ "label": "25/109", "node": 3855, "naklon": 5 }, +{ "label": "33/1", "node": 3846, "naklon": 0 }, +{ "label": "33/2", "node": 3943, "naklon": 0 }, +{ "label": "33/3", "node": 3903, "naklon": 0 }, +{ "label": "33/4", "node": 3982, "naklon": 0 }, +{ "label": "33/5", "node": 3879, "naklon": 0 }, +{ "label": "33/6", "node": 3887, "naklon": 0 }, +{ "label": "33/7", "node": 3920, "naklon": 5 }, +{ "label": "33/8", "node": 3942, "naklon": 5 }, +{ "label": "33/9", "node": 3829, "naklon": null }, +{ "label": "34/79A", "node": 3452, "naklon": 6 }, +{ "label": "34/79B", "node": 3460, "naklon": 6 }, +{ "label": "34/90A", "node": 3443, "naklon": 6 }, +{ "label": "34/90B", "node": 3445, "naklon": 6 }, +{ "label": "34/90C", "node": 3457, "naklon": 6 }, +{ "label": "34/97A", "node": 3461, "naklon": 6 }, +{ "label": "34/97B", "node": 3463, "naklon": 6 }, +{ "label": "34/98A", "node": 3426, "naklon": 6 }, +{ "label": "34/98B", "node": 3444, "naklon": 6 }, +{ "label": "34/99A", "node": 3453, "naklon": 6 }, +{ "label": "34/99B", "node": 3455, "naklon": 6 }, +{ "label": "34/1", "node": 4009, "naklon": 0 }, +{ "label": "34/2", "node": 3602, "naklon": 0 }, +{ "label": "34/3", "node": 3441, "naklon": 0 }, +{ "label": "34/4", "node": 3464, "naklon": 0 }, +{ "label": "34/5", "node": 3883, "naklon": 0 }, +{ "label": "34/6", "node": 4098, "naklon": 0 }, +{ "label": "34/7", "node": 4003, "naklon": 0 }, +{ "label": "34/8", "node": 4004, "naklon": 0 }, +{ "label": "34/9", "node": 3888, "naklon": 0 }, +{ "label": "34/10", "node": 3997, "naklon": 0 }, +{ "label": "34/11", "node": 4006, "naklon": 0 }, +{ "label": "34/12", "node": 4000, "naklon": 0 }, +{ "label": "34/13", "node": 4011, "naklon": 0 }, +{ "label": "34/14", "node": 3999, "naklon": 0 }, +{ "label": "34/15", "node": 3981, "naklon": 0 }, +{ "label": "34/16", "node": 3988, "naklon": 0 }, +{ "label": "34/17", "node": 3986, "naklon": 0 }, +{ "label": "34/18", "node": 3998, "naklon": 0 }, +{ "label": "34/19", "node": 3983, "naklon": 0 }, +{ "label": "34/20", "node": 4005, "naklon": 0 }, +{ "label": "34/21", "node": 4001, "naklon": 0 }, +{ "label": "34/22", "node": 4008, "naklon": 0 }, +{ "label": "34/23", "node": 4002, "naklon": 0 }, +{ "label": "34/24", "node": 3996, "naklon": 0 }, +{ "label": "34/25", "node": 4007, "naklon": 0 }, +{ "label": "34/26", "node": 4113, "naklon": 15 }, +{ "label": "34/27", "node": 3595, "naklon": 5 }, +{ "label": "34/28", "node": 3599, "naklon": 5 }, +{ "label": "34/29", "node": 3601, "naklon": 5 }, +{ "label": "34/30", "node": 4205, "naklon": 0 }, +{ "label": "34/31", "node": 3987, "naklon": null }, +{ "label": "34/32", "node": 4214, "naklon": 0 }, +{ "label": "34/33", "node": 4226, "naklon": 0 }, +{ "label": "34/34", "node": 4209, "naklon": 0 }, +{ "label": "34/35", "node": 4201, "naklon": 0 }, +{ "label": "34/36", "node": 3440, "naklon": 0 }, +{ "label": "34/37", "node": 3465, "naklon": 0 }, +{ "label": "34/38", "node": 3446, "naklon": 0 }, +{ "label": "34/39", "node": 4039, "naklon": 5 }, +{ "label": "34/40", "node": 4057, "naklon": 5 }, +{ "label": "34/41", "node": 3985, "naklon": 0 }, +{ "label": "34/42", "node": 3882, "naklon": 0 }, +{ "label": "34/43", "node": 4040, "naklon": 5 }, +{ "label": "34/44", "node": 3231, "naklon": 0 }, +{ "label": "34/45", "node": 3227, "naklon": 0 }, +{ "label": "34/46", "node": 3431, "naklon": 0 }, +{ "label": "34/47", "node": 4013, "naklon": 0 }, +{ "label": "34/48", "node": 4045, "naklon": 0 }, +{ "label": "34/49", "node": 3949, "naklon": 0 }, +{ "label": "34/50", "node": 3953, "naklon": 0 }, +{ "label": "34/51", "node": 3952, "naklon": 0 }, +{ "label": "34/52", "node": 3995, "naklon": 0 }, +{ "label": "34/53", "node": 3992, "naklon": 0 }, +{ "label": "34/54", "node": 4198, "naklon": 0 }, +{ "label": "34/55", "node": 3935, "naklon": 0 }, +{ "label": "34/56", "node": 4050, "naklon": 0 }, +{ "label": "34/57", "node": 4049, "naklon": 0 }, +{ "label": "34/58", "node": 4054, "naklon": 0 }, +{ "label": "34/59", "node": 3663, "naklon": 6 }, +{ "label": "34/60", "node": 3661, "naklon": 6 }, +{ "label": "34/61", "node": 3664, "naklon": 6 }, +{ "label": "34/62", "node": 3660, "naklon": 6 }, +{ "label": "34/63", "node": 3662, "naklon": 6 }, +{ "label": "34/65", "node": 3665, "naklon": 6 }, +{ "label": "34/66", "node": 3667, "naklon": 6 }, +{ "label": "34/67", "node": 3675, "naklon": 6 }, +{ "label": "34/68", "node": 3666, "naklon": 6 }, +{ "label": "34/69", "node": 3652, "naklon": 6 }, +{ "label": "34/70", "node": 3458, "naklon": 6 }, +{ "label": "34/70/1", "node": 4425, "naklon": 6 }, +{ "label": "34/71", "node": 3459, "naklon": 6 }, +{ "label": "34/72", "node": 3435, "naklon": 6 }, +{ "label": "34/73", "node": 3448, "naklon": 6 }, +{ "label": "34/74", "node": 3451, "naklon": 6 }, +{ "label": "34/75", "node": 3442, "naklon": 6 }, +{ "label": "34/76", "node": 3449, "naklon": 6 }, +{ "label": "34/77", "node": 3450, "naklon": 6 }, +{ "label": "34/78", "node": 3433, "naklon": 6 }, +{ "label": "34/80", "node": 3432, "naklon": 12 }, +{ "label": "34/81", "node": 3470, "naklon": 12 }, +{ "label": "34/82", "node": 3438, "naklon": 12 }, +{ "label": "34/83", "node": 3439, "naklon": 12 }, +{ "label": "34/84", "node": 4028, "naklon": 5 }, +{ "label": "34/85", "node": 3596, "naklon": 5 }, +{ "label": "34/86", "node": 4021, "naklon": 5 }, +{ "label": "34/87", "node": 3437, "naklon": 12 }, +{ "label": "34/88", "node": 3428, "naklon": 12 }, +{ "label": "34/89", "node": 3430, "naklon": 12 }, +{ "label": "34/91", "node": 3454, "naklon": 6 }, +{ "label": "34/92", "node": 3492, "naklon": 6 }, +{ "label": "34/93", "node": 3462, "naklon": 6 }, +{ "label": "34/94", "node": 3427, "naklon": 6 }, +{ "label": "34/95", "node": 3434, "naklon": 6 }, +{ "label": "34/96", "node": 3456, "naklon": 6 }, +{ "label": "34/98/1", "node": 4307, "naklon": 6 }, +{ "label": "35/1", "node": 3968, "naklon": 0 }, +{ "label": "35/2", "node": 3969, "naklon": 0 }, +{ "label": "35/3", "node": 3947, "naklon": 0 }, +{ "label": "35/4", "node": 3979, "naklon": 0 }, +{ "label": "35/5", "node": 3959, "naklon": 0 }, +{ "label": "35/6", "node": 3948, "naklon": 0 }, +{ "label": "35/7", "node": 3961, "naklon": 0 }, +{ "label": "35/8", "node": 3956, "naklon": 0 }, +{ "label": "35/9", "node": 3604, "naklon": 0 }, +{ "label": "35/10", "node": 3944, "naklon": 0 }, +{ "label": "35/11", "node": 3960, "naklon": 0 }, +{ "label": "35/12", "node": 3958, "naklon": 0 }, +{ "label": "35/13", "node": 4036, "naklon": 5 }, +{ "label": "35/18", "node": 3893, "naklon": 5 }, +{ "label": "35/19", "node": 4108, "naklon": 5 }, +{ "label": "35/20", "node": 4017, "naklon": 5 }, +{ "label": "35/21", "node": 3963, "naklon": 0 }, +{ "label": "35/22", "node": 3658, "naklon": 0 }, +{ "label": "35/23", "node": 3975, "naklon": 0 }, +{ "label": "35/24", "node": 3980, "naklon": 0 }, +{ "label": "35/25", "node": 4012, "naklon": 0 }, +{ "label": "35/26", "node": 3655, "naklon": 0 }, +{ "label": "35/27", "node": 3962, "naklon": 0 }, +{ "label": "35/28", "node": 3945, "naklon": 0 }, +{ "label": "35/29", "node": 3569, "naklon": 0 }, +{ "label": "35/30", "node": 3429, "naklon": 0 }, +{ "label": "35/31", "node": 3657, "naklon": 0 }, +{ "label": "35/32", "node": 3646, "naklon": 0 }, +{ "label": "35/33", "node": 3622, "naklon": 0 }, +{ "label": "35/34", "node": 3612, "naklon": 0 }, +{ "label": "35/35", "node": 3955, "naklon": 0 }, +{ "label": "35/37", "node": 3878, "naklon": 0 }, +{ "label": "35/38", "node": 3965, "naklon": 0 }, +{ "label": "35/39", "node": 3970, "naklon": 0 }, +{ "label": "35/40", "node": 3611, "naklon": 0 }, +{ "label": "35/41", "node": 2635, "naklon": 0 }, +{ "label": "35/42", "node": 3978, "naklon": 0 }, +{ "label": "35/43", "node": 4213, "naklon": 0 }, +{ "label": "35/44", "node": 3984, "naklon": 0 }, +{ "label": "35/45", "node": 4218, "naklon": 0 }, +{ "label": "35/46", "node": 3973, "naklon": 0 }, +{ "label": "35/47", "node": 3974, "naklon": 0 }, +{ "label": "35/48", "node": 3759, "naklon": 0 }, +{ "label": "35/49", "node": 3844, "naklon": 0 }, +{ "label": "35/50", "node": 3804, "naklon": 0 }, +{ "label": "35/51", "node": 3831, "naklon": 0 }, +{ "label": "35/52", "node": 3643, "naklon": 0 }, +{ "label": "35/53", "node": 3966, "naklon": 0 }, +{ "label": "35/54", "node": 3971, "naklon": 0 }, +{ "label": "35/55", "node": 3650, "naklon": 6 }, +{ "label": "35/56", "node": 3651, "naklon": 6 }, +{ "label": "35/57", "node": 3669, "naklon": 6 }, +{ "label": "35/58", "node": 3648, "naklon": 6 }, +{ "label": "35/59", "node": 3645, "naklon": 6 }, +{ "label": "35/60", "node": 3659, "naklon": 6 }, +{ "label": "35/61", "node": 3647, "naklon": 6 }, +{ "label": "35/62", "node": 3644, "naklon": 6 }, +{ "label": "35/63", "node": 3671, "naklon": 6 }, +{ "label": "35/64", "node": 3682, "naklon": 6 }, +{ "label": "35/65", "node": 3672, "naklon": 6 }, +{ "label": "35/66", "node": 3683, "naklon": 6 }, +{ "label": "35/67", "node": 3673, "naklon": 6 }, +{ "label": "35/68", "node": 3674, "naklon": 6 }, +{ "label": "35/69", "node": 3679, "naklon": 6 }, +{ "label": "35/70", "node": 3649, "naklon": 6 }, +{ "label": "35/71", "node": 3687, "naklon": 6 }, +{ "label": "35/72", "node": 3677, "naklon": 6 }, +{ "label": "35/73", "node": 3690, "naklon": 6 }, +{ "label": "35/74", "node": 3691, "naklon": 6 }, +{ "label": "35/75", "node": 3676, "naklon": 6 }, +{ "label": "35/76", "node": 3680, "naklon": 6 }, +{ "label": "35/77", "node": 3668, "naklon": 6 }, +{ "label": "35/78", "node": 3681, "naklon": 6 }, +{ "label": "35/79", "node": 3670, "naklon": 6 }, +{ "label": "35/80", "node": 3689, "naklon": 6 }, +{ "label": "35/81", "node": 3678, "naklon": 6 }, +{ "label": "35/82", "node": 3693, "naklon": 6 }, +{ "label": "35/83", "node": 3654, "naklon": null }, +{ "label": "36/1", "node": 4086, "naklon": 0 }, +{ "label": "36/2", "node": 4085, "naklon": 0 }, +{ "label": "36/3", "node": 4083, "naklon": 0 }, +{ "label": "36/4", "node": 4087, "naklon": 0 }, +{ "label": "36/5", "node": 4077, "naklon": 0 }, +{ "label": "36/6", "node": 4082, "naklon": 0 }, +{ "label": "36/7", "node": 4186, "naklon": 0 }, +{ "label": "36/8", "node": 4206, "naklon": 0 }, +{ "label": "36/9", "node": 4080, "naklon": 0 }, +{ "label": "36/10", "node": 4081, "naklon": 0 }, +{ "label": "36/11", "node": 4210, "naklon": 0 }, +{ "label": "36/12", "node": 4079, "naklon": 0 }, +{ "label": "36/13", "node": 4076, "naklon": 0 }, +{ "label": "36/14", "node": 3852, "naklon": 0 }, +{ "label": "36/15", "node": 2813, "naklon": 0 }, +{ "label": "36/16", "node": 4219, "naklon": 0 }, +{ "label": "37/1A", "node": 3104, "naklon": 15 }, +{ "label": "37/1B", "node": 3121, "naklon": 15 }, +{ "label": "37/2", "node": 3113, "naklon": 10 }, +{ "label": "37/3", "node": 3118, "naklon": 10 }, +{ "label": "37/4", "node": 3111, "naklon": 10 }, +{ "label": "37/5", "node": 4020, "naklon": 10 }, +{ "label": "37/6", "node": 3119, "naklon": 10 }, +{ "label": "37/7", "node": 3110, "naklon": 10 }, +{ "label": "37/8", "node": 3108, "naklon": 10 }, +{ "label": "37/12", "node": 3107, "naklon": 0 }, +{ "label": "37/13", "node": 3199, "naklon": 0 }, +{ "label": "37/14", "node": 3120, "naklon": 0 }, +{ "label": "37/15", "node": 3122, "naklon": 0 }, +{ "label": "37/16", "node": 3908, "naklon": 0 }, +{ "label": "37/17", "node": 3114, "naklon": 0 }, +{ "label": "37/18", "node": 3105, "naklon": 0 }, +{ "label": "37/19", "node": 3116, "naklon": 0 }, +{ "label": "37/20", "node": 3106, "naklon": 0 }, +{ "label": "37/21", "node": 2887, "naklon": 5 }, +{ "label": "37/22", "node": 2888, "naklon": 5 }, +{ "label": "37/23", "node": 2877, "naklon": 5 }, +{ "label": "37/24", "node": 2891, "naklon": 5 }, +{ "label": "37/25", "node": 2886, "naklon": 5 }, +{ "label": "37/26", "node": 2892, "naklon": 5 }, +{ "label": "37/27", "node": 2882, "naklon": 5 }, +{ "label": "37/28", "node": 2883, "naklon": 5 }, +{ "label": "37/29", "node": 2876, "naklon": 5 }, +{ "label": "37/30", "node": 3109, "naklon": 5 }, +{ "label": "37/31", "node": 4152, "naklon": 5 }, +{ "label": "37/32", "node": 3117, "naklon": 5 }, +{ "label": "37/33", "node": 2870, "naklon": 5 }, +{ "label": "37/34", "node": 2867, "naklon": 0 }, +{ "label": "37/35", "node": 2865, "naklon": 0 }, +{ "label": "37/36", "node": 3096, "naklon": 0 }, +{ "label": "37/37", "node": 2871, "naklon": 0 }, +{ "label": "37/38", "node": 2884, "naklon": 0 }, +{ "label": "37/39", "node": 2855, "naklon": 0 }, +{ "label": "37/40", "node": 2878, "naklon": 0 }, +{ "label": "37/41", "node": 2889, "naklon": 0 }, +{ "label": "37/42", "node": 2879, "naklon": 0 }, +{ "label": "37/43", "node": 2850, "naklon": 0 }, +{ "label": "37/44", "node": 2894, "naklon": 0 }, +{ "label": "37/45", "node": 2880, "naklon": 0 }, +{ "label": "37/46", "node": 2893, "naklon": 0 }, +{ "label": "37/47", "node": 2890, "naklon": 0 }, +{ "label": "37/48", "node": 4355, "naklon": 0 }, +{ "label": "37/49", "node": 2885, "naklon": 0 }, +{ "label": "37/50", "node": 2875, "naklon": 0 }, +{ "label": "38/30A", "node": 3486, "naklon": 5 }, +{ "label": "38/30B", "node": 3714, "naklon": 0 }, +{ "label": "38/32A", "node": 3483, "naklon": 5 }, +{ "label": "38/32B", "node": 3496, "naklon": 0 }, +{ "label": "38/35A", "node": 3487, "naklon": 5 }, +{ "label": "38/35B", "node": 3498, "naklon": 5 }, +{ "label": "38/38A", "node": 3578, "naklon": 5 }, +{ "label": "38/38B", "node": 3508, "naklon": 5 }, +{ "label": "38/59A", "node": 3787, "naklon": 5 }, +{ "label": "38/59B", "node": 3491, "naklon": 5 }, +{ "label": "38/1", "node": 3568, "naklon": null }, +{ "label": "38/2", "node": 3502, "naklon": 0 }, +{ "label": "38/3", "node": 3836, "naklon": 0 }, +{ "label": "38/4", "node": 3588, "naklon": 0 }, +{ "label": "38/5", "node": 3493, "naklon": 0 }, +{ "label": "38/6", "node": 3580, "naklon": 0 }, +{ "label": "38/7", "node": 3572, "naklon": 0 }, +{ "label": "38/8", "node": 3506, "naklon": 0 }, +{ "label": "38/9", "node": 3503, "naklon": 0 }, +{ "label": "38/10", "node": 3579, "naklon": 0 }, +{ "label": "38/11", "node": 3586, "naklon": 0 }, +{ "label": "38/12", "node": 3495, "naklon": 5 }, +{ "label": "38/13", "node": 3492, "naklon": 5 }, +{ "label": "38/14", "node": 3532, "naklon": 5 }, +{ "label": "38/15", "node": 3574, "naklon": 5 }, +{ "label": "38/16", "node": 4140, "naklon": 5 }, +{ "label": "38/17", "node": 3795, "naklon": 5 }, +{ "label": "38/18", "node": 3788, "naklon": 5 }, +{ "label": "38/19", "node": 3796, "naklon": 5 }, +{ "label": "38/20", "node": 3797, "naklon": 5 }, +{ "label": "38/21", "node": 3798, "naklon": 5 }, +{ "label": "38/22", "node": 3786, "naklon": 5 }, +{ "label": "38/23", "node": 4067, "naklon": 5 }, +{ "label": "38/24", "node": 3773, "naklon": 5 }, +{ "label": "38/25", "node": 3774, "naklon": 5 }, +{ "label": "38/26", "node": 3789, "naklon": 5 }, +{ "label": "38/27", "node": 3520, "naklon": null }, +{ "label": "38/29", "node": 3577, "naklon": 5 }, +{ "label": "38/31", "node": 3530, "naklon": null }, +{ "label": "38/33", "node": 3488, "naklon": 5 }, +{ "label": "38/34", "node": 3505, "naklon": 5 }, +{ "label": "38/36", "node": 3497, "naklon": 5 }, +{ "label": "38/37", "node": 3490, "naklon": 5 }, +{ "label": "38/39", "node": 3585, "naklon": 5 }, +{ "label": "38/40", "node": 3582, "naklon": 5 }, +{ "label": "38/41", "node": 3507, "naklon": 5 }, +{ "label": "38/42", "node": 3484, "naklon": 5 }, +{ "label": "38/43", "node": 3501, "naklon": 5 }, +{ "label": "38/44", "node": 3581, "naklon": 5 }, +{ "label": "38/45", "node": 4192, "naklon": 5 }, +{ "label": "38/46", "node": 3576, "naklon": 5 }, +{ "label": "38/47", "node": 3489, "naklon": 5 }, +{ "label": "38/48", "node": 3573, "naklon": 5 }, +{ "label": "38/49", "node": 3504, "naklon": 5 }, +{ "label": "38/50", "node": 3794, "naklon": 5 }, +{ "label": "38/51", "node": 3485, "naklon": 5 }, +{ "label": "38/52", "node": 3482, "naklon": 5 }, +{ "label": "38/53", "node": 3575, "naklon": 5 }, +{ "label": "38/54", "node": 3499, "naklon": 5 }, +{ "label": "38/55", "node": 3494, "naklon": 5 }, +{ "label": "38/56", "node": 3583, "naklon": 5 }, +{ "label": "38/57", "node": 3570, "naklon": 5 }, +{ "label": "38/58", "node": 3500, "naklon": 0 }, +{ "label": "38/63", "node": 3510, "naklon": null }, +{ "label": "38/64", "node": 3512, "naklon": null }, +{ "label": "38/67", "node": 3535, "naklon": null }, +{ "label": "38/68", "node": 3531, "naklon": null }, +{ "label": "38/69", "node": 3537, "naklon": null }, +{ "label": "39/1", "node": 3522, "naklon": 10 }, +{ "label": "39/2", "node": 4018, "naklon": 10 }, +{ "label": "39/3", "node": 4019, "naklon": 10 }, +{ "label": "39/4", "node": 4154, "naklon": 5 }, +{ "label": "39/5", "node": 3907, "naklon": 5 }, +{ "label": "39/6", "node": 4148, "naklon": 0 }, +{ "label": "39/7", "node": 4153, "naklon": 0 }, +{ "label": "39/8", "node": 3938, "naklon": 0 }, +{ "label": "39/9", "node": 3802, "naklon": 0 }, +{ "label": "39/10", "node": 4015, "naklon": 0 }, +{ "label": "39/11", "node": 3929, "naklon": 0 }, +{ "label": "39/12", "node": 3946, "naklon": 0 }, +{ "label": "39/13", "node": 4014, "naklon": 0 }, +{ "label": "39/14", "node": 4155, "naklon": 0 }, +{ "label": "39/15", "node": 4149, "naklon": 0 }, +{ "label": "39/16", "node": 3642, "naklon": 0 }, +{ "label": "39/17", "node": 3636, "naklon": 5 }, +{ "label": "39/18", "node": 3991, "naklon": 0 }, +{ "label": "39/19", "node": 3994, "naklon": 0 }, +{ "label": "39/20", "node": 3990, "naklon": 0 }, +{ "label": "39/21", "node": 3967, "naklon": 0 }, +{ "label": "39/22", "node": 3977, "naklon": 0 }, +{ "label": "39/23", "node": 3757, "naklon": 15 }, +{ "label": "39/24", "node": 3633, "naklon": 15 }, +{ "label": "39/25", "node": 3744, "naklon": 15 }, +{ "label": "39/26", "node": 4023, "naklon": 5 }, +{ "label": "39/27", "node": 3720, "naklon": 5 }, +{ "label": "39/28", "node": 3734, "naklon": 5 }, +{ "label": "39/29", "node": 3741, "naklon": 5 }, +{ "label": "39/30", "node": 3721, "naklon": 5 }, +{ "label": "39/31", "node": 3845, "naklon": 0 }, +{ "label": "39/32", "node": 3840, "naklon": 0 }, +{ "label": "39/33", "node": 3837, "naklon": null }, +{ "label": "39/34", "node": 3839, "naklon": 0 }, +{ "label": "41/1", "node": 3542, "naklon": null }, +{ "label": "41/2", "node": 3566, "naklon": null }, +{ "label": "41/3", "node": 3548, "naklon": null }, +{ "label": "41/4", "node": 3515, "naklon": null }, +{ "label": "41/5", "node": 3559, "naklon": null }, +{ "label": "41/6", "node": 3509, "naklon": null }, +{ "label": "41/7", "node": 3524, "naklon": null }, +{ "label": "41/8", "node": 3518, "naklon": null }, +{ "label": "41/9", "node": 3182, "naklon": null }, +{ "label": "41/10", "node": 3187, "naklon": null }, +{ "label": "41/11", "node": 3195, "naklon": 0 }, +{ "label": "41/12", "node": 3857, "naklon": 0 }, +{ "label": "41/13", "node": 3183, "naklon": 0 }, +{ "label": "41/14", "node": 3177, "naklon": 0 }, +{ "label": "41/15", "node": 3189, "naklon": 0 }, +{ "label": "41/16", "node": 3186, "naklon": 0 }, +{ "label": "42/1", "node": 3178, "naklon": 0 }, +{ "label": "42/2", "node": 3219, "naklon": 0 }, +{ "label": "42/3", "node": 3475, "naklon": 0 }, +{ "label": "42/4", "node": 3175, "naklon": 0 }, +{ "label": "42/5", "node": 3222, "naklon": 0 }, +{ "label": "42/6", "node": 3221, "naklon": 0 }, +{ "label": "42/7", "node": 3181, "naklon": 0 }, +{ "label": "42/8", "node": 3180, "naklon": 0 }, +{ "label": "42/9", "node": 3218, "naklon": 0 }, +{ "label": "42/10", "node": 3176, "naklon": 0 }, +{ "label": "42/11", "node": 3174, "naklon": 0 }, +{ "label": "42/12", "node": 3220, "naklon": 0 }, +{ "label": "42/13", "node": 3533, "naklon": null }, +{ "label": "42/14", "node": 3543, "naklon": null }, +{ "label": "42/15", "node": 3554, "naklon": null }, +{ "label": "42/16", "node": 3549, "naklon": null }, +{ "label": "43/1", "node": 2857, "naklon": 5 }, +{ "label": "43/2", "node": 2752, "naklon": 5 }, +{ "label": "43/3", "node": 2753, "naklon": 5 }, +{ "label": "43/4", "node": 3024, "naklon": 5 }, +{ "label": "43/5", "node": 2754, "naklon": 5 }, +{ "label": "43/6", "node": 4423, "naklon": 5 }, +{ "label": "43/7", "node": 4368, "naklon": 5 }, +{ "label": "43/8", "node": 2978, "naklon": 5 }, +{ "label": "43/9", "node": 2868, "naklon": 5 }, +{ "label": "43/10", "node": 4377, "naklon": 5 }, +{ "label": "43/11", "node": 2757, "naklon": 5 }, +{ "label": "43/12", "node": 2970, "naklon": 5 }, +{ "label": "43/13", "node": 2758, "naklon": 5 }, +{ "label": "43/14", "node": 4378, "naklon": 5 }, +{ "label": "43/15", "node": 2759, "naklon": 5 }, +{ "label": "43/16", "node": 4342, "naklon": 5 }, +{ "label": "43/17", "node": 3026, "naklon": null }, +{ "label": "43/18", "node": 2986, "naklon": null }, +{ "label": "43/19", "node": 4343, "naklon": null }, +{ "label": "43/20", "node": 2760, "naklon": null }, +{ "label": "43/21", "node": 4379, "naklon": null }, +{ "label": "43/22", "node": 2971, "naklon": null }, +{ "label": "43/23", "node": 2761, "naklon": null }, +{ "label": "43/24", "node": 2762, "naklon": null }, +{ "label": "43/25", "node": 4339, "naklon": null }, +{ "label": "43/26", "node": 4380, "naklon": null }, +{ "label": "43/27", "node": 2975, "naklon": null }, +{ "label": "43/28", "node": 3003, "naklon": null }, +{ "label": "43/29", "node": 4338, "naklon": null }, +{ "label": "43/30", "node": 3002, "naklon": null }, +{ "label": "43/31", "node": 4345, "naklon": null }, +{ "label": "43/32", "node": 2979, "naklon": null }, +{ "label": "43/33", "node": 2989, "naklon": null }, +{ "label": "43/34", "node": 2996, "naklon": null }, +{ "label": "43/35", "node": 2972, "naklon": null }, +{ "label": "43/36", "node": 2988, "naklon": null }, +{ "label": "43/37", "node": 4344, "naklon": null }, +{ "label": "43/38", "node": 3022, "naklon": null }, +{ "label": "43/39", "node": 4341, "naklon": null }, +{ "label": "43/40", "node": 2983, "naklon": null }, +{ "label": "43/41", "node": 2995, "naklon": null }, +{ "label": "43/42", "node": 2763, "naklon": null }, +{ "label": "43/43", "node": 2764, "naklon": null }, +{ "label": "43/44", "node": 2765, "naklon": null }, +{ "label": "43/45", "node": 2766, "naklon": null }, +{ "label": "43/46", "node": 2767, "naklon": null }, +{ "label": "43/47", "node": 2768, "naklon": null }, +{ "label": "43/48", "node": 3018, "naklon": null }, +{ "label": "43/49", "node": 2982, "naklon": null }, +{ "label": "43/50", "node": 3019, "naklon": 0 }, +{ "label": "43/51", "node": 3013, "naklon": null }, +{ "label": "43/52", "node": 3007, "naklon": null }, +{ "label": "43/53", "node": 2980, "naklon": null }, +{ "label": "43/54", "node": 3011, "naklon": null }, +{ "label": "43/55", "node": 2672, "naklon": null }, +{ "label": "43/56", "node": 2673, "naklon": null }, +{ "label": "43/57", "node": 2674, "naklon": null }, +{ "label": "43/58", "node": 2675, "naklon": null }, +{ "label": "43/59", "node": 2676, "naklon": null }, +{ "label": "43/60", "node": 2677, "naklon": null }, +{ "label": "43/61", "node": 2678, "naklon": null }, +{ "label": "43/62", "node": 2679, "naklon": null }, +{ "label": "43/63", "node": 2680, "naklon": null }, +{ "label": "43/64", "node": 2840, "naklon": null }, +{ "label": "43/65", "node": 2682, "naklon": null }, +{ "label": "43/66", "node": 2683, "naklon": null }, +{ "label": "43/67", "node": 2684, "naklon": 0 }, +{ "label": "43/68", "node": 2685, "naklon": null }, +{ "label": "43/69", "node": 2686, "naklon": null }, +{ "label": "43/70", "node": 2687, "naklon": null }, +{ "label": "43/71", "node": 2688, "naklon": null }, +{ "label": "43/72", "node": 2689, "naklon": null }, +{ "label": "43/73", "node": 2690, "naklon": null }, +{ "label": "43/74", "node": 2691, "naklon": null }, +{ "label": "43/75", "node": 2692, "naklon": null }, +{ "label": "43/76", "node": 2693, "naklon": 0 }, +{ "label": "43/77", "node": 2694, "naklon": null }, +{ "label": "43/78", "node": 2695, "naklon": null }, +{ "label": "43/79", "node": 2696, "naklon": 0 }, +{ "label": "43/80", "node": 2697, "naklon": null }, +{ "label": "43/81", "node": 2698, "naklon": 0 }, +{ "label": "43/82", "node": 2699, "naklon": null }, +{ "label": "43/83", "node": 2700, "naklon": 0 }, +{ "label": "43/84", "node": 2744, "naklon": null }, +{ "label": "43/85", "node": 2745, "naklon": 0 }, +{ "label": "43/86", "node": 2746, "naklon": null }, +{ "label": "43/87", "node": 2747, "naklon": 0 }, +{ "label": "43/88", "node": 2748, "naklon": null }, +{ "label": "43/89", "node": 2749, "naklon": 0 }, +{ "label": "43/90", "node": 2841, "naklon": null }, +{ "label": "45/1", "node": 4237, "naklon": 5 }, +{ "label": "45/2", "node": 4226, "naklon": 5 }, +{ "label": "45/3", "node": 4242, "naklon": 5 }, +{ "label": "45/4", "node": 4179, "naklon": 5 }, +{ "label": "45/5", "node": 4247, "naklon": 5 }, +{ "label": "45/6", "node": 4245, "naklon": 5 }, +{ "label": "45/7", "node": 3872, "naklon": 5 }, +{ "label": "45/8", "node": 4175, "naklon": 5 }, +{ "label": "45/9", "node": 4236, "naklon": 5 }, +{ "label": "45/10", "node": 4170, "naklon": 5 }, +{ "label": "45/11", "node": 4238, "naklon": 5 }, +{ "label": "45/12", "node": 4246, "naklon": 5 }, +{ "label": "45/13", "node": 4166, "naklon": 5 }, +{ "label": "45/14", "node": 4227, "naklon": 5 }, +{ "label": "45/15", "node": 4171, "naklon": 5 }, +{ "label": "45/16", "node": 4180, "naklon": 5 }, +{ "label": "45/17", "node": 4244, "naklon": 5 }, +{ "label": "45/18", "node": 4199, "naklon": 5 }, +{ "label": "45/19", "node": 4239, "naklon": 5 }, +{ "label": "45/20", "node": 4207, "naklon": 5 }, +{ "label": "45/21", "node": 4223, "naklon": 5 }, +{ "label": "45/22", "node": 4230, "naklon": 5 }, +{ "label": "45/23", "node": 4233, "naklon": 5 }, +{ "label": "45/24", "node": 4222, "naklon": 5 }, +{ "label": "45/25", "node": 4217, "naklon": 5 }, +{ "label": "45/26", "node": 4243, "naklon": 5 }, +{ "label": "45/27", "node": 4200, "naklon": 5 }, +{ "label": "45/28", "node": 4228, "naklon": 5 }, +{ "label": "45/29", "node": 4211, "naklon": 5 }, +{ "label": "45/30", "node": 4234, "naklon": 5 }, +{ "label": "45/31", "node": 4235, "naklon": 5 }, +{ "label": "45/32", "node": 4203, "naklon": 5 }, +{ "label": "45/33", "node": 4240, "naklon": 5 }, +{ "label": "45/34", "node": 4224, "naklon": 5 }, +{ "label": "45/35", "node": 4232, "naklon": 5 }, +{ "label": "45/36", "node": 4231, "naklon": 5 }, +{ "label": "45/37", "node": 4161, "naklon": 5 }, +{ "label": "45/38", "node": 4225, "naklon": 5 }, +{ "label": "45/39", "node": 4241, "naklon": 5 }, +{ "label": "45/40", "node": 4229, "naklon": 5 }, +{ "label": "45/41", "node": 4221, "naklon": 5 }, +{ "label": "45/42", "node": 4212, "naklon": 5 }, +{ "label": "45/43", "node": 4216, "naklon": 5 }, +{ "label": "45/44", "node": 3864, "naklon": null }, +{ "label": "46/1", "node": 3806, "naklon": 5 }, +{ "label": "46/2", "node": 3813, "naklon": 5 }, +{ "label": "46/3", "node": 3815, "naklon": 5 }, +{ "label": "46/4", "node": 3807, "naklon": 5 }, +{ "label": "46/5", "node": 3895, "naklon": 5 }, +{ "label": "46/6", "node": 3809, "naklon": 5 }, +{ "label": "46/7", "node": 3820, "naklon": 5 }, +{ "label": "46/8", "node": 3805, "naklon": 5 }, +{ "label": "46/9", "node": 3894, "naklon": 0 }, +{ "label": "46/10", "node": 4055, "naklon": 0 }, +{ "label": "46/11", "node": 3812, "naklon": 5 }, +{ "label": "46/12", "node": 3822, "naklon": 5 }, +{ "label": "46/13", "node": 3821, "naklon": 5 }, +{ "label": "46/14", "node": 3808, "naklon": 5 }, +{ "label": "46/15", "node": 3799, "naklon": 5 }, +{ "label": "46/16", "node": 3816, "naklon": 5 }, +{ "label": "46/17", "node": 3751, "naklon": 5 }, +{ "label": "46/18", "node": 3814, "naklon": 5 }, +{ "label": "46/19", "node": 4044, "naklon": 0 }, +{ "label": "46/20", "node": 3931, "naklon": 0 }, +{ "label": "46/21", "node": 3902, "naklon": 5 }, +{ "label": "46/22", "node": 3916, "naklon": 5 }, +{ "label": "46/23", "node": 3892, "naklon": 5 }, +{ "label": "46/24", "node": 3914, "naklon": 5 }, +{ "label": "46/25", "node": 3898, "naklon": 5 }, +{ "label": "46/26", "node": 3897, "naklon": 5 }, +{ "label": "46/27", "node": 3885, "naklon": 5 }, +{ "label": "46/28", "node": 3906, "naklon": 5 }, +{ "label": "46/29", "node": 3930, "naklon": 0 }, +{ "label": "46/30", "node": 3936, "naklon": 0 }, +{ "label": "46/31", "node": 3860, "naklon": 5 }, +{ "label": "46/32", "node": 3841, "naklon": 5 }, +{ "label": "46/33", "node": 3873, "naklon": 5 }, +{ "label": "46/34", "node": 3854, "naklon": 5 }, +{ "label": "46/35", "node": 3939, "naklon": 5 }, +{ "label": "46/36", "node": 3858, "naklon": 5 }, +{ "label": "46/37", "node": 3940, "naklon": 5 }, +{ "label": "46/38", "node": 4035, "naklon": 5 }, +{ "label": "47/1", "node": 3373, "naklon": 0 }, +{ "label": "47/2", "node": 3374, "naklon": 0 }, +{ "label": "47/3", "node": 3384, "naklon": 0 }, +{ "label": "47/4", "node": 3385, "naklon": 0 }, +{ "label": "48/1", "node": 4051, "naklon": 5 }, +{ "label": "48/2", "node": 4041, "naklon": 5 }, +{ "label": "48/3", "node": 4056, "naklon": 5 }, +{ "label": "48/4", "node": 3875, "naklon": 5 }, +{ "label": "48/5", "node": 4061, "naklon": 5 }, +{ "label": "48/6", "node": 4047, "naklon": 5 }, +{ "label": "48/7", "node": 4060, "naklon": 5 }, +{ "label": "48/8", "node": 4059, "naklon": 5 }, +{ "label": "48/9", "node": 4058, "naklon": 5 }, +{ "label": "48/10", "node": 3635, "naklon": 5 }, +{ "label": "48/11", "node": 3640, "naklon": 5 }, +{ "label": "48/12", "node": 3747, "naklon": 5 }, +{ "label": "48/13", "node": 3629, "naklon": 5 }, +{ "label": "48/14", "node": 4048, "naklon": 5 }, +{ "label": "48/15", "node": 4063, "naklon": 5 }, +{ "label": "48/16", "node": 4062, "naklon": 5 }, +{ "label": "48/17", "node": 4043, "naklon": 5 }, +{ "label": "48/18", "node": 4052, "naklon": 5 }, +{ "label": "48/19", "node": 4042, "naklon": 5 }, +{ "label": "48/20", "node": 4053, "naklon": 5 }, +{ "label": "50/15A", "node": 4167, "naklon": null }, +{ "label": "50/15B", "node": 4159, "naklon": null }, +{ "label": "50/1", "node": 4183, "naklon": 0 }, +{ "label": "50/2", "node": 4157, "naklon": 0 }, +{ "label": "50/3", "node": 4160, "naklon": 0 }, +{ "label": "50/4", "node": 4173, "naklon": 0 }, +{ "label": "50/5", "node": 4177, "naklon": 0 }, +{ "label": "50/6", "node": 4174, "naklon": 0 }, +{ "label": "50/7", "node": 4178, "naklon": 0 }, +{ "label": "50/8", "node": 4195, "naklon": 0 }, +{ "label": "50/9", "node": 4169, "naklon": 0 }, +{ "label": "50/10", "node": 4162, "naklon": 0 }, +{ "label": "50/11", "node": 4158, "naklon": 0 }, +{ "label": "50/12", "node": 4182, "naklon": 0 }, +{ "label": "50/13", "node": 4194, "naklon": 0 }, +{ "label": "50/14", "node": 4184, "naklon": 0 }, +{ "label": "50/16", "node": 4197, "naklon": null }, +{ "label": "50/17", "node": 4164, "naklon": null }, +{ "label": "50/18", "node": 3306, "naklon": null }, +{ "label": "50/19", "node": 4168, "naklon": null }, +{ "label": "50/20", "node": 4193, "naklon": null }, +{ "label": "50/21", "node": 4187, "naklon": null }, +{ "label": "50/22", "node": 4163, "naklon": null }, +{ "label": "50/23", "node": 4165, "naklon": null }, +{ "label": "50/24", "node": 4185, "naklon": null }, +{ "label": "50/25", "node": 3904, "naklon": null }, +{ "label": "53/1", "node": 2619, "naklon": 0 }, +{ "label": "53/2", "node": 2623, "naklon": 0 }, +{ "label": "53/3", "node": 2617, "naklon": 0 }, +{ "label": "53/4", "node": 2622, "naklon": 0 }, +{ "label": "53/5", "node": 2618, "naklon": 0 }, +{ "label": "53/6", "node": 2626, "naklon": 0 }, +{ "label": "53/7", "node": 2869, "naklon": 0 }, +{ "label": "53/8", "node": 2613, "naklon": 0 }, +{ "label": "53/9", "node": 2608, "naklon": 0 }, +{ "label": "53/10", "node": 2611, "naklon": 0 }, +{ "label": "53/11", "node": 2598, "naklon": 0 }, +{ "label": "53/12", "node": 3528, "naklon": 0 }, +{ "label": "53/13", "node": 3545, "naklon": 0 }, +{ "label": "53/14", "node": 2620, "naklon": 0 }, +{ "label": "53/15", "node": 2612, "naklon": 0 }, +{ "label": "53/16", "node": 2615, "naklon": 0 }, +{ "label": "53/17", "node": 3722, "naklon": 0 }, +{ "label": "53/18", "node": 2624, "naklon": 0 }, +{ "label": "53/19", "node": 2609, "naklon": 0 }, +{ "label": "53/20", "node": 2625, "naklon": 0 }, +{ "label": "53/21", "node": 2602, "naklon": 0 }, +{ "label": "53/22", "node": 2610, "naklon": 0 }, +{ "label": "53/23", "node": 2599, "naklon": 0 }, +{ "label": "53/24", "node": 2605, "naklon": 0 }, +{ "label": "53/25", "node": 2593, "naklon": 0 }, +{ "label": "53/26", "node": 3074, "naklon": 0 }, +{ "label": "53/27", "node": 2740, "naklon": 0 }, +{ "label": "53/28", "node": 2614, "naklon": 0 }, +{ "label": "53/29", "node": 2601, "naklon": 0 }, +{ "label": "53/30", "node": 4196, "naklon": 0 }, +{ "label": "53/31", "node": 2607, "naklon": 0 }, +{ "label": "53/32", "node": 2582, "naklon": 0 }, +{ "label": "53/33", "node": 2594, "naklon": 0 }, +{ "label": "53/34", "node": 2589, "naklon": 0 }, +{ "label": "53/35", "node": 2596, "naklon": 0 }, +{ "label": "53/36", "node": 2603, "naklon": 0 }, +{ "label": "53/37", "node": 2597, "naklon": 0 }, +{ "label": "53/38", "node": 2590, "naklon": 0 }, +{ "label": "53/39", "node": 2581, "naklon": 0 }, +{ "label": "53/40", "node": 2583, "naklon": 0 }, +{ "label": "53/41", "node": 2580, "naklon": 0 }, +{ "label": "53/42", "node": 2579, "naklon": 0 }, +{ "label": "53/43", "node": 2797, "naklon": 0 }, +{ "label": "53/44", "node": 2577, "naklon": 0 }, +{ "label": "53/45", "node": 2586, "naklon": 0 }, +{ "label": "53/46", "node": 2588, "naklon": 0 }, +{ "label": "53/47", "node": 2591, "naklon": 0 }, +{ "label": "53/48", "node": 2592, "naklon": 0 }, +{ "label": "53/49", "node": 2573, "naklon": 0 }, +{ "label": "53/50", "node": 2595, "naklon": 0 }, +{ "label": "53/51", "node": 2587, "naklon": 0 }, +{ "label": "53/52", "node": 2578, "naklon": 0 }, +{ "label": "53/53", "node": 2574, "naklon": 0 }, +{ "label": "53/54", "node": 2571, "naklon": 0 }, +{ "label": "53/55", "node": 2570, "naklon": 0 }, +{ "label": "53/56", "node": 2576, "naklon": 0 }, +{ "label": "53/57", "node": 2881, "naklon": 0 }, +{ "label": "53/58", "node": 2575, "naklon": 0 }, +{ "label": "53/59", "node": 2572, "naklon": 0 }, +{ "label": "53/60", "node": 2569, "naklon": 0 }, +{ "label": "55/1", "node": 4324, "naklon": 0 }, +{ "label": "55/2", "node": 4319, "naklon": 0 }, +{ "label": "55/3", "node": 4327, "naklon": 0 }, +{ "label": "55/4", "node": 4333, "naklon": 0 }, +{ "label": "55/5", "node": 4332, "naklon": 0 }, +{ "label": "55/6", "node": 4320, "naklon": 0 }, +{ "label": "55/7", "node": 4317, "naklon": 0 }, +{ "label": "55/8", "node": 4318, "naklon": 0 }, +{ "label": "55/9", "node": 4328, "naklon": 0 }, +{ "label": "55/10", "node": 4323, "naklon": 0 }, +{ "label": "55/11", "node": 4325, "naklon": 0 }, +{ "label": "55/12", "node": 4326, "naklon": 0 }, +{ "label": "55/13", "node": 4316, "naklon": 0 }, +{ "label": "55/14", "node": 4322, "naklon": 0 }, +{ "label": "55/15", "node": 4331, "naklon": 0 }, +{ "label": "55/16", "node": 4321, "naklon": 0 }, +{ "label": "55/17", "node": 4315, "naklon": 0 }, +{ "label": "55/18", "node": 4329, "naklon": 0 }, +{ "label": "55/19", "node": 4334, "naklon": 0 }, +{ "label": "55/20", "node": 4330, "naklon": 0 } +]; + +module.exports = { naklony }; diff --git a/databases/settings.table b/databases/settings.table index 0aec6b3..5abd2b1 100644 --- a/databases/settings.table +++ b/databases/settings.table @@ -1,2 +1,2 @@ -rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string -+|rvo_senica_22_ip10.0.0.109|en|28.F46E9D0E0000|48.70826502|17.28455203|192.168.252.1|rvo_senica_22_ip10.0.0.109|9excvr7yBcF3gl3kYZGY|1883|0|48|unipi|ttyUSB0|1|20|5|6|3|u109|........................................... +rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string|has_main_switch:boolean ++|rvo_senica_22_ip10.0.0.109|en|28.F46E9D0E0000|48.70826502|17.28455203|192.168.252.1|rvo_senica_22_ip10.0.0.109|9excvr7yBcF3gl3kYZGY|1883|0|48|unipi|ttyUSB0|1|20|5|6|3|u109|0|........................................... diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 0e96693..8c7543d 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -44,6 +44,9 @@ exports.install = function(instance) { const process = require('process'); const { errLogger, logger, monitor } = require('./helper/logger'); + //for accelerometer purposes + const { naklony } = require("../databases/accelerometer_db"); + const dbNodes = TABLE("nodes"); const dbRelays = TABLE("relays"); @@ -111,7 +114,7 @@ exports.install = function(instance) { priorities["77"] = minutes; priorities["78"] = minutes; priorities["79"] = minutes; - priorities["84"] = minutes; + //priorities["84"] = minutes; minutes = 10; priorities["87"] = minutes; @@ -122,7 +125,7 @@ exports.install = function(instance) { priorities["89"] = minutes; //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) - let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 84, 87, 89]; + let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; const errorHandler = new ErrorToServiceHandler(); @@ -143,6 +146,10 @@ exports.install = function(instance) { //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice const nodeProfileSendFail = new Set(); + //we expect to get current temperature in Senica from senica-prod01 + let temperatureInSenica = null; + let accelerometerInterval = null; + //END OF VARIABLE SETTINGS //-------------------------------- @@ -184,6 +191,7 @@ exports.install = function(instance) { setCorrectPlcTimeOnceADay(); sendNodeReadout = setInterval(sendNodesData, 150000); + accelerometerInterval = setInterval(accelerometerData, 60000 * 30); //30 min } @@ -763,6 +771,10 @@ exports.install = function(instance) { let tbname = nodesData[node].tbname; let nodeStatus = nodesData[node].status; + + //in case we have reported offline node status, we return (continue with next node) + if (nodeStatus === "OFFLINE") return; + nodesData[node].node_status_before_offline = nodeStatus === true ? true : false; nodesData[node].status = "OFFLINE"; nodesData[node].readout = {}; @@ -1666,7 +1678,7 @@ exports.install = function(instance) { } }).catch(function(reason) { - console.log("writeData catch exception", reason); + //console.log("writeData catch exception", reason); instance.send(SEND_TO.debug, reason); terminalCommandResponse(params, "FAILURE", null, reason); @@ -1904,6 +1916,7 @@ exports.install = function(instance) { clearInterval(customTasksInterval); clearInterval(setCorrectTime); clearInterval(sendNodeReadout); + clearInterval(accelerometerInterval); rsPort.close(); }); @@ -2071,7 +2084,16 @@ exports.install = function(instance) { if (flowdata.data.hasOwnProperty("topic")) { let data = getNested(flowdata.data, "content", "data"); - if (data == undefined) { + + //if we get temperature in senica from senica-prod01 + let temperature = getNested(flowdata.data, "content", "senica_temperature"); + + if (temperature !== undefined) { + temperatureInSenica = temperature; + return; + } + + if (data === undefined) { console.log("Invalid rpc command came from platform"); return; } @@ -2682,49 +2704,21 @@ exports.install = function(instance) { values["time_schedule_settings"] = time_schedule_settings; } - //naklon + //naklon - nateraz sa z nodu nevycitava! kvoli problemom s accelerometrom a vracanymi hodnotami, posielame temp a x y z vo funkcii accelerometerData() if (register == 84) { - let temp; - if (byte3 >= 128) { - temp = (byte3 - 128) * (-1); - } - else { - temp = byte3; - } + const temp = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; + const inclination_x = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; + const inclination_y = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; + const inclination_z = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; - let inclination_x; - if (byte2 >= 128) { - inclination_x = (byte2 - 128) * (-1); - } - else { - inclination_x = byte2; - } - - let inclination_y; - if (byte1 >= 128) { - inclination_y = (byte1 - 128) * (-1); - } - else { - inclination_y = byte1; - } - - let inclination_z; - if (byte0 >= 128) { - inclination_z = (byte0 - 128) * (-1); - } - else { - inclination_z = byte0; - } + if (temp === undefined) temp = 999; + if (inclination_x === undefined) inclination_x = 999; + if (inclination_y === undefined) inclination_y = 999; + if (inclination_z === undefined) inclination_z = 999; values["temperature"] = temp; - - //náklon x values["inclination_x"] = inclination_x; - - //náklon y values["inclination_y"] = inclination_y; - - //náklon z values["inclination_z"] = inclination_z; } @@ -2850,5 +2844,44 @@ exports.install = function(instance) { return (typeof item === "object" && !Array.isArray(item) && item !== null); } + + // we fake data, that should be received from accelerometer, as they are a bit unreliable. (temperature, x,y,z) + function accelerometerData() { + + if (temperatureInSenica === null) return; + + for (const key in relaysData) { + + const lineData = relaysData[key]; + const lineNumber = lineData.line; + const contactor = lineData.contactor; + + if (lineNumber === 0) continue; + + if (contactor === 1) { + + let date = Date.now(); + + Object.keys(nodesData).forEach((node, index) => { + + setTimeout(function() { + + if (nodesData[node].line === lineNumber) { + + let x = null; + if (naklony.hasOwnProperty(node)) x = naklony[node].naklon; + if (x === null) x = 0; + + sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData[node].tbname, date); + } + + }, (index + 1) * 500); + }) + + } + } + } + + } // end of instance.export diff --git a/flow/db_init.js b/flow/db_init.js index e809037..5b3d589 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -65,7 +65,7 @@ exports.install = async function(instance) { Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {}) dbs.settings = { - edge_fw_version: "2025-01-30", //rok-mesiac-den + edge_fw_version: "2025-04-24", //rok-mesiac-den language: responseSettings[0]["lang"], rvo_name: responseSettings[0]["rvo_name"], project_id: responseSettings[0]["project_id"], @@ -86,6 +86,7 @@ exports.install = async function(instance) { mqtt_port: responseSettings[0]["mqtt_port"], phases: responseSettings[0]["phases"], cloud_topic: responseSettings[0]["cloud_topic"], + has_main_switch: responseSettings[0]["has_main_switch"], //dynamic values masterNodeIsResponding: true, //cmd_manager diff --git a/flow/designer.json b/flow/designer.json index fbdda76..b51e1ca 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -36,13 +36,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#DA4453", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#DA4453", + "notes": "" }, { "id": "1612776786008", @@ -90,17 +90,17 @@ "output": [] }, "state": { - "text": "Reconnecting", - "color": "yellow" + "text": "Connected", + "color": "green" }, - "color": "#888600", - "notes": "", "options": { "username": "", "clientid": "", "port": "1883", "host": "" - } + }, + "color": "#888600", + "notes": "" }, { "id": "1612778461252", @@ -133,11 +133,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1612783322136", @@ -157,13 +157,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615551060773", @@ -183,21 +183,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#DA4453", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#DA4453", + "notes": "" }, { "id": "1615563373927", "component": "debug", "tab": "1615551125555", "name": "Debug", - "x": 753, - "y": 150, + "x": 755, + "y": 155, "connections": {}, "disabledio": { "input": [ @@ -209,21 +209,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#DA4453", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#DA4453", + "notes": "" }, { "id": "1615566865233", "component": "virtualwireout", "tab": "1615551125555", "name": "tb-push", - "x": 761, - "y": 251, + "x": 755, + "y": 248, "connections": {}, "disabledio": { "input": [], @@ -233,19 +233,19 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1615798582262", "component": "debug", "tab": "1615551125555", "name": "CMD_debug", - "x": 765, - "y": 350, + "x": 755, + "y": 346, "connections": {}, "disabledio": { "input": [ @@ -257,13 +257,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615802995322", @@ -281,13 +281,13 @@ "text": "Disabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": false - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615809128443", @@ -305,13 +305,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615809595184", @@ -329,19 +329,19 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1616165795916", "component": "httproute", "tab": "1615551125555", "name": "POST /terminal", - "x": 114, - "y": 546, + "x": 135, + "y": 547, "connections": { "0": [ { @@ -362,9 +362,6 @@ "text": "Listening", "color": "green" }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, "options": { "timeout": 10, "cachepolicy": 0, @@ -379,15 +376,18 @@ 10000 ], "emptyresponse": false - } + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false }, { "id": "1616165824813", "component": "httpresponse", "tab": "1615551125555", "name": "HTTP Response", - "x": 772, - "y": 443, + "x": 753, + "y": 423, "connections": {}, "disabledio": { "input": [], @@ -397,11 +397,11 @@ "text": "", "color": "gray" }, - "color": "#5D9CEC", - "notes": "", "options": { "datatype": "json" - } + }, + "color": "#5D9CEC", + "notes": "" }, { "id": "1617104731852", @@ -421,21 +421,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1617114651703", "component": "trigger", "tab": "1615551125555", "name": "turnOff line", - "x": 88, - "y": 1158, + "x": 133, + "y": 1161, "connections": { "0": [ { @@ -452,12 +452,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "datatype": "object", - "data": "{line: 3, command: \"turnOff\", force: true}" - } + "data": "{line: 3, command: \"turnOff\", force: true}", + "datatype": "object" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1617115013095", @@ -475,11 +475,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1617284749681", @@ -487,7 +487,7 @@ "tab": "1615551125555", "name": "update profile / node", "x": 112, - "y": 215, + "y": 208, "connections": { "0": [ { @@ -504,20 +504,20 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "data": "profile_nodes", - "datatype": "string" - } + "datatype": "string", + "data": "profile_nodes" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1618235171399", "component": "trigger", "tab": "1615551125555", "name": "tun tasks", - "x": 119, - "y": 280, + "x": 184, + "y": 279, "connections": { "0": [ { @@ -534,11 +534,11 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { "data": "run" - } + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1618300858252", @@ -556,13 +556,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1618300863816", @@ -582,13 +582,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1618393583970", @@ -606,19 +606,19 @@ "text": "from-dido-controller", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "from-dido-controller" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618393674428", "component": "virtualwirein", "tab": "1615551125555", "name": "platform-rpc-call", - "x": 119.88333129882812, - "y": 369, + "x": 132.88333129882812, + "y": 367, "connections": { "0": [ { @@ -635,18 +635,18 @@ "text": "platform-rpc-call", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "platform-rpc-call" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618393759854", "component": "virtualwirein", "tab": "1615551125555", "name": "cmd_to_dido", - "x": 93.88333129882812, + "x": 119.88333129882812, "y": 1007, "connections": { "0": [ @@ -668,19 +668,19 @@ "text": "cmd_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "cmd_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618393827655", "component": "virtualwireout", "tab": "1615551125555", "name": "cmd_to_dido", - "x": 779.8833312988281, - "y": 552, + "x": 752.8833312988281, + "y": 527, "connections": {}, "disabledio": { "input": [], @@ -690,11 +690,11 @@ "text": "cmd_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "cmd_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618558465485", @@ -712,18 +712,18 @@ "text": "platform-rpc-call", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "platform-rpc-call" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618572059773", "component": "trigger", "tab": "1615551125555", "name": "turnOn line", - "x": 89, + "x": 132, "y": 1085, "connections": { "0": [ @@ -741,12 +741,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "data": "{line: 1, command: \"turnOn\", force: true}", - "datatype": "object" - } + "datatype": "object", + "data": "{line: 1, command: \"turnOn\", force: true}" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1619515097737", @@ -799,17 +799,17 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#5D9CEC", - "notes": "", - "options": {} + "notes": "" }, { "id": "1619605019281", "component": "httproute", "tab": "1615551125555", "name": "GET db", - "x": 115, - "y": 651, + "x": 173, + "y": 653, "connections": { "0": [ { @@ -830,9 +830,6 @@ "text": "Listening", "color": "green" }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -846,15 +843,18 @@ "get", 5000 ] - } + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false }, { "id": "1619784672383", "component": "trigger", "tab": "1615551125555", "name": "turnOnAlarm", - "x": 85, - "y": 1231, + "x": 120, + "y": 1234, "connections": { "0": [ { @@ -871,20 +871,20 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "datatype": "object", - "data": "{command: \"turnOnAlarm\"}" - } + "data": "{command: \"turnOnAlarm\"}", + "datatype": "object" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1619784812964", "component": "trigger", "tab": "1615551125555", "name": "turnOffAlarm", - "x": 84, - "y": 1304, + "x": 118, + "y": 1307, "connections": { "0": [ { @@ -901,12 +901,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "datatype": "object", - "data": "{command: \"turnOffAlarm\"}" - } + "data": "{command: \"turnOffAlarm\"}", + "datatype": "object" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1621340721628", @@ -924,11 +924,11 @@ "text": "modbus_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "modbus_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1622640022885", @@ -953,9 +953,6 @@ "text": "Listening", "color": "green" }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -968,7 +965,10 @@ "post", 5000 ] - } + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false }, { "id": "1622640073521", @@ -993,11 +993,11 @@ "text": "", "color": "gray" }, - "color": "#2134B0", - "notes": "", "options": { "edge": "undefined" - } + }, + "color": "#2134B0", + "notes": "" }, { "id": "1622641420685", @@ -1015,9 +1015,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#5D9CEC", - "notes": "", - "options": {} + "notes": "" }, { "id": "1634303504177", @@ -1025,7 +1025,7 @@ "tab": "1612772287426", "name": "RAM", "x": 69.88333129882812, - "y": 885.5, + "y": 888.5, "connections": { "0": [ { @@ -1039,15 +1039,15 @@ "output": [] }, "state": { - "text": "776.75 MB / 987.80 MB", + "text": "795.21 MB / 987.80 MB", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { "enabled": true, "interval": 30000 - } + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1634303533779", @@ -1069,16 +1069,16 @@ "output": [] }, "state": { - "text": "5.79 GB / 7.26 GB", + "text": "5.50 GB / 7.26 GB", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { "enabled": true, "path": "/", "interval": 30000 - } + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1634303595494", @@ -1107,11 +1107,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1634303602169", @@ -1129,11 +1129,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1634303685503", @@ -1151,11 +1151,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1634303743260", @@ -1181,13 +1181,13 @@ "text": "", "color": "gray" }, - "color": "#5D9CEC", - "notes": "", "options": { - "url": "http://192.168.252.2:8004/sentmessage", + "stringify": "json", "method": "POST", - "stringify": "json" - } + "url": "http://192.168.252.2:8004/sentmessage" + }, + "color": "#5D9CEC", + "notes": "" }, { "id": "1634463186563", @@ -1207,21 +1207,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634464580289", "component": "code", "tab": "1612772287426", "name": "Code", - "x": 253, - "y": 788, + "x": 245, + "y": 787, "connections": { "0": [ { @@ -1242,13 +1242,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, "code": "let response = {};\nresponse.cpu = value.cpu;\nresponse.uptime = value.uptime;\n\nsend(0, response);", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1634465243324", @@ -1268,20 +1268,20 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634465281992", "component": "code", "tab": "1612772287426", "name": "Code", - "x": 242, + "x": 245, "y": 884, "connections": { "0": [ @@ -1303,13 +1303,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, - "code": "value.sender = \"ram\";\n//let total = value.total/1024/1024;\n//let free = value.free/1024/1024;\n//let used = value.used/1024/1024;\nlet response = {};\n//value.memory_total = (total).toFixed(0) + ' MB';\n//value.memory_free = (free).toFixed(0) + ' MB';\n//value.memory_used = (used).toFixed(0) + ' MB';\n\nresponse.memory_total = value.total;\nresponse.memory_free = value.free;\nresponse.memory_used = value.used;\n\nsend(0, response);", + "code": "value.sender = \"ram\";\n\nlet response = {};\n\nresponse.memory_total = Math.round(value.total / (1024 ** 2));\nresponse.memory_free = Math.round(value.free / (1024 ** 2));\nresponse.memory_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1634465338103", @@ -1329,13 +1329,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634465821120", @@ -1364,13 +1364,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, - "code": "value.sender = \"hdd\";\n//let total = value.total/1024/1024;\n//let free = value.free/1024/1024;\n//let used = value.used/1024/1024;\nlet response = {};\n//value.hdd_total = (total).toFixed(0) + ' MB';\n//value.hdd_free = (free).toFixed(0) + ' MB';\n//value.used = (used).toFixed(0) + ' MB';\n\nresponse.hdd_total = value.total;\nresponse.hdd_free = value.free;\nresponse.hdd_used = value.used;\n\nsend(0, response);", + "code": "value.sender = \"hdd\";\n\nlet response = {};\n\nresponse.hdd_total = Math.round(value.total / (1024 ** 2));\nresponse.hdd_free = Math.round(value.free / (1024 ** 2));\nresponse.hdd_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1634465892500", @@ -1390,13 +1390,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634484067516", @@ -1416,13 +1416,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634488120710", @@ -1451,11 +1451,11 @@ "text": "", "color": "gray" }, - "color": "#2134B0", - "notes": "", "options": { "edge": "undefined" - } + }, + "color": "#2134B0", + "notes": "" }, { "id": "1635327431236", @@ -1475,21 +1475,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1635936391935", "component": "virtualwireout", "tab": "1615551125555", "name": "send-to-services", - "x": 778, - "y": 656, + "x": 753, + "y": 623, "connections": {}, "disabledio": { "input": [], @@ -1499,11 +1499,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1637069803394", @@ -1525,11 +1525,9 @@ "output": [] }, "state": { - "text": "1.5% / 72.32 MB", + "text": "1.2% / 70.53 MB", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "monitorfiles": true, "monitorconnections": true, @@ -1537,7 +1535,9 @@ "monitorconsumption": true, "enabled": true, "interval": 30000 - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1683664161036", @@ -1557,21 +1557,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1683981346282", "component": "virtualwirein", "tab": "1615551125555", "name": "from-dido-controller", - "x": 113, - "y": 456, + "x": 112, + "y": 459, "connections": { "0": [ { @@ -1592,19 +1592,19 @@ "text": "from-dido-controller", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "from-dido-controller" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1684055037116", "component": "debug", "tab": "1615551125555", "name": "from dido to cmd", - "x": 448, - "y": 519, + "x": 451, + "y": 532, "connections": {}, "disabledio": { "input": [ @@ -1616,21 +1616,21 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1684060205000", "component": "debug", "tab": "1615551125555", "name": "HTTP routes", - "x": 447, - "y": 620, + "x": 450, + "y": 639, "connections": {}, "disabledio": { "input": [ @@ -1642,13 +1642,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1684179110403", @@ -1666,13 +1666,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1699963668903", @@ -1713,11 +1713,11 @@ "text": "", "color": "gray" }, - "color": "#2134B0", - "notes": "", "options": { "edge": "undefined" - } + }, + "color": "#2134B0", + "notes": "" }, { "id": "1699964678894", @@ -1746,11 +1746,11 @@ "text": "modbus_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "modbus_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1699964793925", @@ -1770,13 +1770,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1699965957410", @@ -1825,9 +1825,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#2134B0", - "notes": "", - "options": {} + "notes": "" }, { "id": "1700411878636", @@ -1861,6 +1861,10 @@ { "index": "0", "id": "1732889185927" + }, + { + "index": "0", + "id": "1717441414646" } ] }, @@ -1872,17 +1876,17 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#5CB36D", - "notes": "", - "options": {} + "notes": "" }, { "id": "1714752862828", "component": "debug", "tab": "1611921777196", "name": "MDBToTb", - "x": 599, - "y": 257, + "x": 759, + "y": 313, "connections": {}, "disabledio": { "input": [], @@ -1892,13 +1896,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1717441414646", @@ -1927,13 +1931,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { - "keepmessage": true, - "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n}", - "outputs": 1 - } + "outputs": 1, + "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n\telse if(value.status === \"NOK-thermometer\")\n\t{\n\t\tsend(0, {\"thermometer\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"temperature\"))\n\t{\n\t\tsend(0, {\"thermometer\": \"OK\"});\n\t}\n}", + "keepmessage": true + }, + "color": "#656D78", + "notes": "" }, { "id": "1717442627834", @@ -1953,13 +1957,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1717442631338", @@ -1977,11 +1981,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1718016045116", @@ -2006,11 +2010,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1718016052341", @@ -2039,15 +2043,15 @@ "text": "Running", "color": "gray" }, - "color": "#30E193", - "notes": "", "options": { "slack_channel": "C071KN2Q8SK", "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", - "message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\", \"Node db has changed\"]", + "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "al_shariff_10.0.0.38" - } + "name": "rvo_senica_20_10.0.0.113" + }, + "color": "#30E193", + "notes": "" }, { "id": "1718016073501", @@ -2072,13 +2076,13 @@ "text": "", "color": "gray" }, - "color": "#5D9CEC", - "notes": "", "options": { - "url": "http://192.168.252.2:8004/slack", + "stringify": "json", "method": "POST", - "stringify": "json" - } + "url": "http://192.168.252.2:8004/slack" + }, + "color": "#5D9CEC", + "notes": "" }, { "id": "1718016086212", @@ -2098,13 +2102,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1718016094070", @@ -2129,12 +2133,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", - "datatype": "object" - } + "datatype": "object", + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1729855334955", @@ -2152,11 +2156,11 @@ "text": "platform-rpc-call", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "platform-rpc-call" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1729855371093", @@ -2174,13 +2178,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1731068658334", @@ -2205,11 +2209,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731068754606", @@ -2235,18 +2239,18 @@ "output": [] }, "state": { - "text": "Reconnecting", - "color": "yellow" + "text": "Connected", + "color": "green" }, - "color": "#888600", - "notes": "", "options": { "username": "", "clientid": "", "port": "2764", "host": "192.168.252.2", "topic": "" - } + }, + "color": "#888600", + "notes": "" }, { "id": "1731069001548", @@ -2271,9 +2275,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#888600", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069033416", @@ -2291,11 +2295,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069059135", @@ -2320,9 +2324,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#888600", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069079243", @@ -2340,13 +2344,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1731069116691", @@ -2371,9 +2375,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069131637", @@ -2398,9 +2402,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069137374", @@ -2425,9 +2429,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069179846", @@ -2452,9 +2456,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069192937", @@ -2479,9 +2483,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069264443", @@ -2506,19 +2510,19 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069334626", "component": "virtualwirein", "tab": "1615551125555", "name": "db-init", - "x": 151.88333129882812, - "y": 148, + "x": 172.88333129882812, + "y": 129, "connections": { "0": [ { @@ -2535,11 +2539,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069548145", @@ -2564,11 +2568,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069567152", @@ -2593,19 +2597,19 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731070156936", "component": "virtualwirein", "tab": "1615551125555", "name": "db-init", - "x": 89.88333129882812, - "y": 1381, + "x": 126.88333129882812, + "y": 1377, "connections": { "0": [ { @@ -2622,11 +2626,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731234189516", @@ -2651,9 +2655,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731234189551", @@ -2678,9 +2682,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1732700042559", @@ -2709,9 +2713,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#888600", - "notes": "", - "options": {} + "notes": "" }, { "id": "1732700057052", @@ -2736,11 +2740,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1732700071298", @@ -2758,13 +2762,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1732700642917", @@ -2782,11 +2786,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1732889185927", @@ -2804,13 +2808,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1733574412965", @@ -2835,12 +2839,12 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" } ], - "version": 618 + "version": 615 } diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 523d39b..2e23549 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -35,16 +35,16 @@ pins number 11, 12, 13 (we receive 10,11,12 in rsPortReceivedData) are "stykace" When port receives data, it must be exactly 4 bytes long. Second byte is pin, that changed its value, fourth byte is value itself. After that, we set this value to "previousValues[allPins[whichpin]]" variable -state_of_main_switch - reportovať stav hlavného ističa : 0-> off 1-> on -rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky: Manual = 1 a Auto = 0 -> Manual, +state_of_main_switch - reportovat stav hlaveho istica : 0-> off 1-> on +rotary_switch_state - sem by sa mal reportovat stav vstupov manual a auto pola nasledovnej logiky: Manual = 1 a Auto = 0 -> Manu Manual = 0 a Auto = 0 -> Off, Manual = 0 a Auto = 1 -> Automatic -door_condition - pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open -twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy +door_condition - pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Ope +twilight_sensor - hodnotu, ktoru vracia ten analogovy vstup (17) treba poslat sem ako float number. Zrejme tu potom pridame nejaky koeficient prevodu na luxy -Na každú líniu: -state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off, 1-> on -state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off, 1-> on +Na kazdu liniu +state_of_breaker - podla indexu istica sa reportuje jeho stav, teda istic na liniu 1: 0-> off, 1-> on +state_of_contactor - podla indexu stkaca sa reportuje jeho stav, teda stykac 1 na liniu 1: 0-> off, 1-> on */ const { errLogger, logger, monitor } = require('./helper/logger'); @@ -70,6 +70,7 @@ let rvoTbName; let GLOBALS; //FLOW global GLOBALS let SETTINGS; // GLOBALS.settings let controller_type; +let hasMainSwitch; let alarmStatus = "OFF"; @@ -126,17 +127,17 @@ exports.install = function(instance) { //status for calculating Statecodes let deviceStatus = { //key is device name: temperature,.... - "state_of_main_switch": "Off", //Hlavný istič (po novom druhy dverovy kontakt) - "rotary_switch_state": "Off", //Prevádzkový mód + "state_of_main_switch": "Off", //Hlavny istic (alebo druhy dverovy kontakt) + "rotary_switch_state": "Off", //Prevadzkovy "door_condition": "closed", //Dverový kontakt "em": "OK", //elektromer rvo "temperature": "OK", //templomer - "battery": "OK", //Batéria + "battery": "OK", //Bateria "power_supply": "OK", //Zdroj "master_node": "OK", //MN - GLOBALS.settings.masterNodeIsResponding - "no_voltage": "OK", //GLOBALS.settings.no_voltage - výpadok napätia na fáze - "state_of_breaker": {}, //"Off",//Istič - "state_of_contactor": {}, //"Off",//Stykač + "no_voltage": "OK", //GLOBALS.settings.no_voltage - vypadok napatia na faze + "state_of_breaker": {}, //"Off",//Istic + "state_of_contactor": {}, //"Off",//Stykac "twilight_sensor": "OK" //lux sensor }; @@ -149,10 +150,12 @@ exports.install = function(instance) { pinsData = GLOBALS.pinsData; relaysData = GLOBALS.relaysData; - tbHandler = new DataToTbHandler(SEND_TO.tb) + tbHandler = new DataToTbHandler(SEND_TO.tb); tbHandler.setSender(exports.title); - controller_type = SETTINGS.controller_type //"lm" or "unipi" //logicMachine + controller_type = SETTINGS.controller_type; //"lm" or "unipi" + hasMainSwitch = SETTINGS.has_main_switch; + if (controller_type == "") controller_type = "lm"; console.log(exports.title, "controller type: ", controller_type); @@ -596,27 +599,27 @@ exports.install = function(instance) { let bits = []; - //Hlavný istič - state_of_main_switch => v rvo senica je to druhy door pre silovu cast (EM) - if (deviceStatus["state_of_main_switch"] == "closed") { + //Hlavny istic - state_of_main_switch => v rvo senica je to druhy door pre silovu cast (EM) + if (deviceStatus["state_of_main_switch"] === "closed" || deviceStatus["state_of_main_switch"] === "Off") { bits.push(0); } else { bits.push(1); } - //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY + //Prevadzkovy mod - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY if (!SETTINGS.maintenance_mode) { - if (deviceStatus["rotary_switch_state"] == "Manual") { + if (deviceStatus["rotary_switch_state"] === "Manual") { bits.push(0); bits.push(1); } - if (deviceStatus["rotary_switch_state"] == "Automatic") { + if (deviceStatus["rotary_switch_state"] === "Automatic") { bits.push(0); bits.push(0); } - if (deviceStatus["rotary_switch_state"] == "Off") { + if (deviceStatus["rotary_switch_state"] === "Off") { bits.push(1); bits.push(0); } @@ -626,8 +629,8 @@ exports.install = function(instance) { bits.push(1); } - //Dverový kontakt - if (deviceStatus["door_condition"] == "closed") { + //Dverovy kontakt + if (deviceStatus["door_condition"] === "closed") { bits.push(0); } else { @@ -635,7 +638,7 @@ exports.install = function(instance) { } //EM - if (deviceStatus["em"] == "NOK") { + if (deviceStatus["em"] === "NOK") { bits.push(1); } else { @@ -643,7 +646,7 @@ exports.install = function(instance) { } //Teplomer - if (deviceStatus["temperature"] == "NOK") { + if (deviceStatus["temperature"] === "NOK") { bits.push(1); } else { @@ -651,7 +654,7 @@ exports.install = function(instance) { } //Batéria - if (deviceStatus["battery"] == "NOK") { + if (deviceStatus["battery"] === "NOK") { bits.push(1); } else { @@ -659,7 +662,7 @@ exports.install = function(instance) { } //Zdroj - if (deviceStatus["power_supply"] == "NOK") { + if (deviceStatus["power_supply"] === "NOK") { bits.push(1); } else { @@ -667,7 +670,7 @@ exports.install = function(instance) { } //MN - if (deviceStatus["master_node"] == "NOK") { + if (deviceStatus["master_node"] === "NOK") { bits.push(1); } else { @@ -675,14 +678,14 @@ exports.install = function(instance) { } //výpadok napätia na fáze - if (deviceStatus["no_voltage"] == "NOK") { + if (deviceStatus["no_voltage"] === "NOK") { bits.push(1); } else { bits.push(0); } - if (deviceStatus["twilight_sensor"] == "NOK") { + if (deviceStatus["twilight_sensor"] === "NOK") { bits.push(1); } else { @@ -738,6 +741,26 @@ exports.install = function(instance) { } + function getPins(controllerType, hasMainSwitch) { + + let pins = []; + + if (controllerType === "lm") { + pins = [1, 4, 6]; + if (hasMainSwitch === 1) { + pins = [4, 6]; + } + } else if (controllerType === "unipi") { + pins = ["input1_01", "input1_04", "input1_05"]; + if (hasMainSwitch === 1) { + pins = ["input1_01", "input1_04"]; + } + } + + return pins; + } + + function checkRvoStatus() { // we check if any of these pins values are 0 --> it means status RVO is "NOK" @@ -746,12 +769,12 @@ exports.install = function(instance) { let status = "OK"; for (const [key, value] of Object.entries(deviceStatus)) { - if (["em", "twilight_sensor", "temperature", "master_node"].includes(key) && value == "NOK") status = "NOK"; + if (["em", "twilight_sensor", "temperature", "master_node"].includes(key) && value === "NOK") status = "NOK"; } - if (status == "OK") { - let pinIndexes = [1, 4, 6]; - if (controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05']; + if (status === "OK") { + + let pinIndexes = getPins(controller_type, hasMainSwitch); for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { @@ -807,8 +830,20 @@ exports.install = function(instance) { let value = "On"; if (newPinValue === 0) value = "Off"; - //Prevádzkový mód - if (type == "rotary_switch_state") { + //Hlavny istic + if (type === "state_of_main_switch" && hasMainSwitch) { + if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { + sendNotification("switchLogic", rvoTbName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance, "state_of_main_switch"); + deviceStatus["state_of_main_switch"] = "Off"; + } + else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { + sendNotification("switchLogic", rvoTbName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance, "state_of_main_switch"); + deviceStatus["state_of_main_switch"] = "On"; + } + } + + //Prevadzkovy mod + else if (type == "rotary_switch_state") { // combination of these two pins required to get result let pin2, pin3; if (pinIndex == 2 || pinIndex == "input1_02") { @@ -860,7 +895,7 @@ exports.install = function(instance) { } } - //Batéria - pin 5 + //Bateria - pin 5 else if (type === "battery") { if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", rvoTbName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level"); @@ -874,9 +909,8 @@ exports.install = function(instance) { } } - //Dverový kontakt - pin 6 - //! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch" - //! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition' + //Dverovy kontakt - pin 6 + //! Ak je rvo s dvoma dverovymi kontaktami, ked pride z evoku signal z input1_05, co bol predytm "state_of_main switch" handlujeme ho teraz ako 'door_condition' else if (type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index b907148..d383971 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -41,7 +41,7 @@ exports.install = function(instance) { class SocketWithClients { - constructor () { + constructor() { this.stream = null; this.socket = null; this.clients = {}; @@ -55,7 +55,7 @@ exports.install = function(instance) { this.indexInDeviceConfig = 0; // first item in deviceConfig this.lengthOfActualDeviceStream = null; this.device = null; - + // lampSwitchNotification helper variables this.onNotificationSent = false; this.offNotificationSent = false; @@ -66,7 +66,7 @@ exports.install = function(instance) { buildPhases = () => { let a = []; - for (let i = 1; i<= phases; i++) { + for (let i = 1; i <= phases; i++) { a.push(`Phase_${i}_voltage`) } return a; @@ -81,14 +81,13 @@ exports.install = function(instance) { }) // we create a client for every deviceAddress ( = address) in list and push them into dictionary - for( let i = 0; i < deviceConfig.length; i++) - { + for (let i = 0; i < deviceConfig.length; i++) { this.clients[deviceConfig[i].deviceAddress] = new modbus.client.RTU(this.socket, deviceConfig[i].deviceAddress, 2000); // 2000 is timeout in register request, default is 5000, which is too long } this.socket.on('error', function(e) { console.log('socket connection error', e); - if(e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') { + if (e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') { console.log(exports.title + ' Waiting 10 seconds before trying to connect again'); setTimeout(obj.startSocket, 10000); } @@ -99,7 +98,7 @@ exports.install = function(instance) { setTimeout(obj.startSocket, 10000); }); - this.socket.on('open', function () { + this.socket.on('open', function() { console.log("socket connected"); obj.getActualStreamAndDevice(); obj.timeoutInterval = timeoutInterval - DELAY_BETWEEN_DEVICES; // to make sure readout always runs in timeoutinterval we substract DELAY_BETWEEN_DEVICES @@ -112,11 +111,11 @@ exports.install = function(instance) { this.index = 0; this.errors = 0; this.stream = dev.stream; - this.lengthOfActualDeviceStream = dev.stream.length; + this.lengthOfActualDeviceStream = dev.stream.length; this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number this.device = dev.device; //em340, twilight_sensor - if(this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval); + if (this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval); else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES); } @@ -130,21 +129,18 @@ exports.install = function(instance) { let obj = this; this.clients[this.deviceAddress].readHoldingRegisters(register, size) - .then( function (resp) { - + .then(function(resp) { + resp = resp.response._body.valuesAsArray; //resp is array of length 1 or 2, f.e. [2360,0] // console.log(deviceAddress, register, tbAttribute, resp); //device is responding again after NOK status - if(numberOfNotResponding.hasOwnProperty(obj.device)) - { + if (numberOfNotResponding.hasOwnProperty(obj.device)) { let message = ""; - if(obj.device == "em340") - { + if (obj.device == "em340") { message = "electrometer_ok"; } - else if(obj.device == "twilight_sensor") - { + else if (obj.device == "twilight_sensor") { message = "twilight_sensor_ok"; } message && sendNotification("modbus_reader: readRegisters", tbName, message, {}, "", SEND_TO.tb, instance); @@ -157,25 +153,21 @@ exports.install = function(instance) { obj.index++; obj.readAnotherRegister(); - }).catch (function () { + }).catch(function() { //console.log("errors pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute); - + obj.errors++; - if(obj.errors == obj.lengthOfActualDeviceStream) - { - instance.send(SEND_TO.dido_controller, {status: "NOK-" + obj.device}); // NOK-em340, NOK-em111, NOK-twilight_sensor, NOK-thermometer - + if (obj.errors == obj.lengthOfActualDeviceStream) { + instance.send(SEND_TO.dido_controller, { status: "NOK-" + obj.device }); // NOK-em340, NOK-em111, NOK-twilight_sensor, NOK-thermometer + //todo - neposlalo notification, ked sme vypojili twilight a neposle to do tb, ale do dido ?? - if(!numberOfNotResponding.hasOwnProperty(obj.device)) - { + if (!numberOfNotResponding.hasOwnProperty(obj.device)) { let message = ""; - if(obj.device == "twilight_sensor") - { + if (obj.device == "twilight_sensor") { message = "twilight_sensor_nok"; } - else if(obj.device == "em340") - { + else if (obj.device == "em340") { message = "electrometer_nok"; } message && sendNotification("modbus_reader: readingTimeouted", tbName, message, {}, "", SEND_TO.tb, instance); @@ -183,7 +175,7 @@ exports.install = function(instance) { } obj.errors = 0; - numberOfNotResponding[obj.device] += 1; + numberOfNotResponding[obj.device] += 1; } // console.error(require('util').inspect(arguments, { @@ -191,9 +183,8 @@ exports.install = function(instance) { // })) // if reading out of device's last register returns error, we send accumulated allValues to dido_controller (if allValues are not an empty object) - if(obj.index + 1 >= obj.lengthOfActualDeviceStream) - { - if(!isObjectEmpty(obj.allValues)) instance.send(SEND_TO.dido_controller, {values: obj.allValues}); + if (obj.index + 1 >= obj.lengthOfActualDeviceStream) { + if (!isObjectEmpty(obj.allValues)) instance.send(SEND_TO.dido_controller, { values: obj.allValues }); obj.allValues = {}; } obj.index++; @@ -203,7 +194,7 @@ exports.install = function(instance) { }; readAnotherRegister = () => { - if(this.index < this.lengthOfActualDeviceStream) setTimeout(this.readRegisters, 0); + if (this.index < this.lengthOfActualDeviceStream) setTimeout(this.readRegisters, 0); else this.setNewStream(); } @@ -212,18 +203,16 @@ exports.install = function(instance) { for (let i = 0; i < this.lengthOfActualDeviceStream; i++) { let a = this.stream[i]; - if (a.register === register) - { + if (a.register === register) { let tbAttribute = a.tbAttribute; let multiplier = a.multiplier; - - let value = this.calculateValue(response, multiplier); + + let value = this.calculateValue(response, multiplier); // console.log(register, tbName, tbAttribute, response, a.multiplier, value); // if(tbName == undefined) return; - if(this.index + 1 < this.lengthOfActualDeviceStream) - { + if (this.index + 1 < this.lengthOfActualDeviceStream) { this.allValues[tbAttribute] = value; return; } @@ -236,52 +225,45 @@ exports.install = function(instance) { this.checkNullVoltage(values); this.lampSwitchNotification(values); - instance.send(SEND_TO.dido_controller, {values: values}); + instance.send(SEND_TO.dido_controller, { values: values }); this.allValues = {}; break; } - } + } } - setNewStream = () => - { - if(this.lengthOfActualDeviceStream == this.index) - { - if(this.indexInDeviceConfig + 1 == deviceConfig.length) - { + setNewStream = () => { + if (this.lengthOfActualDeviceStream == this.index) { + if (this.indexInDeviceConfig + 1 == deviceConfig.length) { this.indexInDeviceConfig = 0; - } - else - { + } + else { this.indexInDeviceConfig += 1; } this.getActualStreamAndDevice(); - } + } } - calculateValue = (response, multiplier) => - { + calculateValue = (response, multiplier) => { let value = 0; let l = response.length; - if (l === 2) - { - value = (response[1]*(2**16) + response[0]); + if (l === 2) { + value = (response[1] * (2 ** 16) + response[0]); - if(value >= (2**31)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x80000000), ak vieš robiť logický súčin - { - value = value - "0xFFFFFFFF" + 1; - } + if (value >= (2 ** 31)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x80000000), ak vieš robiť logický súčin + { + value = value - "0xFFFFFFFF" + 1; + } } - else if (l === 1) - { + else if (l === 1) { value = response[0]; - if(value >= (2**15)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x8000), ak vieš robiť logický súčin + if (value >= (2 ** 15)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x8000), ak vieš robiť logický súčin { - value = value - "0xFFFF" + 1; + value = value - "0xFFFF" + 1; } } @@ -290,59 +272,54 @@ exports.install = function(instance) { checkNullVoltage = (values) => { - if(!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return; + if (!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return; Object.keys(values).map(singleValue => { - if (this.phases.includes(singleValue)) - { + if (this.phases.includes(singleValue)) { let l = singleValue.split("_"); let phase = parseInt(l[1]); // console.log(values[singleValue], tbName); - if(values[singleValue] == 0) - { + if (values[singleValue] == 0) { noVoltage.add(phase); - sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase ); + sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", { phase: phase }, "", SEND_TO.tb, instance, "voltage" + phase); // console.log('no voltage') } - else - { + else { noVoltage.delete(phase); // console.log('voltage detected') - sendNotification("modbus_reader: checkNullVoltage", tbName, "voltage_on_phase_restored", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase); + sendNotification("modbus_reader: checkNullVoltage", tbName, "voltage_on_phase_restored", { phase: phase }, "", SEND_TO.tb, instance, "voltage" + phase); } - } + } }) } /** * function sends notification to slack and to tb, if EM total_power value changes more than numberOfNodes*15. This should show, that RVO lamps has been switched on or off - */ + */ lampSwitchNotification = (values) => { - if(!values.hasOwnProperty("total_power")) return; + if (!values.hasOwnProperty("total_power")) return; const actualTotalPower = values.total_power; - + const numberOfNodes = Object.keys(FLOW.GLOBALS.nodesData).length; - if(numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300 - - if(actualTotalPower > numberOfNodes * 15 && this.onNotificationSent == false) - { + if (numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300 + + if (actualTotalPower > numberOfNodes * 15 && this.onNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance); this.onNotificationSent = true; this.offNotificationSent = false; - } - else if(actualTotalPower <= numberOfNodes * 15 && this.offNotificationSent == false) - { + } + else if (actualTotalPower <= numberOfNodes * 15 && this.offNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance); this.onNotificationSent = false; this.offNotificationSent = true; } } - } + } const isObjectEmpty = (objectName) => { return Object.keys(objectName).length === 0 && objectName.constructor === Object; @@ -355,6 +332,8 @@ exports.install = function(instance) { noVoltage = FLOW.GLOBALS.settings.no_voltage; mainSocket = new SocketWithClients(); + console.log("novoltage: ", noVoltage, typeof noVoltage); + // this notification is to show, that flow (unipi) has been restarted sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance); } diff --git a/flow/show_dbdata.js b/flow/show_dbdata.js index d01eecd..0e5cbcc 100644 --- a/flow/show_dbdata.js +++ b/flow/show_dbdata.js @@ -24,25 +24,25 @@ exports.install = function(instance) { instance.send(0, FLOW.GLOBALS.pinsData); }) instance.on("4", _ => { - instance.send(0, {rpcSwitchOffLine, rpcSetNodeDimming, rpcLineProfile, rpcNodeProfile, sunCalcExample, dataFromTerminalBroadcast}) + instance.send(0, { rpcSwitchOffLine, rpcSetNodeDimming, rpcLineProfile, rpcNodeProfile, sunCalcExample, dataFromTerminalBroadcast }) }) instance.on("5", _ => { - exec("sudo tail -n 25 monitor.txt" , (err, stdout, stderr) => { - if (err || stderr) instance.send(0,{err, stderr}); - else instance.send(0,stdout); + exec("sudo tail -n 25 monitor.txt", (err, stdout, stderr) => { + if (err || stderr) instance.send(0, { err, stderr }); + else instance.send(0, stdout); }) }) instance.on("6", _ => { - exec("sudo tail -n 25 err.txt" , (err, stdout, stderr) => { - if (err || stderr) instance.send(0,{err, stderr}); - else instance.send(0,stdout); + exec("sudo tail -n 25 err.txt", (err, stdout, stderr) => { + if (err || stderr) instance.send(0, { err, stderr }); + else instance.send(0, stdout); }) }) -}; +}; -const rpcSwitchOffLine = +const rpcSwitchOffLine = { "topic": "v1/gateway/rpc", "content": { @@ -90,7 +90,7 @@ const rpcSetNodeDimming = } } -const rpcLineProfile = +const rpcLineProfile = { "topic": "v1/gateway/rpc", "content": { @@ -212,7 +212,7 @@ const rpcNodeProfile = } } - const sunCalcExample = { +const sunCalcExample = { dusk_no_offset: '20:18', dawn_no_offset: '05:19', dusk: '20:18', diff --git a/flow/thermometer.js b/flow/thermometer.js index fa015c6..fc7074d 100644 --- a/flow/thermometer.js +++ b/flow/thermometer.js @@ -33,7 +33,7 @@ exports.install = function(instance) { logger.debug(exports.title, "installed"); - instance.on("close", function(){ + instance.on("close", function() { clearInterval(startRead); }) @@ -42,24 +42,25 @@ exports.install = function(instance) { try { - if(temperatureAddress === "") throw "Thermometer: temperatureAddress is not defined"; + if (temperatureAddress === "") throw "Thermometer: temperatureAddress is not defined"; exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => { - if(!error) - { + if (!error) { parseData(stdout) return; } counter++; - if(counter == NUMBER_OF_FAILURES_TO_SEND_ERROR) sendNotification("Thermometer_main", rvoTbName, "thermometer_is_not_responding", {}, {"Error": error}, SEND_TO.tb, instance, "thermometer"); - monitor.info("Thermometer is not responding", error); - instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"}); + if (counter == NUMBER_OF_FAILURES_TO_SEND_ERROR) { + sendNotification("Thermometer_main", rvoTbName, "thermometer_is_not_responding", {}, { "Error": error }, SEND_TO.tb, instance, "thermometer"); + monitor.info("Thermometer is not responding", error); + } + instance.send(SEND_TO.dido_controller, { status: "NOK-thermometer" }); }); } - catch(err) { + catch (err) { errLogger.error(exports.title, err); clearInterval(startRead); } @@ -70,12 +71,12 @@ exports.install = function(instance) { data = parseFloat(data); //logger.debug("Thermometer", data); - if(isNaN(data)) { + if (isNaN(data)) { errLogger.error("Thermometer sends invalid data"); return; } - if(counter > NUMBER_OF_FAILURES_TO_SEND_ERROR) //1 hour + if (counter > NUMBER_OF_FAILURES_TO_SEND_ERROR) //1 hour { instance.send(SEND_TO.debug, "Thermometer - temperature data are comming again"); sendNotification("Thermometer_parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer"); @@ -85,7 +86,7 @@ exports.install = function(instance) { "temperature": Number(data.toFixed(2)), } - instance.send(SEND_TO.dido_controller, {values: values}); + instance.send(SEND_TO.dido_controller, { values: values }); counter = 0; } From 0876e73c6849e3c9ba41a8f8a59b90fa514f4f08 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Sat, 31 May 2025 22:03:23 +0200 Subject: [PATCH 20/25] Add language notifications; Add power door handel --- databases/notifications.table | 13 +- flow/cmd_manager.js | 144 ++++++----------- flow/db_init.js | 28 +++- flow/designer.json | 224 +++++++++++++++------------ flow/dido_controller.js | 59 ++----- flow/helper/ErrorToServiceHandler.js | 135 ++++++---------- flow/helper/notification_reporter.js | 80 +++++----- flow/helper/serialport_helper.js | 15 +- flow/infosender.js | 74 ++++----- flow/modbus_reader.js | 2 - flow/slack_filter.js | 55 ++++--- flow/wsmqttpublish.js | 197 ++++++++++------------- 12 files changed, 445 insertions(+), 581 deletions(-) diff --git a/databases/notifications.table b/databases/notifications.table index 9dbe047..f7c96cb 100644 --- a/databases/notifications.table +++ b/databases/notifications.table @@ -20,9 +20,12 @@ key:string|weight:string|sk:string|en:string +|power_supply_works_correctly|NOTICE|Napájací zdroj pracuje správne|Power supply works correctly|............... +|battery_level_is_low|ERROR|Batéria má nízku úroveň napätia|Battery level is low|............... +|battery_level_is_ok|NOTICE|Batéria má správnu úroveň napätia|Battery level is OK|............... -+|door_opened|NOTICE|Dvere boli otvorené|Door has been opeed|............... -+|door_closed|NOTICE|Dvere boli zatvorené|Door has been closed|............... -+|door_opened_without_permission|WARNING|Dvere boli otvorené bez povolenia - zapnutá siréna|Door opened without permision - alarm is on|............... ++|door_main_open|NOTICE|Hlavné dvere boli otvorené|Main door has been opened|............... ++|door_em_open|NOTICE|Dvere silovej časti boli otvorené|Power door has been opened|............... ++|door_main_open_without_permission|WARNING|Hlavné dvere boli otvorené bez povolenia - zapnutá siréna|Main door has been opened without permission - alarm is on|............... ++|door_em_open_without_permission|WARNING|Dvere silovej časti boli otvorené bez povolenia|Power door has been opened without permission|............... ++|door_main_close|NOTICE|Hlavné dvere boli zatvorené|Main door has been closed|............... ++|door_em_close|NOTICE|Dvere silovej časti boli zatvorené|Power door has been closed|............... +|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|............... +|local_database_is_corrupted|CRITICAL|||............... +|electrometer_nok|ERROR|Elektromer neodpovedá|Electrometer is not responding|............... @@ -34,5 +37,5 @@ key:string|weight:string|sk:string|en:string +|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|............... +|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|............... +|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|............... -+|flow_restart|NOTICE|Restart flowu|Flow has been restarted|............... -+|nodes_db_changed|NOTICE|Zmena v node databaze|Node db has changed|............... ++|flow_restart|NOTICE|FLOW bol reštartovaný|FLOW has been restarted|............... ++|nodes_db_changed|NOTICE|Zmena v node databáze|Node db has changed|............... diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 8c7543d..18807f9 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -39,7 +39,7 @@ exports.install = function(instance) { var SunCalc = require('./helper/suncalc'); const DataToTbHandler = require('./helper/DataToTbHandler'); - const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler'); + const errorHandler = require('./helper/ErrorToServiceHandler'); const { sendNotification } = require('./helper/notification_reporter'); const process = require('process'); const { errLogger, logger, monitor } = require('./helper/logger'); @@ -127,8 +127,6 @@ exports.install = function(instance) { //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; - const errorHandler = new ErrorToServiceHandler(); - let rotary_switch_state; let lux_sensor; let state_of_breaker = {};//key is line, value is On/Off @@ -165,8 +163,6 @@ exports.install = function(instance) { tbHandler = new DataToTbHandler(SEND_TO.tb); tbHandler.setSender(exports.title); - errorHandler.setProjectsId(SETTINGS.project_id); - let now = new Date(); console.log("CMD Manager installed", now.toLocaleString("sk-SK")); @@ -186,7 +182,6 @@ exports.install = function(instance) { }, 120000); reportEdgeDateTimeAndNumberOfLuminaires(); - setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour setCorrectPlcTimeOnceADay(); @@ -1155,7 +1150,7 @@ exports.install = function(instance) { params.type = "cmd-master"; params.register = 4; params.address = 0; - params.timestamp = Date.now() + 60000; + params.timestamp = 0; params.addMinutesToTimestamp = 5; params.tbname = SETTINGS.rvoTbName; params.info = "Master node FW verzia"; @@ -1579,6 +1574,7 @@ exports.install = function(instance) { let readBytes = 11; let timeout = 4000; + // await keyword is important, otherwise incorrect data is returned! await writeData(rsPort, resp, readBytes, timeout).then(function(data) { @@ -1592,7 +1588,6 @@ exports.install = function(instance) { //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let message = result.message; // OK, NOK let message_type = result.type; - let error = result.error; if (params.hasOwnProperty("debug")) { if (params.debug) { @@ -1643,11 +1638,6 @@ exports.install = function(instance) { if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; } - //odoslanie príkazu z terminálu - dáta - if (type == "cmd-terminal") { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance); - } - if (params.debug) { //logger.debug("saveToTb", saveToTb, tbname, values); } @@ -1879,16 +1869,6 @@ exports.install = function(instance) { logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); - //APP START - let dataToInfoSender = { id: SETTINGS.project_id, name: SETTINGS.rvo_name }; - dataToInfoSender.fw_version = SETTINGS.edge_fw_version; - dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); - dataToInfoSender.__force__ = true; - - instance.send(SEND_TO.infoSender, dataToInfoSender); - - logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); - }).catch(function(reason) { instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); }); @@ -1986,7 +1966,7 @@ exports.install = function(instance) { //logger.debug(tasks); logger.debug("-->CMD MANAGER - RUN TASKS"); - interval = setInterval(runTasks, LONG_INTERVAL); + interval = setInterval(runTasks, 5000); } else if (cmd == "reload_relays") { loadRelaysData(flowdata.data.line); @@ -2627,7 +2607,7 @@ exports.install = function(instance) { } //Dimming, CCT - if (register == 1) { + else if (register == 1) { let brightness = 0; let dimming = byte0; if (dimming > 128) { @@ -2648,125 +2628,87 @@ exports.install = function(instance) { } // - if (register == 4) { + else if (register == 4) { values["master_node_version"] = bytes[1] + "." + bytes[2]; //logger.debug("FW Version", register, bytes); } //Napätie - if (register == 74) { + else if (register == 74) { let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); values["voltage"] = Number(voltage); } //Prúd - if (register == 75) { + else if (register == 75) { let current = bytesToInt(bytes); values["current"] = current; } //výkon - if (register == 76) { + else if (register == 76) { let power = (bytesToInt(bytes) * 0.1).toFixed(2); values["power"] = Number(power); } //účinník - if (register == 77) { + else if (register == 77) { let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); values["power_factor"] = Number(power_factor); } //frekvencia - if (register == 78) { + else if (register == 78) { let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); values["frequency"] = Number(frequency); } //energia - if (register == 79) { + else if (register == 79) { let energy = bytesToInt(bytes); - - //Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal. - + //Energiu treba reportovať v kWh -> delit 1000 values["energy"] = energy / 1000; } //doba života - if (register == 80) { + else if (register == 80) { let lifetime = (bytesToInt(bytes) / 60).toFixed(2); values["lifetime"] = Number(lifetime); } //nastavenie profilu - if (register == 8) { + else if (register == 8) { let time_schedule_settings = bytesToInt(bytes); values["time_schedule_settings"] = time_schedule_settings; } //naklon - nateraz sa z nodu nevycitava! kvoli problemom s accelerometrom a vracanymi hodnotami, posielame temp a x y z vo funkcii accelerometerData() - if (register == 84) { - const temp = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; - const inclination_x = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; - const inclination_y = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; - const inclination_z = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; - - if (temp === undefined) temp = 999; - if (inclination_x === undefined) inclination_x = 999; - if (inclination_y === undefined) inclination_y = 999; - if (inclination_z === undefined) inclination_z = 999; - - values["temperature"] = temp; - values["inclination_x"] = inclination_x; - values["inclination_y"] = inclination_y; - values["inclination_z"] = inclination_z; - } - - let h = byte3; - let m = byte2; - - let timestamp; - - if (register == 87 || register == 6 || register == 7) { - //if(byte3 < 10) h = "0" + byte3; - //if(byte2 < 10) m = "0" + byte2; - //if(byte1 < 10) s = "0" + byte1; - - var d = new Date(); - d.setHours(h, m, 0, 0); - timestamp = d.getTime(); - } - - //aktuálny čas - if (register == 87) { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["actual_time"] = h + ":" + m + ":" + s; - - values["actual_time"] = timestamp; - } - - //čas súmraku - if (register == 6) { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["dusk_time"] = h + ":" + m + ":" + s; - - values["dusk_time"] = timestamp; - } - - //čas úsvitu - if (register == 7) { - //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. - //values["dawn_time"] = h + ":" + m + ":" + s; - - values["dawn_time"] = timestamp; + else if (register == 84) { + values["temperature"] = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; + values["inclination_x"] = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; + values["inclination_y"] = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; + values["inclination_z"] = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; } //FW verzia nodu - if (register == 89) { + else if (register == 89) { //formát: "Byte3: Byte2.Byte1 (Byte0)" values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; } + else if (register == 87 || register == 6 || register == 7) { + var d = new Date(); + d.setHours(byte3, byte2, 0, 0); + let timestamp = d.getTime(); + + //aktuálny čas + if (register == 87) values["actual_time"] = timestamp; + //čas súmraku + else if (register == 6) values["dusk_time"] = timestamp; + //čas úsvitu + else if (register == 7) values["dawn_time"] = timestamp; + } + return values; } @@ -2850,9 +2792,13 @@ exports.install = function(instance) { if (temperatureInSenica === null) return; - for (const key in relaysData) { + //clone nodesData and relaysData objects + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + let relaysData_clone = JSON.parse(JSON.stringify(relaysData)); - const lineData = relaysData[key]; + for (const key in relaysData_clone) { + + const lineData = relaysData_clone[key]; const lineNumber = lineData.line; const contactor = lineData.contactor; @@ -2862,17 +2808,21 @@ exports.install = function(instance) { let date = Date.now(); - Object.keys(nodesData).forEach((node, index) => { + Object.keys(nodesData_clone).forEach((node, index) => { setTimeout(function() { - if (nodesData[node].line === lineNumber) { + if (nodesData_clone[node].line === lineNumber) { + + // NOTE: if status of luminaire is NOK or OFFLINE, we do not send data; + let status = nodesData_clone[node].status; + if (status === "OFFLINE" || !status) return; let x = null; if (naklony.hasOwnProperty(node)) x = naklony[node].naklon; if (x === null) x = 0; - sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData[node].tbname, date); + sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData_clone[node].tbname, date); } }, (index + 1) * 500); diff --git a/flow/db_init.js b/flow/db_init.js index 5b3d589..2a5caf9 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -4,8 +4,7 @@ exports.group = 'Worksys'; exports.color = '#888600'; exports.version = '1.0.2'; exports.icon = 'sign-out'; -exports.input = 1; -exports.output = ["blue"]; +exports.output = 2; exports.html = `
@@ -33,6 +32,12 @@ exports.readme = ` const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const { initNotification } = require('./helper/notification_reporter'); +const errorHandler = require('./helper/ErrorToServiceHandler'); + +const SEND_TO = { + db_init: 0, + infoSender: 1 +}; exports.install = async function(instance) { @@ -94,15 +99,26 @@ exports.install = async function(instance) { } FLOW.dbLoaded = true; + errorHandler.setProjectId(dbs.settings.project_id); initNotification(); + //APP START - send to data services + const toService = { + id: dbs.settings.project_id, + name: dbs.settings.rvo_name, + fw_version: dbs.settings.edge_fw_version, + startdate: new Date().toISOString().slice(0, 19).replace('T', ' '), + js_error: "", + error_message: "" + }; + + instance.send(SEND_TO.infoSender, toService); + console.log("----------------> START - message send to service", toService); + setTimeout(() => { console.log("DB_INIT - data loaded"); - instance.send(0, "_") + instance.send(SEND_TO.db_init, "_") }, 5000) }; - - - diff --git a/flow/designer.json b/flow/designer.json index b51e1ca..82dfe7f 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -49,8 +49,8 @@ "component": "wsmqttpublish", "tab": "1612772287426", "name": "WS MQTT publish", - "x": 304.75, - "y": 237, + "x": 281.75, + "y": 174, "connections": { "0": [ { @@ -69,10 +69,6 @@ } ], "2": [ - { - "index": "0", - "id": "1618300863816" - }, { "index": "0", "id": "1634303685503" @@ -107,8 +103,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "tb-push", - "x": 86.75, - "y": 375, + "x": 72.75, + "y": 328, "connections": { "0": [ { @@ -144,8 +140,8 @@ "component": "debug", "tab": "1612772287426", "name": "to TB", - "x": 306.75, - "y": 371, + "x": 283.75, + "y": 324, "connections": {}, "disabledio": { "input": [ @@ -170,8 +166,8 @@ "component": "debug", "tab": "1612772287426", "name": "errors from MQTT Broker", - "x": 650, - "y": 76, + "x": 594, + "y": 57, "connections": {}, "disabledio": { "input": [ @@ -270,8 +266,8 @@ "component": "debug", "tab": "1611921777196", "name": "Debug", - "x": 595.8833312988281, - "y": 557.3500061035156, + "x": 596.8833312988281, + "y": 566.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -294,8 +290,8 @@ "component": "debug", "tab": "1611921777196", "name": "tempToTb", - "x": 598.8833312988281, - "y": 654.3500061035156, + "x": 595.8833312988281, + "y": 658.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -318,8 +314,8 @@ "component": "virtualwireout", "tab": "1611921777196", "name": "tb-push", - "x": 594.8833312988281, - "y": 350.25, + "x": 597.8833312988281, + "y": 377.25, "connections": {}, "disabledio": { "input": [], @@ -453,8 +449,8 @@ "color": "gray" }, "options": { - "data": "{line: 3, command: \"turnOff\", force: true}", - "datatype": "object" + "datatype": "object", + "data": "{line: 3, command: \"turnOff\", force: true}" }, "color": "#F6BB42", "notes": "" @@ -505,8 +501,8 @@ "color": "gray" }, "options": { - "datatype": "string", - "data": "profile_nodes" + "data": "profile_nodes", + "datatype": "string" }, "color": "#F6BB42", "notes": "" @@ -545,8 +541,8 @@ "component": "debug", "tab": "1612772287426", "name": "wsmqtt-exit1", - "x": 650.8833312988281, - "y": 160, + "x": 597.8833312988281, + "y": 149, "connections": {}, "disabledio": { "input": [], @@ -564,32 +560,6 @@ "color": "#967ADC", "notes": "" }, - { - "id": "1618300863816", - "component": "debug", - "tab": "1612772287426", - "name": "wsmqtt-exit2", - "x": 845.8833312988281, - "y": 320, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "options": { - "type": "data", - "repository": false, - "enabled": true - }, - "color": "#967ADC", - "notes": "" - }, { "id": "1618393583970", "component": "virtualwireout", @@ -701,8 +671,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "platform-rpc-call", - "x": 649.8833312988281, - "y": 246, + "x": 597.8833312988281, + "y": 247, "connections": {}, "disabledio": { "input": [], @@ -742,8 +712,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{line: 1, command: \"turnOn\", force: true}" + "data": "{line: 1, command: \"turnOn\", force: true}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -872,8 +842,8 @@ "color": "gray" }, "options": { - "data": "{command: \"turnOnAlarm\"}", - "datatype": "object" + "datatype": "object", + "data": "{command: \"turnOnAlarm\"}" }, "color": "#F6BB42", "notes": "" @@ -902,8 +872,8 @@ "color": "gray" }, "options": { - "data": "{command: \"turnOffAlarm\"}", - "datatype": "object" + "datatype": "object", + "data": "{command: \"turnOffAlarm\"}" }, "color": "#F6BB42", "notes": "" @@ -913,8 +883,8 @@ "component": "virtualwireout", "tab": "1611921777196", "name": "modbus_to_dido", - "x": 596, - "y": 462, + "x": 599, + "y": 471, "connections": {}, "disabledio": { "input": [], @@ -1039,7 +1009,7 @@ "output": [] }, "state": { - "text": "795.21 MB / 987.80 MB", + "text": "843.78 MB / 985.68 MB", "color": "gray" }, "options": { @@ -1069,7 +1039,7 @@ "output": [] }, "state": { - "text": "5.50 GB / 7.26 GB", + "text": "5.68 GB / 7.26 GB", "color": "gray" }, "options": { @@ -1140,8 +1110,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "send-to-services", - "x": 650.8833312988281, - "y": 355.5, + "x": 600.8833312988281, + "y": 341.5, "connections": {}, "disabledio": { "input": [], @@ -1163,8 +1133,8 @@ "tab": "1612772287426", "name": "192.168.252.2:8004/sentmessage", "reference": "", - "x": 480.8833312988281, - "y": 1334.7333374023438, + "x": 506.8833312988281, + "y": 1331.7333374023438, "connections": { "0": [ { @@ -1182,9 +1152,9 @@ "color": "gray" }, "options": { - "stringify": "json", + "url": "http://192.168.252.2:8004/sentmessage", "method": "POST", - "url": "http://192.168.252.2:8004/sentmessage" + "stringify": "json" }, "color": "#5D9CEC", "notes": "" @@ -1194,8 +1164,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 280.75, - "y": 1446, + "x": 305.75, + "y": 1442, "connections": {}, "disabledio": { "input": [ @@ -1403,7 +1373,7 @@ "component": "debug", "tab": "1612772287426", "name": "Send info", - "x": 480, + "x": 513, "y": 1441, "connections": {}, "disabledio": { @@ -1429,8 +1399,8 @@ "component": "infosender", "tab": "1612772287426", "name": "Info sender", - "x": 285, - "y": 1338, + "x": 301, + "y": 1336, "connections": { "0": [ { @@ -1462,8 +1432,8 @@ "component": "debug", "tab": "1612772287426", "name": "Debug", - "x": 795.8833312988281, - "y": 1329.5, + "x": 837.8833312988281, + "y": 1325.5, "connections": {}, "disabledio": { "input": [ @@ -1525,7 +1495,7 @@ "output": [] }, "state": { - "text": "1.2% / 70.53 MB", + "text": "2% / 86.44 MB", "color": "gray" }, "options": { @@ -1885,8 +1855,8 @@ "component": "debug", "tab": "1611921777196", "name": "MDBToTb", - "x": 759, - "y": 313, + "x": 766, + "y": 324, "connections": {}, "disabledio": { "input": [], @@ -1909,8 +1879,8 @@ "component": "code", "tab": "1611921777196", "name": "device-status", - "x": 755.0833282470703, - "y": 209, + "x": 764.0833282470703, + "y": 222, "connections": { "0": [ { @@ -1932,9 +1902,9 @@ "color": "gray" }, "options": { - "outputs": 1, + "keepmessage": true, "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n\telse if(value.status === \"NOK-thermometer\")\n\t{\n\t\tsend(0, {\"thermometer\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"temperature\"))\n\t{\n\t\tsend(0, {\"thermometer\": \"OK\"});\n\t}\n}", - "keepmessage": true + "outputs": 1 }, "color": "#656D78", "notes": "" @@ -1945,7 +1915,7 @@ "tab": "1611921777196", "name": "modbus service", "x": 966.0833282470703, - "y": 152, + "y": 165, "connections": {}, "disabledio": { "input": [ @@ -2048,7 +2018,7 @@ "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "rvo_senica_20_10.0.0.113" + "name": "" }, "color": "#30E193", "notes": "" @@ -2077,9 +2047,9 @@ "color": "gray" }, "options": { - "stringify": "json", + "url": "http://192.168.252.2:8004/slack", "method": "POST", - "url": "http://192.168.252.2:8004/slack" + "stringify": "json" }, "color": "#5D9CEC", "notes": "" @@ -2134,8 +2104,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -2145,7 +2115,7 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "platform-rpc-call", - "x": 649.9333343505859, + "x": 599.9333343505859, "y": 541.3500061035156, "connections": {}, "disabledio": { @@ -2167,8 +2137,8 @@ "component": "debug", "tab": "1612772287426", "name": "rpc cloud", - "x": 650.9333343505859, - "y": 451.3500061035156, + "x": 601.9333343505859, + "y": 440.3500061035156, "connections": {}, "disabledio": { "input": [], @@ -2191,8 +2161,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "db-init", - "x": 90.75, - "y": 250, + "x": 75.75, + "y": 184, "connections": { "0": [ { @@ -2219,9 +2189,9 @@ "id": "1731068754606", "component": "cloudmqttconnect", "tab": "1612772287426", - "name": "MQTT client - to senica-prod01", - "x": 304.75, - "y": 474, + "name": "MQTT to senica-prod01", + "x": 284.75, + "y": 452, "connections": { "1": [ { @@ -2257,14 +2227,20 @@ "component": "db_init", "tab": "1612772287426", "name": "DB Initialization", - "x": 91.75, - "y": 55.25, + "x": 1003.75, + "y": 240.25, "connections": { "0": [ { "index": "0", "id": "1731069033416" } + ], + "1": [ + { + "index": "0", + "id": "1747561603739" + } ] }, "disabledio": { @@ -2284,8 +2260,8 @@ "component": "virtualwireout", "tab": "1612772287426", "name": "db-init", - "x": 343.75, - "y": 50.25, + "x": 1244.75, + "y": 233.25, "connections": {}, "disabledio": { "input": [], @@ -2798,7 +2774,7 @@ "tab": "1611921777196", "name": "tempToDido", "x": 594.8833312988281, - "y": 745, + "y": 753, "connections": {}, "disabledio": { "input": [], @@ -2821,8 +2797,8 @@ "component": "virtualwirein", "tab": "1612772287426", "name": "db-init", - "x": 86.75, - "y": 495, + "x": 72.75, + "y": 474, "connections": { "0": [ { @@ -2844,6 +2820,48 @@ }, "color": "#303E4D", "notes": "" + }, + { + "id": "1747561603739", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "send-to-services", + "x": 1243.8833312988281, + "y": 334.5, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "options": { + "wirename": "send-to-services" + }, + "color": "#303E4D", + "notes": "" + }, + { + "id": "1747562867845", + "component": "comment", + "tab": "1612772287426", + "name": "FLOW STARTING POINT", + "x": 1003.5666656494141, + "y": 178, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#704cff", + "notes": "" } ], "version": 615 diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 2e23549..8d4f47a 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -58,8 +58,7 @@ const bitwise = require('bitwise'); const DataToTbHandler = require('./helper/DataToTbHandler'); let tbHandler; -const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler'); -const errorHandler = new ErrorToServiceHandler(); +const errorHandler = require('./helper/ErrorToServiceHandler'); let ws = null; let rsPort = null; @@ -741,7 +740,7 @@ exports.install = function(instance) { } - function getPins(controllerType, hasMainSwitch) { + function pinsForRvoStatus(controllerType, hasMainSwitch) { let pins = []; @@ -774,7 +773,7 @@ exports.install = function(instance) { if (status === "OK") { - let pinIndexes = getPins(controller_type, hasMainSwitch); + let pinIndexes = pinsForRvoStatus(controller_type, hasMainSwitch); for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { @@ -914,21 +913,25 @@ exports.install = function(instance) { else if (type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; - if (value === "open" && SETTINGS.maintenance_mode) { - sendNotification("switchLogic", rvoTbName, "door_opened", {}, "", SEND_TO.tb, instance, "rvo_door"); + let door = "door_main"; + if (type === "state_of_main_switch") door = "door_em"; + + if (value === "open") { + if (SETTINGS.maintenance_mode) { + sendNotification("switchLogic", rvoTbName, door + "_open", {}, "", SEND_TO.tb, instance, door); + } else { + sendNotification("switchLogic", rvoTbName, door + "_open_without_permission", {}, "", SEND_TO.tb, instance, door); + + // zapneme sirenu + // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition") + if (type === "door_condition") turnAlarm("on"); + } } - if (value === "open" && !SETTINGS.maintenance_mode) { - sendNotification("switchLogic", rvoTbName, "door_opened_without_permission", {}, "", SEND_TO.tb, instance, "rvo_door"); - - // zapneme sirenu - // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition") - if (type === "door_condition") turnAlarm("on"); - } if (value === "closed") { if (alarmStatus == "ON") turnAlarm("off"); - sendNotification("switchLogic", rvoTbName, "door_closed", {}, "", SEND_TO.tb, instance, "rvo_door"); + sendNotification("switchLogic", rvoTbName, door + "_close", {}, "", SEND_TO.tb, instance, door); } deviceStatus[type] = value; @@ -1011,34 +1014,6 @@ exports.install = function(instance) { instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "reload_relays", line: line, value: value, dataChanged: dataChanged }); reportLineStatus(line); - - //modify table relays - // dbRelays.modify({ contactor: newPinValue }).where("line", line).make(function(builder) { - // builder.callback(function(err, response) { - // if(!err) - // { - // let time = 0; - // if(value) time = 1000 * 10;//10 sekund - - // let dataChanged = false; - // if(relaysData[line].contactor != newPinValue) dataChanged = true; - // relaysData[line].contactor = newPinValue; // 0,1 - - // //ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles - // //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor - // setTimeout(function(){ - // instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); - // }, time); - - // reportLineStatus(line); - // } - // else - // { - // errLogger.error("modify table relays failed", err); - // } - - // }); - // }); } else if (type === "state_of_breaker") { diff --git a/flow/helper/ErrorToServiceHandler.js b/flow/helper/ErrorToServiceHandler.js index 110ea8b..2b27c32 100644 --- a/flow/helper/ErrorToServiceHandler.js +++ b/flow/helper/ErrorToServiceHandler.js @@ -1,126 +1,91 @@ const { MD5 } = require('./md5.js'); const { networkInterfaces } = require('os'); -class ErrorToServiceHandler -{ - constructor() { +class ErrorToServiceHandler { + constructor() { this.previousValues = {}; - this.projects_id = undefined; + this.project_id = undefined; const nets = networkInterfaces(); - this.ipAddresses = Object.create(null); // Or just '{}', an empty object + this.ipAddresses = {}; for (const name of Object.keys(nets)) { for (const net of nets[name]) { // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses if (net.family === 'IPv4' && !net.internal) { if (!this.ipAddresses[name]) { - this.ipAddresses[name] = []; + this.ipAddresses[name] = []; } this.ipAddresses[name].push(net.address); } } } - - //console.log(this.ipAddresses); - } - setProjectsId(projects_id) - { - this.projects_id = projects_id; + setProjectId(project_id) { + this.project_id = project_id; } - processMessage(message, seconds, message_type) - { - if(message_type == undefined) message_type = "error_message"; - if(Array.isArray(message)) message = message.join(', '); - - let key = MD5(message); - let timestamp = new Date().getTime(); - - //keep in memory - default value is 1h - if (seconds === undefined) seconds = 60*60; - - if(!this.previousValues.hasOwnProperty(key)) - { - this.previousValues[key] = {ts: timestamp, duration: seconds}; - } - - let diff = (timestamp - this.previousValues[key].ts); - if(diff < this.previousValues[key].duration*1000) return false; - - this.previousValues[key].ts = timestamp; - - return true; - } - - sendMessageToService(message, seconds, message_type) - { - - let f = this.processMessage(message, seconds, message_type); - if(!f) return; - - /* - //------------- - if(message_type == undefined) message_type = "error_message"; - if(Array.isArray(message)) message = message.join(', '); + processMessage(message, seconds) { + if (Array.isArray(message)) message = message.join(', '); let key = MD5(message); - let timestamp = new Date().getTime(); + let ts = Date.now(); - //keep in memory - if (seconds === undefined) seconds = 60*60; + //keep in memory - default value is 1h + if (seconds === undefined) seconds = 60 * 60; - if(!this.previousValues.hasOwnProperty(key)) - { - this.previousValues[key] = {ts: timestamp, duration: seconds}; + if (!this.previousValues.hasOwnProperty(key)) { + this.previousValues[key] = { ts: ts, duration: seconds }; } - let diff = (timestamp - this.previousValues[key].ts); - if(diff < this.previousValues[key].duration*1000) return; + let diff = (ts - this.previousValues[key].ts); + if (diff < this.previousValues[key].duration * 1000) return false; - this.previousValues[key].ts = timestamp; - */ + this.previousValues[key].ts = ts; - //------------------------- + return message; + } - //send to service - - let dataToInfoSender = {id: this.projects_id}; + sendMessageToService(message, seconds, message_type) { + + // if error occures too early FLOW.GLOBALS.settings.project_id is still undefined + if (this.project_id === undefined) { + console.log("ErrorToServiceHandler.js: no project_id"); + return; + } + + let f = this.processMessage(message, seconds); + if (f === false) return; + + if (message_type === undefined) message_type = "error_message"; + + let toService = { + id: this.project_id, + ipAddresses: this.ipAddresses + }; //js_error || error_message - dataToInfoSender[message_type] = message; - dataToInfoSender.ipAddresses = this.ipAddresses; + toService[message_type] = message; - console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender); - - //TODO UGLY!!! - // if error occures too early FLOW.GLOBALs.settings.project_id is still undefined - // if(this.projects_id === undefined) this.projects_id = FLOW.GLOBALS.settings.project_id; - if(this.projects_id === undefined) return; - - /* - if(this.projects_id === undefined) - { - console.log("this.projects_id is undefined"); - return; - } - */ + console.log("ErrorToServiceHandler------------------------>send to service", toService); RESTBuilder.make(function(builder) { - builder.method('POST'); - builder.post(dataToInfoSender); - builder.url('http://192.168.252.2:8004/sentmessage'); - - builder.callback(function(err, response, output) { - console.log("process.on error send", err, response, output, dataToInfoSender); - }); + builder.method('POST'); + builder.post(toService); + builder.url('http://192.168.252.2:8004/sentmessage'); + + builder.callback(function(err, response, output) { + console.log("process.on error send", err, response, output, toService); + }); }); - } } -module.exports = ErrorToServiceHandler; \ No newline at end of file +const errorHandler = new ErrorToServiceHandler(); + + +module.exports = errorHandler; +//module.exports = ErrorToServiceHandler; diff --git a/flow/helper/notification_reporter.js b/flow/helper/notification_reporter.js index 61c0aef..839c12c 100644 --- a/flow/helper/notification_reporter.js +++ b/flow/helper/notification_reporter.js @@ -1,6 +1,9 @@ -//key is device, value = str -let sentValues= {}; +//key is device, value = message {} +let sentValues = {}; let notificationsData = null; +let rvoName; + +//sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); let ERRWEIGHT = { EMERGENCY: "emergency", // System unusable @@ -23,82 +26,67 @@ var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]); function initNotification() { notificationsData = FLOW.GLOBALS.notificationsData; + rvoName = FLOW.GLOBALS.settings.rvo_name; } + function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) { - // return; - let storeToSendValues = true; - if(saveKey == undefined) storeToSendValues = false; + if (saveKey == undefined) storeToSendValues = false; - let lang = FLOW.GLOBALS.settings.language; - if(lang != "en" || lang != "sk") lang = "en"; - - let tpl = key; let weight = ""; + let message = {}; - if(notificationsData[key]) - { - weight = notificationsData[key].weight; - weight = weight.toLowerCase(); + let notification = notificationsData[key]; - tpl = notificationsData[key][lang]; - tpl = template(tpl, params); + if (notification) { + weight = notification.weight.toLowerCase(); + + Object.keys(notification).forEach(item => { + if (["en", "sk", "de", "cz", "it", "es"].includes(item)) { + message[item] = rvoName + ": " + template(notification[item], params); + } + }) } - else - { + else { //console.error("sendNotification: Notifications: undefined key", key, func, notificationsData); - console.error("sendNotification: Notifications: undefined key", key, func ); + console.error("sendNotification: Notifications: undefined key", key, func); return false; } //detect invalid err weight - if(getKey(ERRWEIGHT, weight) == undefined) - { + if (getKey(ERRWEIGHT, weight) == undefined) { console.error("sendNotification: Notifications: undefined weight", weight, key, func); return false; } - if(sentValues.hasOwnProperty(saveKey)) - { - if(sentValues[saveKey] == tpl) - { + if (sentValues.hasOwnProperty(saveKey)) { + if (JSON.stringify(sentValues[saveKey]) == JSON.stringify(message)) { return false; } } - if(sentValues[saveKey] == undefined) - { - if(storeToSendValues) - { + if (sentValues[saveKey] == undefined) { + if (storeToSendValues) { //do not send - flow is was started - sentValues[saveKey] = tpl; + sentValues[saveKey] = message; return false; } } - if(saveKey == "rvo_door") - { - //console.log("******", saveKey, sentValues[saveKey], tpl); - } - - if(storeToSendValues) sentValues[saveKey] = tpl; - - let str = FLOW.GLOBALS.settings.rvo_name; - if(str != "") str = str + ": "; - str = str + tpl; + if (storeToSendValues) sentValues[saveKey] = message; let content = { "type": weight, "status": "new", "source": { - "func":func, - "component":instance.id, - "component_name":instance.name, - "edge":device + "func": func, + "component": instance.id, + "component_name": instance.name, + "edge": device }, - "message":str, + "message": message, "message_data": extra }; @@ -107,7 +95,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance, { "ts": Date.now(), "values": { - "_event":content + "_event": content } } ]; @@ -118,6 +106,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance, } else { bufferError(msg); }*/ + instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections return true; @@ -129,3 +118,4 @@ module.exports = { ERRWEIGHT, initNotification } + diff --git a/flow/helper/serialport_helper.js b/flow/helper/serialport_helper.js index 8efa6af..7f5920d 100644 --- a/flow/helper/serialport_helper.js +++ b/flow/helper/serialport_helper.js @@ -1,6 +1,6 @@ const { exec } = require('child_process'); -function openPort(port){ +function openPort(port) { return new Promise((resolve, reject) => { var callbackError = function(err) { @@ -25,24 +25,24 @@ function openPort(port){ }) } -function runSyncExec(command){ +function runSyncExec(command) { return new Promise((resolve, reject) => { exec(command, (error, stdout, stderr) => { - if(error == null) resolve(stdout); + if (error == null) resolve(stdout); reject(error); }); }) } -async function writeData(port, data, readbytes, timeout){ +async function writeData(port, data, readbytes, timeout) { return new Promise((resolve, reject) => { // If first item in data array is 255, we just write broadcast command to rsPort // We wait 3 seconds and resolve(["broadcast"]) // It is important to resolve with array - if(data[0] == 255) { + if (data[0] == 255) { port.write(Buffer.from(data), function(err) { if (err) { @@ -55,13 +55,12 @@ async function writeData(port, data, readbytes, timeout){ } //cmd-manager mame http route POST / terminal a tomu sa tiez nastavuje timeout!!! - + var callback = function(data) { rsPortReceivedData.push(...data); let l = rsPortReceivedData.length; - if(l >= readbytes) - { + if (l >= readbytes) { port.removeListener('data', callback); clearTimeout(t); diff --git a/flow/infosender.js b/flow/infosender.js index c7afd83..587724f 100644 --- a/flow/infosender.js +++ b/flow/infosender.js @@ -33,69 +33,49 @@ exports.install = function(instance) { let ipAddresses = Object.create(null); // Or just '{}', an empty object for (const name of Object.keys(nets)) { - for (const net of nets[name]) { - // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses - if (net.family === 'IPv4' && !net.internal) { - if (!ipAddresses[name]) { - ipAddresses[name] = []; - } - ipAddresses[name].push(net.address); - } + for (const net of nets[name]) { + // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses + if (net.family === 'IPv4' && !net.internal) { + if (!ipAddresses[name]) { + ipAddresses[name] = []; + } + ipAddresses[name].push(net.address); } - } - - function sendValues() - { - if(!configured) return; - - if(Object.keys(allValues).length > 0) - { - if(id) - { - delete allValues.__force__; - let dataToSend = {...allValues}; - dataToSend.id = id; - dataToSend.ipAddresses = ipAddresses; - - instance.send(0, dataToSend); - - allValues = {}; - } - else - { - console.log(exports.title, "unable to send data, no id"); - } - } } - + + function sendValues() { + if (!configured) return; + + if (Object.keys(allValues).length > 0) { + let dataToSend = { ...allValues }; + dataToSend.id = id; + dataToSend.ipAddresses = ipAddresses; + + instance.send(0, dataToSend); + + allValues = {}; + } + } + instance.on("close", () => { clearInterval(sendAllValuesInterval); }) instance.on("0", _ => { id = FLOW.GLOBALS.settings.project_id; - configured = true; + if (id) configured = true; + else console.log(exports.title, "InfoSender: Unable to send data, no id"); }) instance.on("1", flowdata => { - - allValues = { ...allValues, ...flowdata.data}; + allValues = { ...allValues, ...flowdata.data }; //console.log("DATA RECEIVED", flowdata.data); - - //__force__ - if(flowdata.data.hasOwnProperty("__force__")) - { - if(flowdata.data.__force__) - { - sendValues(); - } - } }) sendAllValuesInterval = setInterval(() => { sendValues(); - }, 60000*3); - + }, 60000 * 3); + } diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index d383971..65eed75 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -332,8 +332,6 @@ exports.install = function(instance) { noVoltage = FLOW.GLOBALS.settings.no_voltage; mainSocket = new SocketWithClients(); - console.log("novoltage: ", noVoltage, typeof noVoltage); - // this notification is to show, that flow (unipi) has been restarted sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance); } diff --git a/flow/slack_filter.js b/flow/slack_filter.js index 753c24a..6b2d2a5 100644 --- a/flow/slack_filter.js +++ b/flow/slack_filter.js @@ -7,7 +7,7 @@ exports.output = 1; exports.author = 'Jakub Klena'; exports.icon = 'plug'; exports.version = '1.0.8'; -exports.options = { 'name':'', 'types': '["emergency", "critical", "error", "alert"]', 'message_includes':'["is responding again"]', 'tag_on_include':'[{"user_id":"U072JE5JUQG", "includes":["Electrometer", "Twilight sensor"]}]', 'slack_channel':'' }; +exports.options = { 'name': '', 'types': '["emergency", "critical", "error", "alert"]', 'message_includes': '["is responding again"]', 'tag_on_include': '[{"user_id":"U072JE5JUQG", "includes":["Electrometer", "Twilight sensor"]}]', 'slack_channel': '' }; exports.html = `
@@ -37,7 +37,7 @@ exports.install = function(instance) { if (!running) return; let value = response.data; if (typeof value !== 'object') return; - + let can = false var k = Object.keys(value); var interested = JSON.parse(instance.options.types); @@ -57,11 +57,11 @@ exports.install = function(instance) { let icon = ':totaljs:'; let type = value[k[0]][0]['values']['_event']['type']; let source = value[k[0]][0]['values']['_event']['source']['func']; - let message = value[k[0]][0]['values']['_event']['message']; + let message = value[k[0]][0]['values']['_event']['message']['en']; let message_data = value[k[0]][0]['values']['_event']['message_data']; let tag = ''; - switch(type){ + switch (type) { case 'debug': icon = ':beetle:'; break; @@ -89,15 +89,15 @@ exports.install = function(instance) { } // Check if this message includes one of the strings we are watching for - for (const msg of msg_incl){ - if (message.includes(msg)){ + for (const msg of msg_incl) { + if (message.includes(msg)) { if (msg == 'is responding again') icon = ':large_green_circle:'; can = true; break; } } // Check if message is one of the types we are watching for - if (interested.includes(type)){ + if (interested.includes(type)) { can = true; } @@ -105,10 +105,10 @@ exports.install = function(instance) { // Check for each person tags based on what the message includes - for (const person of tags){ - for (const msg of person.includes){ - if (message.includes(msg)){ - tag += '<@'+person.user_id+'> '; + for (const person of tags) { + for (const msg of person.includes) { + if (message.includes(msg)) { + tag += '<@' + person.user_id + '> '; break; // Break out from this person checks as they are already tagged now } } @@ -116,46 +116,46 @@ exports.install = function(instance) { // Now that all people are tagged add new line symbol if (tag != '') tag += '\n'; - let send_data = tag+instance.options.name+' '+type.toUpperCase()+'\n*Source*: '+source+'\n*Message*: '+message; + let send_data = tag + instance.options.name + ' ' + type.toUpperCase() + '\n*Source*: ' + source + '\n*Message*: ' + message; if (message_data) { - send_data += '\nData: '+message_data; + send_data += '\nData: ' + message_data; } let ignore_msg = false - if (message.includes('Configuration of dimming profile to node no')){ - for (let i = 0; i < FLOW["savedSlackMessages"].length; i++){ - if (FLOW["savedSlackMessages"][i].message == message){ + if (message.includes('Configuration of dimming profile to node no')) { + for (let i = 0; i < FLOW["savedSlackMessages"].length; i++) { + if (FLOW["savedSlackMessages"][i].message == message) { ignore_msg = true; break; } } - if (!ignore_msg){ - FLOW["savedSlackMessages"].push({message, 'dateandtime': Date.now()}); - if (timer === null){ - timer = setTimeout(checkSavedMessages, 60*60000); + if (!ignore_msg) { + FLOW["savedSlackMessages"].push({ message, 'dateandtime': Date.now() }); + if (timer === null) { + timer = setTimeout(checkSavedMessages, 60 * 60000); } } } - if (!ignore_msg){ - instance.send2({'msg':send_data,'bot_name':instance.options.name+' '+type.toUpperCase(),'bot_icon':icon,'channel':instance.options.slack_channel}); + if (!ignore_msg) { + instance.send2({ 'msg': send_data, 'bot_name': instance.options.name + ' ' + type.toUpperCase(), 'bot_icon': icon, 'channel': instance.options.slack_channel }); } }); - function checkSavedMessages(){ + function checkSavedMessages() { var d = Date.now(); d = d - 86400000; // older then 24hr var a = []; //Remove msgs older then 24hr - for (let i = 0; i < FLOW["savedSlackMessages"].length; i++){ - if (FLOW["savedSlackMessages"][i].dateandtime > d){ + for (let i = 0; i < FLOW["savedSlackMessages"].length; i++) { + if (FLOW["savedSlackMessages"][i].dateandtime > d) { a.push(FLOW["savedSlackMessages"][i]); } } FLOW["savedSlackMessages"] = a; if (FLOW["savedSlackMessages"].length > 0) { - timer = setTimeout(checkSavedMessages, 60*60000); + timer = setTimeout(checkSavedMessages, 60 * 60000); } else { timer = null; } @@ -163,7 +163,7 @@ exports.install = function(instance) { instance.reconfigure = function() { try { - if (!FLOW["savedSlackMessages"]){ + if (!FLOW["savedSlackMessages"]) { FLOW["savedSlackMessages"] = []; } @@ -183,5 +183,4 @@ exports.install = function(instance) { instance.on('options', instance.reconfigure); setTimeout(instance.reconfigure, 10000); - }; diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index b2a218d..0eba804 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -55,7 +55,7 @@ let saveTelemetryOnError = true;//backup_on_failure overrides this value //------------------------ let rollers; -if(createTelemetryBackup) rollers = require('streamroller'); +if (createTelemetryBackup) rollers = require('streamroller'); const noSqlFileSizeLimit = 4194304;//use 5MB - 4194304 let insertNoSqlCounter = 0; @@ -70,14 +70,14 @@ let lastRestoreTime = 0; // if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable let sendClientError = true; -process.on('uncaughtException', function (err) { +process.on('uncaughtException', function(err) { errLogger.error('uncaughtException:', err.message) errLogger.error(err.stack); //TODO //send to service - + //process.exit(1); }) @@ -96,22 +96,19 @@ exports.install = function(instance) { let sendWsStatusVar = null; let wsmqtt_status = 'disconnected'; - function getWsmqttName(host) - { - if(host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; - else if(host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; - else if(host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; + function getWsmqttName(host) { + if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; + else if (host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; + else if (host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; } - function sendWsStatus() - { - instance.send(SEND_TO.services, {[wsmqttName]: wsmqtt_status}); + function sendWsStatus() { + instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status }); } - function main() - { - if(!FLOW.dbLoaded) return; + function main() { + if (!FLOW.dbLoaded) return; loadSettings(); clearInterval(sendWsStatus); @@ -119,11 +116,9 @@ exports.install = function(instance) { } //set opts according to db settings - function loadSettings() - { + function loadSettings() { - if(instance.options.host !== "") - { + if (instance.options.host !== "") { //override settings from database var o = instance.options; opts = { @@ -139,13 +134,12 @@ exports.install = function(instance) { console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); } - else - { - + else { + const SETTINGS = FLOW.GLOBALS.settings; backup_on_failure = SETTINGS.backup_on_failure; saveTelemetryOnError = backup_on_failure; - + restore_from_backup = SETTINGS.restore_from_backup; restore_backup_wait = SETTINGS.restore_backup_wait; @@ -170,8 +164,7 @@ exports.install = function(instance) { connectToTbServer(); } - function connectToTbServer() - { + function connectToTbServer() { var url = "mqtt://" + opts.host + ":" + opts.port; console.log("MQTT URL: ", url); @@ -179,7 +172,7 @@ exports.install = function(instance) { client.on('connect', function() { instance.status("Connected", "green"); - monitor.info("MQTT client connected"); + //monitor.info("MQTT client connected"); sendClientError = true; clientReady = true; @@ -198,31 +191,31 @@ exports.install = function(instance) { TRY(function() { message = JSON.parse(message); - if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { - client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, {qos:1}); - instance.send(SEND_TO.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}}); + if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { + client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); + instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); } }, () => instance.debug('MQTT: Error parsing data', message)); } - instance.send(SEND_TO.rpcCall, {"topic":topic, "content":message }); + instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message }); }); client.on('close', function() { clientReady = false; wsmqtt_status = 'disconnected'; - + instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !"}); + instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); }); client.on('error', function(err) { - instance.status("Err: "+ err.code, "red"); - instance.send(SEND_TO.debug, {"message":"Client ERROR signal received !", "error":err, "opt":opts }); - if(sendClientError) { - monitor.info('MQTT client error', err); - sendClientError = false; + instance.status("Err: " + err.code, "red"); + instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); + if (sendClientError) { + monitor.info('MQTT client error', err); + sendClientError = false; } clientReady = false; wsmqtt_status = 'disconnected'; @@ -238,45 +231,39 @@ exports.install = function(instance) { instance.on('1', function(data) { - if(clientReady) - { - //do we have some data in backup file? if any, process data from database - if(saveTelemetryOnError) - { + if (clientReady) { + //do we have some data in backup file? if any, process data from database + if (saveTelemetryOnError) { //read telemetry data and send back to server - if(!processingData) processDataFromDatabase(); + if (!processingData) processDataFromDatabase(); } - + let stringifiedJson = JSON.stringify(data.data); - client.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1}); + client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 }); //backup telemetry - if(createTelemetryBackup) - { + if (createTelemetryBackup) { data.data.id = UID(); nosqlBackup.insert(data.data); insertBackupNoSqlCounter++; - if(insertBackupNoSqlCounter > 150) - { - let options = {compress: true}; + if (insertBackupNoSqlCounter > 150) { + let options = { compress: true }; let path = __dirname + "/../databases/backup/tbdata.nosql"; var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options); stream.write(""); stream.end(); insertBackupNoSqlCounter = 0; - } + } } - - } - else - { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); - instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); - if(saveTelemetryOnError) - { + } + else { + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); + + if (saveTelemetryOnError) { //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql makeBackupFromDbFile(); @@ -289,72 +276,63 @@ exports.install = function(instance) { instance.close = function(done) { - if(clientReady){ + if (clientReady) { client.end(); clearInterval(sendWsStatusVar); } }; - - function getDbBackupFileCounter(type) - { + + function getDbBackupFileCounter(type) { var files = fs.readdirSync(__dirname + "/../databases"); let counter = 0; - for(var i = 0; i < files.length; i++) - { + for (var i = 0; i < files.length; i++) { - if(files[i] == "tbdata.nosql") continue; + if (files[i] == "tbdata.nosql") continue; - if(files[i].endsWith(".nosql")) - { + if (files[i].endsWith(".nosql")) { let pos = files[i].indexOf("."); - if(pos > -1) - { + if (pos > -1) { let fileCounter = counter; let firstDigit = files[i].slice(0, pos); fileCounter = parseInt(firstDigit); - if(isNaN(fileCounter)) fileCounter = 0; + if (isNaN(fileCounter)) fileCounter = 0; //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); - - if(type == "max") - { - if(fileCounter > counter) - { + + if (type == "max") { + if (fileCounter > counter) { counter = fileCounter; } } - else if(type == "min") - { - if(counter == 0) counter = fileCounter; + else if (type == "min") { + if (counter == 0) counter = fileCounter; - if(fileCounter < counter) - { + if (fileCounter < counter) { counter = fileCounter; } } } } - + } - - if(type == "max") counter++; + + if (type == "max") counter++; return counter; } const makeBackupFromDbFile = async () => { - if(!saveTelemetryOnError) return; + if (!saveTelemetryOnError) return; //to avoid large file: tbdata.nosql //init value is 0! - if(insertNoSqlCounter > 0) - { + if (insertNoSqlCounter > 0) { --insertNoSqlCounter; return; } @@ -366,8 +344,7 @@ exports.install = function(instance) { var stats = fs.statSync(source); var fileSizeInBytes = stats.size; - if(fileSizeInBytes > noSqlFileSizeLimit) - { + if (fileSizeInBytes > noSqlFileSizeLimit) { let counter = 1; counter = getDbBackupFileCounter("max"); @@ -381,21 +358,20 @@ exports.install = function(instance) { //clear tbdata.nosql fs.writeFileSync(source, ""); fs.truncateSync(source, 0); - + } } const processDataFromDatabase = async () => { - - if(restore_from_backup <= 0) return; + + if (restore_from_backup <= 0) return; //calculate diff const now = new Date(); let currentTime = now.getTime(); let diff = currentTime - lastRestoreTime; - if( (diff / 1000) < restore_backup_wait) - { + if ((diff / 1000) < restore_backup_wait) { //console.log("*********restore_backup_wait", diff, restore_backup_wait); return; } @@ -409,60 +385,55 @@ exports.install = function(instance) { let dataBase = 'tbdata'; var nosql; - if(counter == 0) dataBase = 'tbdata'; + if (counter == 0) dataBase = 'tbdata'; else dataBase = counter + "." + 'tbdata'; nosql = NOSQL(dataBase); //select all data - use limit restore_from_backup let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); - - for(let i = 0; i < records.length; i++) - { - if(clientReady) { + + for (let i = 0; i < records.length; i++) { + if (clientReady) { let item = records[i]; let id = item.id; - if(id !== undefined) - { + if (id !== undefined) { //console.log("------------processDataFromDatabase - remove", id, dataBase, i); try { let message = JSON.parse(JSON.stringify(item)); delete message.id; - - client.publish("v1/gateway/telemetry", JSON.stringify(message), {qos:1}); - + + client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 }); + //remove from database await promisifyBuilder(nosql.remove().where("id", id)); - } catch(error) { + } catch (error) { //process error console.log("processDataFromDatabase", error); } } - + } - else - { + else { processingData = false; return; } } - if(records.length > 0) - { + if (records.length > 0) { //clean backup file - if(counter > 0) nosql.clean(); + if (counter > 0) nosql.clean(); } //no data in db, remove - if(records.length == 0) - { - if(counter > 0) nosql.drop(); + if (records.length == 0) { + if (counter > 0) nosql.drop(); } const d = new Date(); From 0c993f50b1c1b486b62747cea50d3418bb312025 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Thu, 12 Jun 2025 11:34:03 +0200 Subject: [PATCH 21/25] Repeat node request 3x; fix rsPort and ws connection after close --- databases/nodes.table | 2 +- flow/cmd_manager.js | 50 +++++++++++-------- flow/designer.json | 103 +++++++++++++++++++++++++--------------- flow/dido_controller.js | 62 +++++++++++++----------- flow/modbus_reader.js | 19 ++++---- flow/show_dbdata.js | 6 ++- 6 files changed, 144 insertions(+), 98 deletions(-) diff --git a/databases/nodes.table b/databases/nodes.table index 0efaed3..b2f13d9 100644 --- a/databases/nodes.table +++ b/databases/nodes.table @@ -1,2 +1,2 @@ node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number -+|638|rDbQ84xzwgdqEoPm3kbJQWk9anOZY1RXyBv2LVM6|3|{"intervals":[{"cct":3000,"value":20,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"05:30","start_time":"20:00"},{"cct":3000,"value":20,"end_time":"13:00","start_time":"05:30"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":-20,"dusk_astro_clock_offset":20,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|0|1725885127396|............................................................................................................................................................................................................................................................|................. ++|638|rDbQ84xzwgdqEoPm3kbJQWk9anOZY1RXyBv2LVM6|3|{"intervals":[{"cct":3000,"value":20,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"05:30","start_time":"20:00"},{"cct":3000,"value":20,"end_time":"13:00","start_time":"05:30"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":-20,"dusk_astro_clock_offset":20,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|0|1725885127396|............................................................................................................................................................................................................................................................ diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 18807f9..185214f 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -225,7 +225,9 @@ exports.install = function(instance) { params.priority = priority; } - params.addMinutesToTimestamp = 0;//repeat task if value is > 0, + params.addMinutesToTimestamp = 0;//repeat task if value is > 0 + // if node regular readout does not respond, we repeat request + params.repeatCounter = 0; //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" //params.info = ""; //params.debug = true; // will console.log params in writeData response @@ -642,7 +644,7 @@ exports.install = function(instance) { function reportOnlineNodeStatus(line) { //Po zapnutí línie broadcastovo aktualizovať predtým čas a o 3 sek neskor - status, brightness - logger.debug("--->reportOnlineNodeStatus for line", line); + logger.debug("Cmd-mngr: ----->reportOnlineNodeStatus for line", line); const d = new Date(); @@ -746,7 +748,7 @@ exports.install = function(instance) { function reportOfflineNodeStatus(line) { - logger.debug("--->reportOfflineNodeStatus for line", line); + logger.debug("Cmd-mngr: ----->reportOfflineNodeStatus for line", line); values = {}; values["dimming"] = 0;//brightness @@ -766,11 +768,10 @@ exports.install = function(instance) { let tbname = nodesData[node].tbname; let nodeStatus = nodesData[node].status; - //in case we have reported offline node status, we return (continue with next node) if (nodeStatus === "OFFLINE") return; - nodesData[node].node_status_before_offline = nodeStatus === true ? true : false; + nodesData[node].node_status_before_offline = nodeStatus; nodesData[node].status = "OFFLINE"; nodesData[node].readout = {}; @@ -795,7 +796,6 @@ exports.install = function(instance) { } - function detectIfResponseIsValid(bytes) { //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let type = "RESPONSE"; @@ -1492,7 +1492,7 @@ exports.install = function(instance) { let stop = true; - if (type == "cmd-terminal" || type == "cmd-master") stop = false; + if (type === "cmd-terminal" || type === "cmd-master") stop = false; if (stop) { interval = setInterval(runTasks, LONG_INTERVAL); return; @@ -1502,7 +1502,7 @@ exports.install = function(instance) { let contactorStatus = 1; if (relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; - if (line == 0 || contactorStatus == 0) { + if (line === 0 || contactorStatus === 0 || FLOW.deviceStatus.state_of_breaker[line] === "Off") { interval = setInterval(runTasks, LONG_INTERVAL); return; } @@ -1653,7 +1653,6 @@ exports.install = function(instance) { } else { - terminalCommandResponse(params, "ERROR", data); handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); @@ -1697,6 +1696,16 @@ exports.install = function(instance) { } + function repeatCommand(params) { + params.repeatCounter++; + if (params.repeatCounter > 0) console.log("repeated-------", params.address, params.register, params.repeatCounter); + if (params.repeatCounter < 4) { + params.timestamp = 0; + params.addMinutesToTimestamp = 0; + tasks.push(params); + } + } + function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb) { let node = params.address; @@ -1712,6 +1721,7 @@ exports.install = function(instance) { if (itIsNodeCommand) { values.comm_status = "NOK"; nodesData[node].readout.comm_status = "NOK"; + repeatCommand(params); } if (updateStatus) { @@ -1747,10 +1757,12 @@ exports.install = function(instance) { } + function sendNodesData() { Object.keys(nodesData).forEach(node => { if (nodesData[node]["status"] !== "OFFLINE") { sendTelemetry(nodesData[node].readout, nodesData[node].tbname); + nodesData[node].readout = {}; } }) } @@ -1763,13 +1775,10 @@ exports.install = function(instance) { */ function terminalCommandResponse(params, responseType, data = null, reason = "") { //success, error, failure - if (params.refFlowdataKey == undefined) { + if (params.refFlowdataKey === undefined) { //console.log("params.refFlowdataKey is undefined", params); return; } - else { - console.log("params.refFlowdataKey: ", params); - } let message = null; let type = null; @@ -1792,7 +1801,6 @@ exports.install = function(instance) { } logger.debug(message); - logger.debug(params); //make http response let responseObj = {} @@ -1851,6 +1859,11 @@ exports.install = function(instance) { function handleRsPort() { + if (rsPort) { + rsPort.removeAllListeners(); + rsPort = null; + } + //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI @@ -1875,16 +1888,13 @@ exports.install = function(instance) { }); rsPort.on('error', function(err) { - - //TODO report to service!!! - //errLogger.error(exports.title, "unable to open port", SETTINGS.serial_port, err.message); errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); - - instance.send(SEND_TO.debug, err.message); + monitor.info("Cmd_manager: Error on rsPort", err.message); }); rsPort.on("close", () => { - setTimeout(() => rsPort.open(), 1000); + monitor.info("Cmd_manager: rsPort closed, reconnecting..."); + setTimeout(handleRsPort, 1000); }); rsPort.open(); diff --git a/flow/designer.json b/flow/designer.json index 82dfe7f..7565777 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -449,8 +449,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{line: 3, command: \"turnOff\", force: true}" + "data": "{line: 3, command: \"turnOff\", force: true}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -501,8 +501,8 @@ "color": "gray" }, "options": { - "data": "profile_nodes", - "datatype": "string" + "datatype": "string", + "data": "profile_nodes" }, "color": "#F6BB42", "notes": "" @@ -712,8 +712,8 @@ "color": "gray" }, "options": { - "data": "{line: 1, command: \"turnOn\", force: true}", - "datatype": "object" + "datatype": "object", + "data": "{line: 1, command: \"turnOn\", force: true}" }, "color": "#F6BB42", "notes": "" @@ -842,8 +842,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{command: \"turnOnAlarm\"}" + "data": "{command: \"turnOnAlarm\"}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -872,8 +872,8 @@ "color": "gray" }, "options": { - "datatype": "object", - "data": "{command: \"turnOffAlarm\"}" + "data": "{command: \"turnOffAlarm\"}", + "datatype": "object" }, "color": "#F6BB42", "notes": "" @@ -1009,7 +1009,7 @@ "output": [] }, "state": { - "text": "843.78 MB / 985.68 MB", + "text": "840.05 MB / 985.68 MB", "color": "gray" }, "options": { @@ -1039,7 +1039,7 @@ "output": [] }, "state": { - "text": "5.68 GB / 7.26 GB", + "text": "5.78 GB / 7.26 GB", "color": "gray" }, "options": { @@ -1152,9 +1152,9 @@ "color": "gray" }, "options": { - "url": "http://192.168.252.2:8004/sentmessage", + "stringify": "json", "method": "POST", - "stringify": "json" + "url": "http://192.168.252.2:8004/sentmessage" }, "color": "#5D9CEC", "notes": "" @@ -1495,7 +1495,7 @@ "output": [] }, "state": { - "text": "2% / 86.44 MB", + "text": "2.4% / 74.33 MB", "color": "gray" }, "options": { @@ -2018,7 +2018,7 @@ "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "" + "name": "rvo_senica_33_10.0.0.127" }, "color": "#30E193", "notes": "" @@ -2047,9 +2047,9 @@ "color": "gray" }, "options": { - "url": "http://192.168.252.2:8004/slack", + "stringify": "json", "method": "POST", - "stringify": "json" + "url": "http://192.168.252.2:8004/slack" }, "color": "#5D9CEC", "notes": "" @@ -2104,8 +2104,8 @@ "color": "gray" }, "options": { - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", - "datatype": "object" + "datatype": "object", + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" }, "color": "#F6BB42", "notes": "" @@ -2282,8 +2282,8 @@ "component": "showdb", "tab": "1612772287426", "name": "Show db data", - "x": 1121.75, - "y": 814.25, + "x": 1186.75, + "y": 821.25, "connections": { "0": [ { @@ -2309,8 +2309,8 @@ "component": "debug", "tab": "1612772287426", "name": "dbData", - "x": 1315.75, - "y": 853.25, + "x": 1390.75, + "y": 870.25, "connections": {}, "disabledio": { "input": [], @@ -2333,8 +2333,8 @@ "component": "trigger", "tab": "1612772287426", "name": "settings", - "x": 911.75, - "y": 710.75, + "x": 934.75, + "y": 669.75, "connections": { "0": [ { @@ -2360,8 +2360,8 @@ "component": "trigger", "tab": "1612772287426", "name": "relaysData", - "x": 832.75, - "y": 775.75, + "x": 876.75, + "y": 733.75, "connections": { "0": [ { @@ -2387,8 +2387,8 @@ "component": "trigger", "tab": "1612772287426", "name": "nodesData", - "x": 747.75, - "y": 840.75, + "x": 852.75, + "y": 794.75, "connections": { "0": [ { @@ -2414,8 +2414,8 @@ "component": "trigger", "tab": "1612772287426", "name": "pinsData", - "x": 803.75, - "y": 899.75, + "x": 850.75, + "y": 861.75, "connections": { "0": [ { @@ -2441,8 +2441,8 @@ "component": "trigger", "tab": "1612772287426", "name": "sample data", - "x": 858.75, - "y": 959.75, + "x": 857.75, + "y": 928.75, "connections": { "0": [ { @@ -2613,8 +2613,8 @@ "component": "trigger", "tab": "1612772287426", "name": "monitor.txt", - "x": 915.75, - "y": 1017.75, + "x": 882.75, + "y": 991.75, "connections": { "0": [ { @@ -2640,8 +2640,8 @@ "component": "trigger", "tab": "1612772287426", "name": "err.txt", - "x": 966.75, - "y": 1080.75, + "x": 904.75, + "y": 1053.75, "connections": { "0": [ { @@ -2862,6 +2862,33 @@ "options": {}, "color": "#704cff", "notes": "" + }, + { + "id": "1749211698385", + "component": "trigger", + "tab": "1612772287426", + "name": "deviceStatus", + "x": 911.75, + "y": 1116.75, + "connections": { + "0": [ + { + "index": "7", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" } ], "version": 615 diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 8d4f47a..d16fb19 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -124,8 +124,8 @@ exports.install = function(instance) { }; */ - //status for calculating Statecodes - let deviceStatus = { //key is device name: temperature,.... + //status for calculating Statecodes-we make it global to see it from outside + FLOW.deviceStatus = { //key is device name: temperature,.... "state_of_main_switch": "Off", //Hlavny istic (alebo druhy dverovy kontakt) "rotary_switch_state": "Off", //Prevadzkovy "door_condition": "closed", //Dverový kontakt @@ -139,6 +139,7 @@ exports.install = function(instance) { "state_of_contactor": {}, //"Off",//Stykac "twilight_sensor": "OK" //lux sensor }; + let deviceStatus = FLOW.deviceStatus; function main() { @@ -211,18 +212,17 @@ exports.install = function(instance) { function handleRsPort() { + + if (rsPort) { + rsPort.removeAllListeners(); + rsPort = null; + } + //TODO build according to pins!!! //! rsPort to open are the same for lm and unipi and electromer ("/dev/ttymxc0") const setRSPortData = [0xAA, 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 1, 1, 1, 1, 0, 0, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0]; rsPort = new SerialPort("/dev/ttymxc0", { autoOpen: false }); - rsPort.on('error', function(err) { - logger.debug("rsPort opened error - failed", err.message); - instance.send(SEND_TO.debug, err.message); - - errorHandler.sendMessageToService(exports.title + " rsPort opened error - failed: " + err.message); - }) - rsPort.on('open', async function() { await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function(status) { @@ -283,8 +283,18 @@ exports.install = function(instance) { }); + rsPort.on('error', err => { + let message = "Dido: rsPort error: " + err.message; + logger.debug(message); + monitor.info(message); + errorHandler.sendMessageToService(message); + }) + rsPort.on("close", () => { - rsPort.close(); + let message = "Dido: rsPort closed - reconnecting ..."; + logger.debug(message); + monitor.info(message); + setTimeout(handleRsPort, 1000); }) rsPort.open(); @@ -293,6 +303,11 @@ exports.install = function(instance) { function handleWebSocket() { + if (ws) { + ws.removeAllListeners(); + ws = null; + } + //to keep websocket opened, we send request every 150 seconds let startRequests = null; @@ -356,23 +371,13 @@ exports.install = function(instance) { }) } - - ws.on('error', (err) => { - monitor.info('websocket error, reconnect') - instance.send(SEND_TO.debug, err.message); - clearInterval(startRequests); - ws = null; - setTimeout(handleWebSocket, 1000); + ws.on('error', err => { + logger.debug('Dido: websocket error', err); }) - ws.onclose = function() { - // connection closed, discard old websocket and create a new one in 5s - // stopRequests(); - monitor.info('websocket onclose, reconnect') + logger.debug('Dido: websocket onclose, reconnecting...') clearInterval(startRequests); - ws = null; - console.log("ws is null now, reconnecting..."); setTimeout(handleWebSocket, 1000); } } @@ -382,7 +387,6 @@ exports.install = function(instance) { if (ws) ws.close(); }) - function getPin(line) { //conversionTable let keys = Object.keys(pinsData); @@ -483,14 +487,14 @@ exports.install = function(instance) { if (!force) { if (relaysData[line].contactor == value) { instance.send(SEND_TO.debug, `line is already ${onOrOff} ` + line); - logger.debug(`turnLine: line is already ${onOrOff} `, line); + logger.debug(`Dido: turnLine: line is already ${onOrOff} `, line); return; } } // if(!rsPort.isOpen && !ws) if (!rsPort && !ws) { - errLogger.error("dido controller - port or websocket is not opened"); + errLogger.error("Dido - port or websocket is not opened"); return; } @@ -502,18 +506,18 @@ exports.install = function(instance) { rsPort.write(Buffer.from(arr), function(err) { if (err === undefined) { - monitor.info(`turnLine ${onOrOff} zapisal do rsPort-u`, line, pin, arr, info); + monitor.info(`Dido: turnLine ${onOrOff} zapisal do rsPort-u`, line, pin, arr, info); switchLogic(arr); } else { - monitor.info(`turnLine ${onOrOff} WRITE error`, err); + monitor.info(`Dido: turnLine ${onOrOff} WRITE error`, err); } }); } else if (ws) { //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method - monitor.info(`turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); + monitor.info(`Dido: turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value }; ws.send(JSON.stringify(cmd)); } diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index 65eed75..064f956 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -76,6 +76,11 @@ exports.install = function(instance) { let obj = this; + if (this.socket) { + this.socket.removeAllListeners(); + this.socket = null; + } + this.socket = new SerialPort("/dev/ttymxc0", { baudRate: 9600, }) @@ -86,15 +91,11 @@ exports.install = function(instance) { } this.socket.on('error', function(e) { - console.log('socket connection error', e); - if (e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') { - console.log(exports.title + ' Waiting 10 seconds before trying to connect again'); - setTimeout(obj.startSocket, 10000); - } + console.log('Modbus_reader: Socket connection error', e); //'ECONNREFUSED' or 'ECONNRESET' ?? }); this.socket.on('close', function() { - console.log('Socket connection closed ' + exports.title + ' Waiting 10 seconds before trying to connect again'); + console.log('Modbus_reader: Socket connection closed - Waiting 10 seconds before connecting again'); setTimeout(obj.startSocket, 10000); }); @@ -115,7 +116,8 @@ exports.install = function(instance) { this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number this.device = dev.device; //em340, twilight_sensor - if (this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval); + //if we just start to loop devices from the beginning, or there is just 1 device in config, we wait whole timeoutInterval + if (this.indexInDeviceConfig == 0 || deviceConfig.length === 1) setTimeout(this.readRegisters, this.timeoutInterval); else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES); } @@ -330,7 +332,8 @@ exports.install = function(instance) { phases = FLOW.GLOBALS.settings.phases; tbName = FLOW.GLOBALS.settings.rvoTbName; noVoltage = FLOW.GLOBALS.settings.no_voltage; - mainSocket = new SocketWithClients(); + if (deviceConfig.length) mainSocket = new SocketWithClients(); + else console.log("Modbus_reader: no modbus device in configuration"); // this notification is to show, that flow (unipi) has been restarted sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance); diff --git a/flow/show_dbdata.js b/flow/show_dbdata.js index 0e5cbcc..1e23ff7 100644 --- a/flow/show_dbdata.js +++ b/flow/show_dbdata.js @@ -4,7 +4,7 @@ exports.group = 'Worksys'; exports.color = '#888600'; exports.version = '1.0.2'; exports.icon = 'sign-out'; -exports.input = 7; +exports.input = 8; exports.output = 1; const { exec } = require('child_process'); @@ -38,10 +38,12 @@ exports.install = function(instance) { else instance.send(0, stdout); }) }) + instance.on("7", _ => { + instance.send(0, FLOW.deviceStatus); + }) }; - const rpcSwitchOffLine = { "topic": "v1/gateway/rpc", From 5540cee8ecdef1513aa31b318eab14a0365eb33d Mon Sep 17 00:00:00 2001 From: rasta5man Date: Mon, 7 Jul 2025 14:30:03 +0200 Subject: [PATCH 22/25] Node status offline without setTimeout; handel turn line logs according to total_energy.js --- databases/total_energy.js | 37 +++++++++ flow/cmd_manager.js | 169 ++++++++++++++------------------------ flow/db_init.js | 31 ++----- flow/modbus_reader.js | 9 +- 4 files changed, 110 insertions(+), 136 deletions(-) create mode 100644 databases/total_energy.js diff --git a/databases/total_energy.js b/databases/total_energy.js new file mode 100644 index 0000000..cc8c018 --- /dev/null +++ b/databases/total_energy.js @@ -0,0 +1,37 @@ +//key is rvo_number, value is max energy when lamps are on +const total_energy = { + 1: 580, + 2: 1100, + 3: 3700, + 4: 4100, + 7: 360, + 12: 1700, + 13: 5400, + 14: 440, + 15: 6100, + 16: 4800, + 20: 1600, + 21: 1000, + 22: 2600, + 23: 1000, + 25: 2600, + 33: 240, + 34: 4000, + 35: 2700, + 36: 820, + 37: 1400, + 35: 3500, + 39: 1170, + 41: 740, + 42: 660, + 43: 4900, + 45: 930, + 46: 700, + 47: 1100, + 48: 1500, + 50: 3200, + 55: 1000, + 56: 5500 +} + +module.exports = total_energy; diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 185214f..4ead96b 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -104,25 +104,29 @@ exports.install = function(instance) { let priorities = []; let minutes = 1; - priorities["0"] = minutes; - priorities["1"] = minutes; + priorities["1"] = minutes; // dimming + priorities["76"] = minutes; // power minutes = 5; - priorities["74"] = minutes; - priorities["75"] = minutes; - priorities["76"] = minutes; - priorities["77"] = minutes; - priorities["78"] = minutes; - priorities["79"] = minutes; + priorities["75"] = minutes; // current + priorities["79"] = minutes; // energy + priorities["87"] = minutes; // aktualny cas //priorities["84"] = minutes; minutes = 10; - priorities["87"] = minutes; - priorities["6"] = minutes; - priorities["7"] = minutes; - priorities["80"] = minutes; - priorities["8"] = minutes; - priorities["89"] = minutes; + priorities["74"] = minutes; // voltage + priorities["77"] = minutes; // power factor + priorities["78"] = minutes; // frequency + + minutes = 60; + priorities["0"] = minutes; // statecode + priorities["6"] = minutes; // dusk + priorities["7"] = minutes; // dawn + priorities["8"] = minutes; // profile + + minutes = 60 * 24; + priorities["89"] = minutes; // verzia fw + priorities["80"] = minutes; // lifetime //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; @@ -164,7 +168,7 @@ exports.install = function(instance) { tbHandler.setSender(exports.title); let now = new Date(); - console.log("CMD Manager installed", now.toLocaleString("sk-SK")); + console.log("Cmd-mngr installed", now.toLocaleString("sk-SK")); sunCalcResult = calculateDuskDawn(); @@ -263,7 +267,7 @@ exports.install = function(instance) { try { nodeProfile = JSON.parse(nodeProfile); } catch (error) { - logger.debug("Cmd_manager - Error parsing node profile", error); + logger.debug("Cmd-mngr: Error parsing node profile", error); } } @@ -679,7 +683,7 @@ exports.install = function(instance) { let status = "NOK"; // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. - if (nodesData[k].node_status_before_offline == true || nodesData[k].status === true) { + if (nodesData[k].node_status_before_offline === true || nodesData[k].status === true) { status = "OK"; nodesData[k].time_of_last_communication = time; } @@ -748,7 +752,8 @@ exports.install = function(instance) { function reportOfflineNodeStatus(line) { - logger.debug("Cmd-mngr: ----->reportOfflineNodeStatus for line", line); + + logger.info("Cmd-mngr: ------>reportOffLineNodeStatus for line ", line); values = {}; values["dimming"] = 0;//brightness @@ -758,28 +763,24 @@ exports.install = function(instance) { const date = Date.now(); - Object.keys(nodesData).forEach((node, index) => { + Object.keys(nodesData).forEach(node => { - setTimeout(function() { + //potrebujem nody k danej linii + if (line == nodesData[node].line || line == undefined) { - //potrebujem nody k danej linii - if (line == nodesData[node].line || line == undefined) { + let tbname = nodesData[node].tbname; + let nodeStatus = nodesData[node].status; - let tbname = nodesData[node].tbname; - let nodeStatus = nodesData[node].status; + //in case we have reported offline node status, we return (continue with next node) + if (nodeStatus === "OFFLINE") return; - //in case we have reported offline node status, we return (continue with next node) - if (nodeStatus === "OFFLINE") return; + nodesData[node].node_status_before_offline = nodeStatus; + nodesData[node].status = "OFFLINE"; + nodesData[node].readout = {}; - nodesData[node].node_status_before_offline = nodeStatus; - nodesData[node].status = "OFFLINE"; - nodesData[node].readout = {}; - - sendTelemetry({ ...values }, tbname, date); - } - - }, (index + 1) * 300); - }) + sendTelemetry({ ...values }, tbname, date); + } + }); } @@ -1034,7 +1035,7 @@ exports.install = function(instance) { } catch (error) { if (profilestr !== "") { //errLogger.error(profilestr, error); - console.log(`Cmd_manager: Unable to process line profile ${line}. Error: `, error); + console.log(`Cmd_mngr: Unable to process line profile ${line}. Error: `, error); errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); } else { turnLine("off", line, "No line profile. Switching it off on startup"); @@ -1321,7 +1322,7 @@ exports.install = function(instance) { if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { //reportovali sme? if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { - //sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); + //sendNotification("Cmd-mngr: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; } } @@ -1338,7 +1339,7 @@ exports.install = function(instance) { if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { //reportovali sme? if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { - //sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); + //sendNotification(": calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; } } @@ -1369,7 +1370,7 @@ exports.install = function(instance) { if (!rsPort.isOpen) { instance.send(SEND_TO.debug, "!rsPort.isOpen"); //await rsPort.open(); - //console.log("Cmd_manager - !rsPort.isOpen"); + //console.log("Cmd-mngr: !rsPort.isOpen"); } let currentTask = tasks[0]; @@ -1390,7 +1391,7 @@ exports.install = function(instance) { let type = params.type; let tbname = params.tbname; let node = params.address; - + let register = params.register; let line = null; let itIsNodeCommand; @@ -1453,39 +1454,6 @@ exports.install = function(instance) { return; } - //zhodeny hlavny istic - let disconnected = false; - //if(rotary_switch_state == "Off") disconnected = true; - - //state_of_breaker[line] - alebo istic linie - if (state_of_breaker.hasOwnProperty(line)) { - //if(state_of_breaker[line] == "Off") disconnected = true; - } - - //toto sa reportuje po prijati dat z dido_controlera - if (disconnected) { - let values = { "status": "OFFLINE" }; - - logger.debug("disconnected", values); - logger.debug("rotary_switch_state", rotary_switch_state); - logger.debug("state_of_breaker", state_of_breaker[line]); - - //report only once! - if (!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; - - if (!disconnectedReport[tbname]) { - sendTelemetry(values, tbname) - } - - interval = setInterval(runTasks, SHORT_INTERVAL); - - return; - } - - disconnectedReport[tbname] = false; - - const register = params.register; - if (!SETTINGS.masterNodeIsResponding) { //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal cmd-master errorHandler.sendMessageToService("Master node is not responding"); @@ -1508,25 +1476,8 @@ exports.install = function(instance) { } // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors - // - // let relayStatus = 1; - // if (relaysData[line] != undefined) { - // relayStatus = relaysData[line].contactor; - // } - - // if (line == 0) relayStatus = 0; - // if (type == "cmd-terminal") relayStatus = 1; - - // //check if rotary_switch_state == "Off" - // if (relayStatus == 0) { - // console.log("------------------------------------relayStatus", relayStatus, line); - // let values = { "status": "OFFLINE" }; - - // if(tbname) sendTelemetry(values, tbname) - - // interval = setInterval(runTasks, SHORT_INTERVAL); - // return; - // } + // check if rotary_switch_state == "Off" + // state_of_braker: disconnected = true? if (!rsPort.isOpen) { interval = setInterval(runTasks, LONG_INTERVAL); @@ -1578,6 +1529,9 @@ exports.install = function(instance) { // await keyword is important, otherwise incorrect data is returned! await writeData(rsPort, resp, readBytes, timeout).then(function(data) { + //sometimes happens, that status of node changes even if line was turned off and should be offline. To prevent this, we return if line contactor is 0: + if (itIsNodeCommand && line && relaysData[line].contactor !== 1) return; + endTime = new Date(); var timeDiff = endTime - startTime; @@ -1610,7 +1564,7 @@ exports.install = function(instance) { dbNodes.modify({ processed: true }).where("node", node).make(function(builder) { builder.callback(function(err, response) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); + sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); logger.debug("--> profil úspešne odoslaný na node č. " + node); nodesData[node].processed = true; @@ -1633,7 +1587,7 @@ exports.install = function(instance) { //master node if (node == 0) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); + sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); SETTINGS.masterNodeIsResponding = true; if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; } @@ -1698,7 +1652,6 @@ exports.install = function(instance) { function repeatCommand(params) { params.repeatCounter++; - if (params.repeatCounter > 0) console.log("repeated-------", params.address, params.register, params.repeatCounter); if (params.repeatCounter < 4) { params.timestamp = 0; params.addMinutesToTimestamp = 0; @@ -1733,7 +1686,7 @@ exports.install = function(instance) { //master node if (node == 0) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); + sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); logger.debug("master_node_is_not_responding", params); SETTINGS.masterNodeIsResponding = false; @@ -1745,7 +1698,7 @@ exports.install = function(instance) { logger.debug("profil nebol úspešne odoslaný na node č. ", params); if (!nodeProfileSendFail.has(node)) { - sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); + sendNotification("Cmd-mngr: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); nodeProfileSendFail.add(node); } } @@ -1875,7 +1828,7 @@ exports.install = function(instance) { rsPort.on('open', async function() { - logger.debug("CMD manager - rsPort opened success"); + logger.debug("Cmd-mngr: rsPort opened success"); await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); @@ -1883,17 +1836,17 @@ exports.install = function(instance) { logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); }).catch(function(reason) { - instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); + instance.send(SEND_TO.debug, "Cmd-mngr: RPC runSyncExec - promise rejected:" + reason); }); }); rsPort.on('error', function(err) { errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); - monitor.info("Cmd_manager: Error on rsPort", err.message); + monitor.info("Cmd-mngr: Error on rsPort", err.message); }); rsPort.on("close", () => { - monitor.info("Cmd_manager: rsPort closed, reconnecting..."); + monitor.info("Cmd-mngr: rsPort closed, reconnecting..."); setTimeout(handleRsPort, 1000); }); @@ -1969,13 +1922,13 @@ exports.install = function(instance) { if (cmd == "buildTasks") { clearInterval(interval); - logger.debug("-->CMD MANAGER - BUILD TASKS"); + logger.debug("-->Cmd-mngr: BUILD TASKS"); buildTasks(); //logger.debug("tasks:"); //logger.debug(tasks); - logger.debug("-->CMD MANAGER - RUN TASKS"); + logger.debug("-->Cmd-mngr: RUN TASKS"); interval = setInterval(runTasks, 5000); } else if (cmd == "reload_relays") { @@ -2049,8 +2002,8 @@ exports.install = function(instance) { if (relaysData.hasOwnProperty(line)) { let tbname = relaysData[line].tbname; - if (value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); - else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + if (value == "Off") sendNotification("Cmd-mngr: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + else sendNotification("Cmd-mngr: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); //report status liniu sendTelemetry({ status: status }, tbname) @@ -2248,7 +2201,7 @@ exports.install = function(instance) { if (profile === "") logger.debug("worksys - update node profile done - profile is empty"); //profil úspešne prijatý pre node č. xx - sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); + sendNotification("Cmd-mngr", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); nodesData[node].processed = false; nodesData[node].profile = profile; @@ -2302,7 +2255,7 @@ exports.install = function(instance) { buildTasks({ processLineProfiles: true, line: line }); - sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); + sendNotification("Cmd-mngr: set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); }); }); break; @@ -2345,7 +2298,7 @@ exports.install = function(instance) { let params = flowdata.data.body; if (params == undefined) { - //logger.debug("CMD manager flowdata.data.body is undefined"); + //logger.debug("Cmd-mngr: flowdata.data.body is undefined"); return; } diff --git a/flow/db_init.js b/flow/db_init.js index 2a5caf9..60a0510 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -6,33 +6,14 @@ exports.version = '1.0.2'; exports.icon = 'sign-out'; exports.output = 2; -exports.html = `
-
-
-
Hostname or IP address (if not empty - setting will override db setting)
-
-
-
Port
-
-
-
-
-
@(Client id)
-
-
-
@(Username)
-
-
-
`; - - exports.readme = ` -# DB initialization + # DB initialization `; const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const { initNotification } = require('./helper/notification_reporter'); const errorHandler = require('./helper/ErrorToServiceHandler'); +const total_energy = require('../databases/total_energy'); const SEND_TO = { db_init: 0, @@ -41,7 +22,6 @@ const SEND_TO = { exports.install = async function(instance) { - const dbNodes = TABLE("nodes"); const dbRelays = TABLE("relays"); const dbSettings = TABLE("settings"); @@ -70,7 +50,7 @@ exports.install = async function(instance) { Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {}) dbs.settings = { - edge_fw_version: "2025-04-24", //rok-mesiac-den + edge_fw_version: "2025-07-08", //rok-mesiac-den language: responseSettings[0]["lang"], rvo_name: responseSettings[0]["rvo_name"], project_id: responseSettings[0]["project_id"], @@ -98,6 +78,11 @@ exports.install = async function(instance) { maintenance_mode: false, } + + let rvo_number = responseSettings[0]["rvo_name"].match(/\D+(\d{1,2})_/)[1]; + dbs.settings.energy_to_switch_lamps = total_energy[rvo_number]; + if (dbs.settings.energy_to_switch_lamps === undefined) console.log('=============== db_init.js: energy_to_switch_lamps is undefined'); + FLOW.dbLoaded = true; errorHandler.setProjectId(dbs.settings.project_id); initNotification(); diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index 064f956..d6d9dad 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -36,6 +36,7 @@ let mainSocket; let phases; //phases where voltage is 0 (set) let noVoltage; +let energyToSwitchLamps; exports.install = function(instance) { @@ -306,15 +307,12 @@ exports.install = function(instance) { const actualTotalPower = values.total_power; - const numberOfNodes = Object.keys(FLOW.GLOBALS.nodesData).length; - if (numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300 - - if (actualTotalPower > numberOfNodes * 15 && this.onNotificationSent == false) { + if (actualTotalPower > energyToSwitchLamps && this.onNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance); this.onNotificationSent = true; this.offNotificationSent = false; } - else if (actualTotalPower <= numberOfNodes * 15 && this.offNotificationSent == false) { + else if (actualTotalPower <= energyToSwitchLamps && this.offNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance); this.onNotificationSent = false; this.offNotificationSent = true; @@ -332,6 +330,7 @@ exports.install = function(instance) { phases = FLOW.GLOBALS.settings.phases; tbName = FLOW.GLOBALS.settings.rvoTbName; noVoltage = FLOW.GLOBALS.settings.no_voltage; + energyToSwitchLamps = FLOW.GLOBALS.settings.energy_to_switch_lamps / 2.5; //half value is enought to show if lamps are turned on or off if (deviceConfig.length) mainSocket = new SocketWithClients(); else console.log("Modbus_reader: no modbus device in configuration"); From 1012544193ffbd4dce5904c8277237b57e0f42f1 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Wed, 6 Aug 2025 12:00:17 +0200 Subject: [PATCH 23/25] Power factor new calculation; new bytesToInt function with uint and bigendian --- flow/cmd_manager.js | 5 +- flow/helper/utils.js | 236 ++++++++++++++++++++----------------------- 2 files changed, 114 insertions(+), 127 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 4ead96b..07e12c6 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -2616,7 +2616,7 @@ exports.install = function(instance) { //účinník else if (register == 77) { - let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); + let power_factor = Math.cos(bytesToInt(bytes) * 0.1 * (Math.PI / 180)).toFixed(2); values["power_factor"] = Number(power_factor); } @@ -2629,8 +2629,7 @@ exports.install = function(instance) { //energia else if (register == 79) { let energy = bytesToInt(bytes); - //Energiu treba reportovať v kWh -> delit 1000 - values["energy"] = energy / 1000; + values["energy"] = energy / 1000; //energia v kWh -> delit 1000 } //doba života diff --git a/flow/helper/utils.js b/flow/helper/utils.js index a16210c..9c054aa 100644 --- a/flow/helper/utils.js +++ b/flow/helper/utils.js @@ -1,124 +1,112 @@ -function bytesToInt(bytes, numberOfBytes) -{ - let buffer = []; - if(Array.isArray(bytes)) - { - buffer = bytes.slice(0); - if(numberOfBytes != undefined) - { - buffer = bytes.slice(bytes.length - numberOfBytes); - } - } - else buffer.push(bytes); - - //var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; - let l = (buffer.length - 1) * 8; - - let decimal = 0; - for(let i = 0; i < buffer.length; i++) - { - var s = buffer[i] << l; - if(l < 8) s = buffer[i] - decimal = decimal + s; - - l = l - 8; - - } - - return decimal; -} - -function resizeArray(arr, newSize, defaultValue) { - while(newSize > arr.length) - arr.push(defaultValue); - arr.length = newSize; -} - -longToByteArray = function(/*long*/long) { - // we want to represent the input as a 8-bytes array - var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; - - for ( var index = 0; index < byteArray.length; index ++ ) { - var byte = long & 0xff; - byteArray [ index ] = byte; - long = (long - byte) / 256 ; - } - - return byteArray; - }; - -function addDays(date, days) { - var result = new Date(date); - result.setDate(result.getDate() + days); - return result; -} - -/* -sleep(2000).then(() => { - // Do something after the sleep! - - -}); -*/ - -function sleep (time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - -function isEmptyObject( obj ) { - for ( var name in obj ) { - return false; - } - return true; -} - -function convertUTCDateToLocalDate(date) { - var newDate = new Date(date); - newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset()); - return newDate; -} - -function addZeroBefore(n) { - return (n < 10 ? '0' : '') + n; -} - -var convertBase = function () { - - function convertBase(baseFrom, baseTo) { - return function (num) { - return parseInt(num, baseFrom).toString(baseTo); - - }; - } - - // binary to decimal - convertBase.bin2dec = convertBase(2, 10); - - // binary to hexadecimal - convertBase.bin2hex = convertBase(2, 16); - - // decimal to binary - convertBase.dec2bin = convertBase(10, 2); - - // decimal to hexadecimal - convertBase.dec2hex = convertBase(10, 16); - - // hexadecimal to binary - convertBase.hex2bin = convertBase(16, 2); - - // hexadecimal to decimal - convertBase.hex2dec = convertBase(16, 10); - - return convertBase; - }(); - -module.exports = { - bytesToInt, - longToByteArray, - addDays, - addZeroBefore, - resizeArray, - isEmptyObject, - sleep, - convertUTCDateToLocalDate -} \ No newline at end of file +function bytesToInt(bytes, numberOfBytes) { + let buffer = []; + if (Array.isArray(bytes)) { + buffer = bytes.slice(0); + if (numberOfBytes != undefined) { + buffer = bytes.slice(bytes.length - numberOfBytes); + } + } + else buffer.push(bytes); + + let result = 0; + for (let i = 0; i < buffer.length; i++) { + result = (result << 8) | buffer[i]; + } + + return result >>> 0; //ensure it's an unsigned 32-bit number +} + +function resizeArray(arr, newSize, defaultValue) { + while (newSize > arr.length) + arr.push(defaultValue); + arr.length = newSize; +} + +longToByteArray = function(/*long*/long) { + // we want to represent the input as a 8-bytes array + var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (var index = 0; index < byteArray.length; index++) { + var byte = long & 0xff; + byteArray[index] = byte; + long = (long - byte) / 256; + } + + return byteArray; +}; + +function addDays(date, days) { + var result = new Date(date); + result.setDate(result.getDate() + days); + return result; +} + +/* +sleep(2000).then(() => { + // Do something after the sleep! + + +}); +*/ + +function sleep(time) { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +function isEmptyObject(obj) { + for (var name in obj) { + return false; + } + return true; +} + +function convertUTCDateToLocalDate(date) { + var newDate = new Date(date); + newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset()); + return newDate; +} + +function addZeroBefore(n) { + return (n < 10 ? '0' : '') + n; +} + +var convertBase = function() { + + function convertBase(baseFrom, baseTo) { + return function(num) { + return parseInt(num, baseFrom).toString(baseTo); + + }; + } + + // binary to decimal + convertBase.bin2dec = convertBase(2, 10); + + // binary to hexadecimal + convertBase.bin2hex = convertBase(2, 16); + + // decimal to binary + convertBase.dec2bin = convertBase(10, 2); + + // decimal to hexadecimal + convertBase.dec2hex = convertBase(10, 16); + + // hexadecimal to binary + convertBase.hex2bin = convertBase(16, 2); + + // hexadecimal to decimal + convertBase.hex2dec = convertBase(16, 10); + + return convertBase; +}(); + +module.exports = { + bytesToInt, + longToByteArray, + addDays, + addZeroBefore, + resizeArray, + isEmptyObject, + sleep, + convertUTCDateToLocalDate +} From cf16481324c7b20fb9d273a4b692715a6daf3955 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Fri, 8 Aug 2025 16:29:39 +0200 Subject: [PATCH 24/25] version 2025-08-08; refactoring --- flow/cmd_manager.js | 3 ++- flow/db_init.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index 07e12c6..e92d48f 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -1529,7 +1529,7 @@ exports.install = function(instance) { // await keyword is important, otherwise incorrect data is returned! await writeData(rsPort, resp, readBytes, timeout).then(function(data) { - //sometimes happens, that status of node changes even if line was turned off and should be offline. To prevent this, we return if line contactor is 0: + //sometimes happens, that status of node changes to OK, NOK even if line was turned off and should be status OFFLINE. To prevent this, we return if line contactor is 0: if (itIsNodeCommand && line && relaysData[line].contactor !== 1) return; endTime = new Date(); @@ -1650,6 +1650,7 @@ exports.install = function(instance) { } + // if node does not respond to request, we repeat request 3 times: function repeatCommand(params) { params.repeatCounter++; if (params.repeatCounter < 4) { diff --git a/flow/db_init.js b/flow/db_init.js index 60a0510..a759465 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -50,7 +50,7 @@ exports.install = async function(instance) { Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {}) dbs.settings = { - edge_fw_version: "2025-07-08", //rok-mesiac-den + edge_fw_version: "2025-08-08", //rok-mesiac-den language: responseSettings[0]["lang"], rvo_name: responseSettings[0]["rvo_name"], project_id: responseSettings[0]["project_id"], From 06b289d7a341e5f0cf28aed8393ec96dc55e73b9 Mon Sep 17 00:00:00 2001 From: rasta5man Date: Fri, 8 Aug 2025 16:53:33 +0200 Subject: [PATCH 25/25] Initial commit - testing version --- flow/cloudmqttconnect.js | 604 ++-- flow/cmd_manager.js | 5135 ++++++++++++++++---------------- flow/db_init.js | 31 +- flow/designer.json | 634 ++-- flow/designer.json_orig | 2775 +++++++++++++++++ flow/dido_controller.js | 72 +- flow/helper/DataToTbHandler.js | 280 +- flow/helper/utils.js | 282 +- flow/modbus_reader.js | 36 +- flow/variables.txt | 0 flow/wsmqttpublish.js | 606 ++-- 11 files changed, 6636 insertions(+), 3819 deletions(-) create mode 100644 flow/designer.json_orig create mode 100644 flow/variables.txt diff --git a/flow/cloudmqttconnect.js b/flow/cloudmqttconnect.js index d619784..1fc0e09 100644 --- a/flow/cloudmqttconnect.js +++ b/flow/cloudmqttconnect.js @@ -9,10 +9,10 @@ exports.output = 2; exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" }; exports.html = `
-
-
-
Hostname or IP address (if not empty - setting will override db setting)
-
+
+
+
Hostname or IP address (if not empty - setting will override db setting)
+
Port
@@ -27,16 +27,14 @@ exports.html = `
`; - - const { promisifyBuilder } = require('./helper/db_helper'); const fs = require('fs'); const mqtt = require('mqtt'); const nosql = NOSQL('tbdatacloud'); const SEND_TO = { - debug: 0, - rpcCall: 1, + debug: 0, + rpcCall: 1, } //CONFIG @@ -56,319 +54,295 @@ let lastRestoreTime = 0; // if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable let sendClientError = true; - exports.install = function(instance) { - var client; - var opts; - var clientReady = false; - - let o = null; //options - - function main() - { - loadSettings(); - } - - //set opts according to db settings - function loadSettings() - { - - o = instance.options; - if(!o.topic) o.topic = FLOW.GLOBALS.settings.cloud_topic; - - opts = { - host: o.host, - port: o.port, - clientId: o.clientid, - username: o.username, - rejectUnauthorized: false, - resubscribe: false - }; - - console.log("wsmqttpublich -> loadSettings from instance.options",o); - - connectToTbServer(); - } - - function connectToTbServer() - { - var url = "mqtt://" + opts.host + ":" + opts.port; - console.log("MQTT URL: ", url); - - client = mqtt.connect(url, opts); - - client.on('connect', function() { - client.subscribe(`${o.topic}_backward`, (err) => { - if (!err) { - console.log("MQTT subscribed"); - } - }); - instance.status("Connected", "green"); - clientReady = true; - sendClientError = true; - }); - - client.on('reconnect', function() { - instance.status("Reconnecting", "yellow"); - clientReady = false; - }); - - client.on('message', function(topic, message) { - // message is type of buffer - message = message.toString(); - if (message[0] === '{') { - TRY(function() { - - message = JSON.parse(message); - if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { - client.publish(`${o.topic}_forward`, `{"device": "${message.device}", "id": ${message.data.id}, "data": {"success": true}}`, {qos:1}); - instance.send(SEND_TO.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}}); - } - - }, () => instance.debug('MQTT: Error parsing data', message)); - } - - instance.send(SEND_TO.rpcCall, {"topic":o.topic, "content":message }); - }); - - client.on('close', function() { - clientReady = false; - - instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !"}); - }); - - client.on('error', function(err) { - instance.status("Err: "+ err.code, "red"); - instance.send(SEND_TO.debug, {"message":"Client ERROR signal received !", "error":err, "opt":opts }); - if(sendClientError) { - console.log('MQTT client error', err); - sendClientError = false; - } - clientReady = false; - }); - - } - - - instance.on('0', function(data) { - - if(clientReady) - { - //do we have some data in backup file? if any, process data from database - if(saveTelemetryOnError) - { - //read telemetry data and send back to server - if(!processingData) processDataFromDatabase(); - } - - let stringifiedJson = JSON.stringify(data.data) - client.publish(`${o.topic}_forward`, stringifiedJson, {qos: 1}); - } - else - { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); - instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); - - if(saveTelemetryOnError) - { - //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql - makeBackupFromDbFile(); - - //write to tb - data.data.id = UID(); - nosql.insert(data.data); - } - } - }); - - instance.on("1", _ => { - main(); - }) - - instance.close = function(done) { - if(clientReady){ - client.end(); - } - }; - - - function getDbBackupFileCounter(type) - { - var files = fs.readdirSync(__dirname + "/../databases"); - - let counter = 0; - for(var i = 0; i < files.length; i++) - { - - if(files[i] == "tbdatacloud.nosql") continue; - - if(files[i].endsWith(".nosql")) - { - - let pos = files[i].indexOf("."); - if(pos > -1) - { - - let fileCounter = counter; - let firstDigit = files[i].slice(0, pos); - - fileCounter = parseInt(firstDigit); - if(isNaN(fileCounter)) fileCounter = 0; - //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); - - if(type == "max") - { - if(fileCounter > counter) - { - counter = fileCounter; - } - } - else if(type == "min") - { - if(counter == 0) counter = fileCounter; - - if(fileCounter < counter) - { - counter = fileCounter; - } - } - } - } - - } - - if(type == "max") counter++; - - return counter; - } - - const makeBackupFromDbFile = async () => { - - if(!saveTelemetryOnError) return; - - //to avoid large file: tbdata.nosql - - //init value is 0! - if(insertNoSqlCounter > 0) - { - --insertNoSqlCounter; - return; - } - - insertNoSqlCounter = 100; - - let source = __dirname + "/../databases/tbdatacloud.nosql"; - - var stats = fs.statSync(source); - var fileSizeInBytes = stats.size; - - if(fileSizeInBytes > noSqlFileSizeLimit) - { - - let counter = 1; - counter = getDbBackupFileCounter("max"); - - let destination = __dirname + "/../databases/" + counter + "." + "tbdatacloud.nosql"; - - //make backup file - fs.copyFileSync(source, destination); - //fs.renameSync(p, p + "." + counter); - - //clear tbdata.nosql - fs.writeFileSync(source, ""); - fs.truncateSync(source, 0); - - } - } - - const processDataFromDatabase = async () => { - - if(restore_from_backup <= 0) return; - - //calculate diff - const now = new Date(); - let currentTime = now.getTime(); - let diff = currentTime - lastRestoreTime; - - if( (diff / 1000) < restore_backup_wait) - { - //console.log("*********restore_backup_wait", diff, restore_backup_wait); - return; - } - - processingData = true; - - //get filename to process - let counter = getDbBackupFileCounter("min"); - - //we have some backup files - let dataBase = 'tbdata'; - - var nosql; - if(counter == 0) dataBase = 'tbdatacloud'; - else dataBase = counter + "." + 'tbdatacloud'; - - nosql = NOSQL(dataBase); - - //select all data - use limit restore_from_backup - let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); - - for(let i = 0; i < records.length; i++) - { - if(clientReady) { - - let item = records[i]; - let id = item.id; - - if(id !== undefined) - { - //console.log("------------processDataFromDatabase - remove", id, dataBase, i); - - try { + var client; + var opts; + var clientReady = false; + + let o = null; //options + + function main() { + loadSettings(); + } + + //set opts according to db settings + function loadSettings() { + + o = instance.options; + if (!o.topic) o.topic = FLOW.GLOBALS.settings.cloud_topic; + + opts = { + host: o.host, + port: o.port, + clientId: o.clientid, + username: o.username, + rejectUnauthorized: false, + resubscribe: false + }; + + console.log("wsmqttpublich -> loadSettings from instance.options", o); + + connectToTbServer(); + } + + function connectToTbServer() { + var url = "mqtt://" + opts.host + ":" + opts.port; + console.log("MQTT URL: ", url); + + client = mqtt.connect(url, opts); + + client.on('connect', function() { + client.subscribe(`${o.topic}_backward`, (err) => { + if (!err) { + console.log("MQTT subscribed"); + } + }); + instance.status("Connected", "green"); + clientReady = true; + sendClientError = true; + }); + + client.on('reconnect', function() { + instance.status("Reconnecting", "yellow"); + clientReady = false; + }); + + client.on('message', function(topic, message) { + // message is type of buffer + message = message.toString(); + if (message[0] === '{') { + + + try { + message = JSON.parse(message); + if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { + client.publish(`${o.topic}_forward`, `{"device": "${message.device}", "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); + instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); + } + } catch (e) { instance.debug('MQTT: Error parsing data', message) } + + instance.send(SEND_TO.rpcCall, { "topic": o.topic, "content": message }); + } + }); + + client.on('close', function() { + clientReady = false; + + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); + }); - let message = JSON.parse(JSON.stringify(item)); - delete message.id; - client.publish(`${o.topic}_forward`, JSON.stringify(message), {qos:1}); - - //remove from database - await promisifyBuilder(nosql.remove().where("id", id)); + client.on('error', function(err) { + instance.status("Err: " + err.code, "red"); + instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); + if (sendClientError) { + console.log('MQTT client error', err); + sendClientError = false; + } + clientReady = false; + }); - } catch(error) { - //process error - console.log("processDataFromDatabase", error); - } + } - } - - } - else - { - processingData = false; - return; - } - } - if(records.length > 0) - { - //clean backup file - if(counter > 0) nosql.clean(); - } + instance.on('0', function(data) { - //no data in db, remove - if(records.length == 0) - { - if(counter > 0) nosql.drop(); - } - - const d = new Date(); - lastRestoreTime = d.getTime(); - - processingData = false; - - } - - instance.on('options', main); + if (clientReady) { + //do we have some data in backup file? if any, process data from database + if (saveTelemetryOnError) { + //read telemetry data and send back to server + if (!processingData) processDataFromDatabase(); + } + + let stringifiedJson = JSON.stringify(data.data) + client.publish(`${o.topic}_forward`, stringifiedJson, { qos: 1 }); + } + else { + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); + + if (saveTelemetryOnError) { + //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql + makeBackupFromDbFile(); + + //write to tb + data.data.id = UID(); + nosql.insert(data.data); + } + } + }); + + instance.on("1", _ => { + main(); + }) + + instance.close = function(done) { + if (clientReady) { + client.end(); + } + }; + + + function getDbBackupFileCounter(type) { + var files = fs.readdirSync(__dirname + "/../databases"); + + let counter = 0; + for (var i = 0; i < files.length; i++) { + + if (files[i] == "tbdatacloud.nosql") continue; + + if (files[i].endsWith(".nosql")) { + + let pos = files[i].indexOf("."); + if (pos > -1) { + + let fileCounter = counter; + let firstDigit = files[i].slice(0, pos); + + fileCounter = parseInt(firstDigit); + if (isNaN(fileCounter)) fileCounter = 0; + //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); + + if (type == "max") { + if (fileCounter > counter) { + counter = fileCounter; + } + } + else if (type == "min") { + if (counter == 0) counter = fileCounter; + + if (fileCounter < counter) { + counter = fileCounter; + } + } + } + } + + } + + if (type == "max") counter++; + + return counter; + } + + const makeBackupFromDbFile = async () => { + + if (!saveTelemetryOnError) return; + + //to avoid large file: tbdata.nosql + + //init value is 0! + if (insertNoSqlCounter > 0) { + --insertNoSqlCounter; + return; + } + + insertNoSqlCounter = 100; + + let source = __dirname + "/../databases/tbdatacloud.nosql"; + + var stats = fs.statSync(source); + var fileSizeInBytes = stats.size; + + if (fileSizeInBytes > noSqlFileSizeLimit) { + + let counter = 1; + counter = getDbBackupFileCounter("max"); + + let destination = __dirname + "/../databases/" + counter + "." + "tbdatacloud.nosql"; + + //make backup file + fs.copyFileSync(source, destination); + //fs.renameSync(p, p + "." + counter); + + //clear tbdata.nosql + fs.writeFileSync(source, ""); + fs.truncateSync(source, 0); + + } + } + + const processDataFromDatabase = async () => { + + if (restore_from_backup <= 0) return; + + //calculate diff + const now = new Date(); + let currentTime = now.getTime(); + let diff = currentTime - lastRestoreTime; + + if ((diff / 1000) < restore_backup_wait) { + //console.log("*********restore_backup_wait", diff, restore_backup_wait); + return; + } + + processingData = true; + + //get filename to process + let counter = getDbBackupFileCounter("min"); + + //we have some backup files + let dataBase = 'tbdata'; + + var nosql; + if (counter == 0) dataBase = 'tbdatacloud'; + else dataBase = counter + "." + 'tbdatacloud'; + + nosql = NOSQL(dataBase); + + //select all data - use limit restore_from_backup + let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); + + for (let i = 0; i < records.length; i++) { + if (clientReady) { + + let item = records[i]; + let id = item.id; + + if (id !== undefined) { + //console.log("------------processDataFromDatabase - remove", id, dataBase, i); + + try { + + let message = JSON.parse(JSON.stringify(item)); + delete message.id; + client.publish(`${o.topic}_forward`, JSON.stringify(message), { qos: 1 }); + + //remove from database + await promisifyBuilder(nosql.remove().where("id", id)); + + } catch (error) { + //process error + console.log("processDataFromDatabase", error); + } + + } + + } + else { + processingData = false; + return; + } + } + + if (records.length > 0) { + //clean backup file + if (counter > 0) nosql.clean(); + } + + //no data in db, remove + if (records.length == 0) { + if (counter > 0) nosql.drop(); + } + + const d = new Date(); + lastRestoreTime = d.getTime(); + + processingData = false; + + } + + instance.on('options', main); }; diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index e92d48f..d5f0842 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -30,2770 +30,2765 @@ exports.readme = `Manager for CMD calls`; exports.install = function(instance) { - const SerialPort = require('serialport'); - const { exec } = require('child_process'); - const { crc16 } = require('easy-crc'); - const { runSyncExec, writeData } = require('./helper/serialport_helper'); - const { bytesToInt, longToByteArray, addZeroBefore } = require('./helper/utils'); - const bitwise = require('bitwise'); - - var SunCalc = require('./helper/suncalc'); - const DataToTbHandler = require('./helper/DataToTbHandler'); - const errorHandler = require('./helper/ErrorToServiceHandler'); - const { sendNotification } = require('./helper/notification_reporter'); - const process = require('process'); - const { errLogger, logger, monitor } = require('./helper/logger'); - - //for accelerometer purposes - const { naklony } = require("../databases/accelerometer_db"); - - const dbNodes = TABLE("nodes"); - const dbRelays = TABLE("relays"); - - let GLOBALS; - let SETTINGS; - let rsPort; - let tbHandler; - - // runTasks intervals - const SHORT_INTERVAL = 30; - const LONG_INTERVAL = 300; - - //send data to following instances: - const SEND_TO = { - debug: 0, - tb: 1, - http_response: 2, - dido_controller: 3, - infoSender: 4 - } - - const PRIORITY_TYPES = { - terminal: 0, - fw_detection: 1,//reserved only for FW detection - SETTINGS.masterNodeIsResponding - high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) - relay_profile: 3, - node_broadcast: 4, - node_profile: 5, - node_cmd: 6 - } - - const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes - - //list of command calls to process. Processing in runTasks function - let tasks = []; - - let interval = null;//timeout for procesing tasks - let customTasksInterval = null; // interval for reportEdgeDateTimeAndNumberOfLuminaires(); - let setCorrectTime = null; // interval for setting a correct edgeTime - let sendNodeReadout = null; // interval for sending agregate data from node - - let refFlowdataObj = {}; - - //load from settings - let latitude = 48.70826502;//48.682255758; - let longitude = 17.28455203;//17.278910807; - - const gmtOffset = 0; - - //ak nie je nastaveny - //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ - //https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates - - //priorities for registers - let priorities = []; - - let minutes = 1; - priorities["1"] = minutes; // dimming - priorities["76"] = minutes; // power - - minutes = 5; - priorities["75"] = minutes; // current - priorities["79"] = minutes; // energy - priorities["87"] = minutes; // aktualny cas - //priorities["84"] = minutes; - - minutes = 10; - priorities["74"] = minutes; // voltage - priorities["77"] = minutes; // power factor - priorities["78"] = minutes; // frequency - - minutes = 60; - priorities["0"] = minutes; // statecode - priorities["6"] = minutes; // dusk - priorities["7"] = minutes; // dawn - priorities["8"] = minutes; // profile - - minutes = 60 * 24; - priorities["89"] = minutes; // verzia fw - priorities["80"] = minutes; // lifetime - - //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) - let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; - - let rotary_switch_state; - let lux_sensor; - let state_of_breaker = {};//key is line, value is On/Off - let disconnectedReport = {};//key is tbname, value true/false - - let relaysData; - let nodesData; - - let sunCalcResult; - let reportDuskDawn; - - //helper container for counting resolved group of commands (commands related to set profile) - let cmdCounter = {};//key is node, value is counter - - //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice - const nodeProfileSendFail = new Set(); - - //we expect to get current temperature in Senica from senica-prod01 - let temperatureInSenica = null; - let accelerometerInterval = null; - - //END OF VARIABLE SETTINGS - //-------------------------------- - - - function main() { - GLOBALS = FLOW.GLOBALS; - SETTINGS = FLOW.GLOBALS.settings; - relaysData = GLOBALS.relaysData; - nodesData = GLOBALS.nodesData; - latitude = GLOBALS.settings.latitude; - longitude = GLOBALS.settings.longitude; - - tbHandler = new DataToTbHandler(SEND_TO.tb); - tbHandler.setSender(exports.title); - - let now = new Date(); - console.log("Cmd-mngr installed", now.toLocaleString("sk-SK")); - - sunCalcResult = calculateDuskDawn(); - - reportDuskDawn = { - dusk_time: sunCalcResult.dusk_time, - dawn_time: sunCalcResult.dawn_time, - dusk_time_reported: undefined, - dawn_time_reported: undefined - }; + const { SerialPort } = require('serialport'); + const { exec } = require('child_process'); + const { crc16 } = require('easy-crc'); + const { runSyncExec, writeData } = require('./helper/serialport_helper'); + const { bytesToInt, longToByteArray, addZeroBefore } = require('./helper/utils'); + const bitwise = require('bitwise'); + + var SunCalc = require('./helper/suncalc'); + const DataToTbHandler = require('./helper/DataToTbHandler'); + const errorHandler = require('./helper/ErrorToServiceHandler'); + const { sendNotification } = require('./helper/notification_reporter'); + const process = require('process'); + const { errLogger, logger, monitor } = require('./helper/logger'); + + //for accelerometer purposes + const { naklony } = require("../databases/accelerometer_db"); + + const dbNodes = TABLE("nodes"); + const dbRelays = TABLE("relays"); + + let GLOBALS; + let SETTINGS; + let rsPort; + let tbHandler; + + // runTasks intervals + const SHORT_INTERVAL = 30; + const LONG_INTERVAL = 300; + + //send data to following instances: + const SEND_TO = { + debug: 0, + tb: 1, + http_response: 2, + dido_controller: 3, + infoSender: 4 + } + + const PRIORITY_TYPES = { + terminal: 0, + fw_detection: 1,//reserved only for FW detection - SETTINGS.masterNodeIsResponding + high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) + relay_profile: 3, + node_broadcast: 4, + node_profile: 5, + node_cmd: 6 + } + + const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes + + //list of command calls to process. Processing in runTasks function + let tasks = []; + + let interval = null;//timeout for procesing tasks + let customTasksInterval = null; // interval for reportEdgeDateTimeAndNumberOfLuminaires(); + let setCorrectTime = null; // interval for setting a correct edgeTime + let sendNodeReadout = null; // interval for sending agregate data from node + + let refFlowdataObj = {}; + + //load from settings + let latitude = 48.70826502;//48.682255758; + let longitude = 17.28455203;//17.278910807; + + const gmtOffset = 0; + + //ak nie je nastaveny + //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ + //https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates + + //priorities for registers + let priorities = []; + + let minutes = 1; + priorities["1"] = minutes; + priorities["76"] = minutes; + + // minutes = 5; + priorities["75"] = minutes;//current + priorities["79"] = minutes;//energy + priorities["87"] = minutes;//aktualny cas + //priorities["84"] = minutes; + + // minutes = 10; + priorities["74"] = minutes; + priorities["77"] = minutes; + priorities["78"] = minutes; + + // minutes = 60; + priorities["0"] = minutes; + priorities["6"] = minutes; + priorities["7"] = minutes; + priorities["8"] = minutes; + + // minutes = 60 * 24; + priorities["89"] = minutes; + priorities["80"] = minutes; + + //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) + let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; + + let rotary_switch_state; + let lux_sensor; + let state_of_breaker = {};//key is line, value is On/Off + let disconnectedReport = {};//key is tbname, value true/false + + let relaysData; + let nodesData; + + let sunCalcResult; + let reportDuskDawn; + + //helper container for counting resolved group of commands (commands related to set profile) + let cmdCounter = {};//key is node, value is counter + + //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice + const nodeProfileSendFail = new Set(); + + //we expect to get current temperature in Senica from senica-prod01 + let temperatureInSenica = null; + let accelerometerInterval = null; + + //END OF VARIABLE SETTINGS + //-------------------------------- + + + function main() { + GLOBALS = FLOW.GLOBALS; + SETTINGS = FLOW.GLOBALS.settings; + relaysData = GLOBALS.relaysData; + nodesData = GLOBALS.nodesData; + latitude = GLOBALS.settings.latitude; + longitude = GLOBALS.settings.longitude; + + tbHandler = new DataToTbHandler(SEND_TO.tb); + tbHandler.setSender(exports.title); + + let now = new Date(); + console.log("CMD Manager installed", now.toLocaleString("sk-SK")); + + sunCalcResult = calculateDuskDawn(); + + reportDuskDawn = { + dusk_time: sunCalcResult.dusk_time, + dawn_time: sunCalcResult.dawn_time, + dusk_time_reported: undefined, + dawn_time_reported: undefined + }; - handleRsPort(); + handleRsPort(); - customTasksInterval = setInterval(function() { - reportEdgeDateTimeAndNumberOfLuminaires(); - }, 120000); - reportEdgeDateTimeAndNumberOfLuminaires(); + customTasksInterval = setInterval(function() { + reportEdgeDateTimeAndNumberOfLuminaires(); + }, 120000); + reportEdgeDateTimeAndNumberOfLuminaires(); - setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour - setCorrectPlcTimeOnceADay(); + setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour + setCorrectPlcTimeOnceADay(); - sendNodeReadout = setInterval(sendNodesData, 150000); - accelerometerInterval = setInterval(accelerometerData, 60000 * 30); //30 min - } - - - function cmdCounterResolve(address) { - if (cmdCounter.hasOwnProperty(address)) { - cmdCounter[address] = cmdCounter[address] - 1; - - let result = cmdCounter[address]; - if (result == 0) delete cmdCounter[address]; - return result; - } - return -1; - } - - - function getParams(priority) { - let params = {}; - - //core rpc values - params.address = 0;//if(recipient === 0) address = 0; - params.byte1 = 0;//msb, podla dokumentacie data3 - params.byte2 = 0;//podla dokumentacie data2 - params.byte3 = 0;//podla dokumentacie data1 - params.byte4 = 0;//lsb, podla dokumentacie data0 - params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast - params.register = -1;//register number - params.rw = 0;//0: read, 1: write - - //other values - //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" - //params.tbname = tbname; - params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority - params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed - if (priority != undefined) { - params.timestamp = priority; - params.priority = priority; - } + sendNodeReadout = setInterval(sendNodesData, 150000); + accelerometerInterval = setInterval(accelerometerData, 60000 * 30); //30 min + } + + + function cmdCounterResolve(address) { + if (cmdCounter.hasOwnProperty(address)) { + cmdCounter[address] = cmdCounter[address] - 1; + + let result = cmdCounter[address]; + if (result == 0) delete cmdCounter[address]; + return result; + } + return -1; + } + + + function getParams(priority) { + let params = {}; + + //core rpc values + params.address = 0;//if(recipient === 0) address = 0; + params.byte1 = 0;//msb, podla dokumentacie data3 + params.byte2 = 0;//podla dokumentacie data2 + params.byte3 = 0;//podla dokumentacie data1 + params.byte4 = 0;//lsb, podla dokumentacie data0 + params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast + params.register = -1;//register number + params.rw = 0;//0: read, 1: write + + //other values + //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" + //params.tbname = tbname; + params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority + params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed + if (priority != undefined) { + params.timestamp = priority; + params.priority = priority; + } - params.addMinutesToTimestamp = 0;//repeat task if value is > 0 - // if node regular readout does not respond, we repeat request - params.repeatCounter = 0; - //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" - //params.info = ""; - //params.debug = true; // will console.log params in writeData response - - return params; - } - - - //nastav profil nodu - function processNodeProfile(node) { - if (rotary_switch_state != "Automatic") { - logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); - return; - } - - let nodeObj = nodesData[node]; - let line = nodeObj.line; - - if (relaysData[line].contactor == 0) { - logger.debug("line line is off", line, node); - return; - } - - if (nodeObj.processed == 1) { - //logger.debug("node was already processed", node); - return; - } - - let nodeProfile = nodeObj.profile; - logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); - if (nodeProfile) { - - try { - nodeProfile = JSON.parse(nodeProfile); - } catch (error) { - logger.debug("Cmd-mngr: Error parsing node profile", error); - } - - } - - logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); - - let timestamp = PRIORITY_TYPES.node_cmd; - - removeTask({ type: "set_node_profile", address: node }); - - if (nodeProfile === "") { - //vypneme profil nodu, posleme cmd - //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. - //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia - - logger.debug("turn off profile"); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = 'turn off/reset node profile'; - - cmdCounter[node] = 1; - - tasks.push(params); - } - else { - let tasksProfile = []; - - //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = 'turn off node profile'; - - tasksProfile.push(params); - - timestamp++; - - logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); - - //TS1 Time point a TS1 Time Point Levels - let register = 9; - for (let i = 0; i < nodeProfile.intervals.length; i++) { - let obj = nodeProfile.intervals[i]; - //let timePoint = obj.time_point; - let dim_value = obj.value; - - - //Reg 9 až Reg 40 - - /* - Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. - Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). - Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. - Časový bod má formát: - Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované - Register úrovne má rovnaký formát ako dimming register (Reg 1). - */ - - let start_time = obj.start_time; - let t = start_time.split(":"); - //if(timePoint != undefined) t = timePoint.split(":"); - //else t = [0,0]; - - logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = parseInt(t[0]);//hh - params.byte2 = parseInt(t[1]);//mm - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte4 = parseInt(dim_value) + 128;// - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point Levels ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - } - - //Threshold lux level for DUSK/DAWN - { - - logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); - - let params = getParams(); - params.type = "set_node_profile"; - params.address = node; - params.register = 96; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = "Threshold lux level for DUSK/DAWN"; - - if (nodeProfile.dusk_lux_sensor) { - let v = nodeProfile.dusk_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if (nodeProfile.dawn_lux_sensor) { - let v = nodeProfile.dawn_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //DUSK/DAWN max. adjust period - { - - logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); - - let params = getParams(); - params.type = "set_node_profile"; - params.address = node; - params.register = 97; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = "DUSK/DAWN max. adjust period"; - - if (nodeProfile.astro_clock) { - let v = nodeProfile.dusk_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if (nodeProfile.astro_clock) { - let v = nodeProfile.dawn_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //Static offset - { - - //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: - //Bity 0 – 6: hodnota v minútach - //Bit 7: znamienko (1 – mínus) - - logger.debug("processNodeProfile: Static offset", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 98; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = "Static offset"; - - if (nodeProfile.astro_clock) { - let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); - let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); - - if (dusk_astro_clock_offset < 0) { - params.byte3 = (dusk_astro_clock_offset * -1) + 128; - } - else { - params.byte3 = dusk_astro_clock_offset; - } - - if (dawn_astro_clock_offset < 0) { - params.byte4 = (dawn_astro_clock_offset * -1) + 128; - } - else { - params.byte4 = dawn_astro_clock_offset; - } - } - - tasksProfile.push(params); - timestamp++; - } - - logger.debug("Time schedule settings - turn on", node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 8; - params.recipient = 1; - params.rw = 1;//write - - //Time schedule settings - let bits = []; - - //Byte 0 (LSB): - //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). - bits.push(1); - //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) - bits.push(0); - bits.push(0); - bits.push(0); - if (nodeProfile.astro_clock == true) { - //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý - bits.push(1); - } - else bits.push(0); - - //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) - bits.push(0); - - //Bity 6-7 - zatiaľ nepoužité - bits.push(0); - bits.push(0); - - params.byte4 = bitwise.byte.write(bits.reverse()); - - //Byte 2 – nastavenie pre lux senzor: - bits = []; - - //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if (nodeProfile.dusk_lux_sensor == true)//sumrak - { - bits.push(1); - } - else bits.push(0); - - //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if (nodeProfile.dawn_lux_sensor == true)//usvit - { - bits.push(1); - } - else bits.push(0); - - //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. - bits.push(0);//zatial neimplementovane - - //Bit 3 – 7 - nepoužité - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - - params.byte2 = bitwise.byte.write(bits.reverse()); - params.timestamp = timestamp; - params.info = "Time schedule settings - turn on"; - - tasksProfile.push(params); - - //zaver - cmdCounter[node] = tasksProfile.length; - - //tasks.push(tasksProfile); - tasks = tasks.concat(tasksProfile); - - } - - logger.debug("finished set profile for ", node); - - console.log("proces profile finished *********************") - } - - - function cleanUpRefFlowdataObj() { - let now = new Date(); - let timestamp = now.getTime(); + params.addMinutesToTimestamp = 0;//repeat task if value is > 0 + //if node regular readout does not respond, we repeat request + params.repeatCounter = 0; + //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" + //params.info = ""; + //params.debug = true; // will console.log params in writeData response + + return params; + } + + + //nastav profil nodu + function processNodeProfile(node) { + if (rotary_switch_state != "Automatic") { + logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); + return; + } + + let nodeObj = nodesData[node]; + let line = nodeObj.line; + + if (relaysData[line].contactor == 0) { + logger.debug("line line is off", line, node); + return; + } + + if (nodeObj.processed == 1) { + //logger.debug("node was already processed", node); + return; + } + + let nodeProfile = nodeObj.profile; + logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); + if (nodeProfile) { + + try { + nodeProfile = JSON.parse(nodeProfile); + } catch (error) { + logger.debug("Cmd_manager - Error parsing node profile", error); + } + + } + + logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); + + let timestamp = PRIORITY_TYPES.node_cmd; + + removeTask({ type: "set_node_profile", address: node }); + + if (nodeProfile === "") { + //vypneme profil nodu, posleme cmd + //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. + //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia + + logger.debug("turn off profile"); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = 'turn off/reset node profile'; + + cmdCounter[node] = 1; + + tasks.push(params); + } + else { + let tasksProfile = []; + + //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = 'turn off node profile'; + + tasksProfile.push(params); + + timestamp++; + + logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); + + //TS1 Time point a TS1 Time Point Levels + let register = 9; + for (let i = 0; i < nodeProfile.intervals.length; i++) { + let obj = nodeProfile.intervals[i]; + //let timePoint = obj.time_point; + let dim_value = obj.value; + + + //Reg 9 až Reg 40 + + /* + Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. + Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). + Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. + Časový bod má formát: + Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované + Register úrovne má rovnaký formát ako dimming register (Reg 1). + */ + + let start_time = obj.start_time; + let t = start_time.split(":"); + //if(timePoint != undefined) t = timePoint.split(":"); + //else t = [0,0]; + + logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = parseInt(t[0]);//hh + params.byte2 = parseInt(t[1]);//mm + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte4 = parseInt(dim_value) + 128;// + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point Levels ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + } + + //Threshold lux level for DUSK/DAWN + { + + logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); + + let params = getParams(); + params.type = "set_node_profile"; + params.address = node; + params.register = 96; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = "Threshold lux level for DUSK/DAWN"; + + if (nodeProfile.dusk_lux_sensor) { + let v = nodeProfile.dusk_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if (nodeProfile.dawn_lux_sensor) { + let v = nodeProfile.dawn_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //DUSK/DAWN max. adjust period + { + + logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); + + let params = getParams(); + params.type = "set_node_profile"; + params.address = node; + params.register = 97; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = "DUSK/DAWN max. adjust period"; + + if (nodeProfile.astro_clock) { + let v = nodeProfile.dusk_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if (nodeProfile.astro_clock) { + let v = nodeProfile.dawn_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //Static offset + { + + //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: + //Bity 0 – 6: hodnota v minútach + //Bit 7: znamienko (1 – mínus) + + logger.debug("processNodeProfile: Static offset", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 98; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = "Static offset"; + + if (nodeProfile.astro_clock) { + let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); + let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); + + if (dusk_astro_clock_offset < 0) { + params.byte3 = (dusk_astro_clock_offset * -1) + 128; + } + else { + params.byte3 = dusk_astro_clock_offset; + } + + if (dawn_astro_clock_offset < 0) { + params.byte4 = (dawn_astro_clock_offset * -1) + 128; + } + else { + params.byte4 = dawn_astro_clock_offset; + } + } + + tasksProfile.push(params); + timestamp++; + } + + logger.debug("Time schedule settings - turn on", node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 8; + params.recipient = 1; + params.rw = 1;//write + + //Time schedule settings + let bits = []; + + //Byte 0 (LSB): + //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). + bits.push(1); + //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) + bits.push(0); + bits.push(0); + bits.push(0); + if (nodeProfile.astro_clock == true) { + //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý + bits.push(1); + } + else bits.push(0); + + //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) + bits.push(0); + + //Bity 6-7 - zatiaľ nepoužité + bits.push(0); + bits.push(0); + + params.byte4 = bitwise.byte.write(bits.reverse()); + + //Byte 2 – nastavenie pre lux senzor: + bits = []; + + //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if (nodeProfile.dusk_lux_sensor == true)//sumrak + { + bits.push(1); + } + else bits.push(0); + + //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if (nodeProfile.dawn_lux_sensor == true)//usvit + { + bits.push(1); + } + else bits.push(0); + + //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. + bits.push(0);//zatial neimplementovane + + //Bit 3 – 7 - nepoužité + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + + params.byte2 = bitwise.byte.write(bits.reverse()); + params.timestamp = timestamp; + params.info = "Time schedule settings - turn on"; + + tasksProfile.push(params); + + //zaver + cmdCounter[node] = tasksProfile.length; + + //tasks.push(tasksProfile); + tasks = tasks.concat(tasksProfile); + + } + + logger.debug("finished set profile for ", node); + + console.log("proces profile finished *********************") + } + + + function cleanUpRefFlowdataObj() { + let now = new Date(); + let timestamp = now.getTime(); - //clear old refFlowdata references - let keys = Object.keys(refFlowdataObj); - for (let i = 0; i < keys.length; i++) { - let timestampKey = keys[i]; + //clear old refFlowdata references + let keys = Object.keys(refFlowdataObj); + for (let i = 0; i < keys.length; i++) { + let timestampKey = keys[i]; - if ((timestamp - timestampKey) > 60 * 1000) { - console.log("cleanUpRefFlowdataObj delete", timestampKey); - delete refFlowdataObj[timestampKey]; - } - } - } + if ((timestamp - timestampKey) > 60 * 1000) { + console.log("cleanUpRefFlowdataObj delete", timestampKey); + delete refFlowdataObj[timestampKey]; + } + } + } - function removeTask(obj) { - let keys = Object.keys(obj); - tasks = tasks.filter((task) => { + function removeTask(obj) { + let keys = Object.keys(obj); + tasks = tasks.filter((task) => { - let counter = 0; - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - if (task.hasOwnProperty(key) && obj.hasOwnProperty(key)) { - if (task[key] == obj[key]) counter++; - } - } + let counter = 0; + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + if (task.hasOwnProperty(key) && obj.hasOwnProperty(key)) { + if (task[key] == obj[key]) counter++; + } + } - if (counter == keys.length) return false; - return true; - }); - } + if (counter == keys.length) return false; + return true; + }); + } - process.on('uncaughtException', function(err) { - //TODO send to service + process.on('uncaughtException', function(err) { + //TODO send to service - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); + errLogger.error('uncaughtException:', err.message) + errLogger.error(err.stack); - errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); - //process.exit(1); - }) + errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); + //process.exit(1); + }) - //te();//force error + //te();//force error - function processAllNodeProfilesOnLine(line) { - for (let k in nodesData) { - if (line == nodesData[k].line) { - let node = nodesData[k].node; - let processed = nodesData[k].processed; - - if (!processed) processNodeProfile(node); - //else logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); - } - } - } - - - function loadRelaysData(line) { - for (const [key, value] of Object.entries(relaysData)) { - if (key == "0") continue; - if (line != undefined) { - //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData - if (line != value.line) continue; - } - - if (value.contactor == 1) processAllNodeProfilesOnLine(value.line); - } - } - - - function reportOnlineNodeStatus(line) { - //Po zapnutí línie broadcastovo aktualizovať predtým čas a o 3 sek neskor - status, brightness - - logger.debug("Cmd-mngr: ----->reportOnlineNodeStatus for line", line); + function processAllNodeProfilesOnLine(line) { + for (let k in nodesData) { + if (line == nodesData[k].line) { + let node = nodesData[k].node; + let processed = nodesData[k].processed; + + if (!processed) processNodeProfile(node); + //else logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); + } + } + } + + + function loadRelaysData(line) { + for (const [key, value] of Object.entries(relaysData)) { + if (key == "0") continue; + if (line != undefined) { + //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData + if (line != value.line) continue; + } + + if (value.contactor == 1) processAllNodeProfilesOnLine(value.line); + } + } + + + function reportOnlineNodeStatus(line) { + //Po zapnutí línie broadcastovo aktualizovať predtým čas a o 3 sek neskor - status, brightness + + logger.debug("Cmd-mngr: ----->reportOnlineNodeStatus for line", line); - const d = new Date(); + const d = new Date(); - // broadcast actual time - let params = getParams(); - params.address = 0xffffffff;//Broadcast - params.byte1 = d.getHours(); - params.byte2 = d.getMinutes(); - params.recipient = 2;//2 broadcast, address = 0 - params.register = 87;//Actual time - params.rw = 1;//write - params.type = "node-onetime-write"; - params.timestamp = d.getTime() + 30000; - params.info = "run broadcast: Actual time"; - //params.debug = true; + // broadcast actual time + let params = getParams(); + params.address = 0xffffffff;//Broadcast + params.byte1 = d.getHours(); + params.byte2 = d.getMinutes(); + params.recipient = 2;//2 broadcast, address = 0 + params.register = 87;//Actual time + params.rw = 1;//write + params.type = "node-onetime-write"; + params.timestamp = d.getTime() + 30000; + params.info = "run broadcast: Actual time"; + //params.debug = true; - tasks.push(params); + tasks.push(params); - let sec = 3; - setTimeout(function() { - //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel + let sec = 3; + setTimeout(function() { + //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel - let time = Date.now(); - - for (let k in nodesData) { + let time = Date.now(); + + for (let k in nodesData) { - //potrebujem nody k danej linii - if (line == nodesData[k].line || line == undefined) { + //potrebujem nody k danej linii + if (line == nodesData[k].line || line == undefined) { - let tbname = nodesData[k].tbname; - let node = nodesData[k].node; - let status = "NOK"; + let tbname = nodesData[k].tbname; + let node = nodesData[k].node; + let status = "NOK"; - // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. - if (nodesData[k].node_status_before_offline === true || nodesData[k].status === true) { - status = "OK"; - nodesData[k].time_of_last_communication = time; - } + // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. + if (nodesData[k].node_status_before_offline === true || nodesData[k].status === true) { + status = "OK"; + nodesData[k].time_of_last_communication = time; + } - nodesData[k].readout.status = status; + nodesData[k].readout.status = status; - updateNodeStatus(k, status === "OK" ? true : false); - if (nodesData[k].hasOwnProperty("node_status_before_offline")) delete nodesData[k].node_status_before_offline; - sendTelemetry({ status: status }, tbname, time); + updateNodeStatus(k, status === "OK" ? true : false); + if (nodesData[k].hasOwnProperty("node_status_before_offline")) delete nodesData[k].node_status_before_offline; + sendTelemetry({ status: status }, tbname, time); - //vyreportovanie dimming, current, input power pre liniu pre vsetky nody - //Prud - { - let params = getParams(); + //vyreportovanie dimming, current, input power pre liniu pre vsetky nody + //Prud + { + let params = getParams(); - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 75;//prud - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = time + 4000; - params.info = 'read current'; - //params.debug = true; - tasks.push(params); - } + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 75;//prud + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 4000; + params.info = 'read current'; + //params.debug = true; + tasks.push(params); + } - //vykon - { - let params = getParams(); + //vykon + { + let params = getParams(); - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 76;//výkon - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = time + 4100; - params.info = 'read power'; - //params.debug = true; - - tasks.push(params); - } - //dimming - { - let params = getParams(); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = time + 4200; - params.info = 'read dimming'; - //params.debug = true; - - tasks.push(params); - } + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 76;//výkon + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 4100; + params.info = 'read power'; + //params.debug = true; + + tasks.push(params); + } + //dimming + { + let params = getParams(); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 4200; + params.info = 'read dimming'; + //params.debug = true; - } - } + tasks.push(params); + } - }, sec * 1000); - } - - - function reportOfflineNodeStatus(line) { - - logger.info("Cmd-mngr: ------>reportOffLineNodeStatus for line ", line); - - values = {}; - values["dimming"] = 0;//brightness - values["power"] = 0;//výkon - values["current"] = 0;//prúd - values["status"] = "OFFLINE"; - - const date = Date.now(); - - Object.keys(nodesData).forEach(node => { - - //potrebujem nody k danej linii - if (line == nodesData[node].line || line == undefined) { - - let tbname = nodesData[node].tbname; - let nodeStatus = nodesData[node].status; - - //in case we have reported offline node status, we return (continue with next node) - if (nodeStatus === "OFFLINE") return; - - nodesData[node].node_status_before_offline = nodeStatus; - nodesData[node].status = "OFFLINE"; - nodesData[node].readout = {}; - - sendTelemetry({ ...values }, tbname, date); - } - }); - - } - - - function turnLine(onOrOff, line, info) { - let obj = { - line: line, - command: onOrOff, - info: info - }; - - //logger.debug("linia", line, obj); - instance.send(SEND_TO.dido_controller, obj); - } - - - function detectIfResponseIsValid(bytes) { - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - let type = "RESPONSE"; - if (bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] - else if (bytes[4] == 0) type = "RESPONSE"; - else if (bytes[4] == 1) type = "ERROR"; - else if (bytes[4] == 2) type = "EVENT"; - else type = "UNKNOWN"; + } + } - let message = "OK"; - let error = ""; - if (type == "BROADCAST") return { message, type, error }; + }, sec * 1000); + } - let crc = crc16('ARC', bytes.slice(0, 9)); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; - if (c1 != bytes[9]) { - //CRC_ERROR - message = "NOK"; - error = "CRC_ERROR c1"; - instance.send(SEND_TO.debug, "CRC_ERROR c1"); - } + function reportOfflineNodeStatus(line) { + + logger.debug("Cmd-mngr: ------>reportOfflineNodeStatus for line", line); + + values = {}; + values["dimming"] = 0;//brightness + values["power"] = 0;//výkon + values["current"] = 0;//prúd + values["status"] = "OFFLINE"; + + const date = Date.now(); - if (c2 != bytes[10]) { - //CRC_ERROR - message = "NOK"; - error = "CRC_ERROR c2"; - instance.send(SEND_TO.debug, "CRC_ERROR c2"); - } + Object.keys(nodesData).forEach(node => { - //crc error - if (type != "RESPONSE") { - instance.send(SEND_TO.debug, bytes); - instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); + //potrebujem nody k danej linii + if (line == nodesData[node].line || line == undefined) { + + let tbname = nodesData[node].tbname; + let nodeStatus = nodesData[node].status; + + //in case we have reported offline node status, we return (continue with next node) + if (nodeStatus === "OFFLINE") return; + + nodesData[node].node_status_before_offline = nodeStatus; + nodesData[node].status = "OFFLINE"; + nodesData[node].readout = {}; + + sendTelemetry({ ...values }, tbname, date); + } + }) - //logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes); + } - error = "type is: " + type; - message = "NOK"; - } + function turnLine(onOrOff, line, info) { + let obj = { + line: line, + command: onOrOff, + info: info + }; + + //logger.debug("linia", line, obj); + instance.send(SEND_TO.dido_controller, obj); + } - return { message, type, error }; - } - //BUILD TASKS// - function buildTasks(params) { + function detectIfResponseIsValid(bytes) { + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let type = "RESPONSE"; + if (bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] + else if (bytes[4] == 0) type = "RESPONSE"; + else if (bytes[4] == 1) type = "ERROR"; + else if (bytes[4] == 2) type = "EVENT"; + else type = "UNKNOWN"; - //return; - console.log("buidTAaasks start ****************", params); - monitor.info("buildTasks - params", params); + let message = "OK"; + let error = ""; + if (type == "BROADCAST") return { message, type, error }; - let processLine; //defined line - let init = false; - let processLineProfiles = true; - let processBroadcast = true; - let processNodes = true; + let crc = crc16('ARC', bytes.slice(0, 9)); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; - if (params == undefined) { - init = true; - tasks = []; - logger.debug("-->buildTasks clear tasks"); - } - else { - processLineProfiles = false; - processBroadcast = false; - processNodes = false; + if (c1 != bytes[9]) { + //CRC_ERROR + message = "NOK"; + error = "CRC_ERROR c1"; + instance.send(SEND_TO.debug, "CRC_ERROR c1"); + } - processLineProfiles = params.processLineProfiles; - processLine = params.line; - } + if (c2 != bytes[10]) { + //CRC_ERROR + message = "NOK"; + error = "CRC_ERROR c2"; + instance.send(SEND_TO.debug, "CRC_ERROR c2"); + } - let now = new Date(); + //crc error + if (type != "RESPONSE") { + instance.send(SEND_TO.debug, bytes); + instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); - //process line profiles - if (processLineProfiles) { + //logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes); - let keys = Object.keys(relaysData); + error = "type is: " + type; - for (let i = 0; i < keys.length; i++) { + message = "NOK"; + } - let line = parseInt(keys[i]); - let profilestr = relaysData[line].profile; + return { message, type, error }; + } - if (processLine != undefined) { - if (processLine != line) continue; - } - try { + //BUILD TASKS// + function buildTasks(params) { - /** - * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... - */ - if (profilestr === "") throw ("Profile is not defined"); - let profile = JSON.parse(profilestr); - if (Object.keys(profile).length === 0) throw ("Profile is empty"); + //return; + console.log("buidTAaasks start ****************", params); + monitor.info("buildTasks - params", params); - monitor.info("buildTasks: profile for line", line); - monitor.info("profile:", profile); + let processLine; //defined line + let init = false; + let processLineProfiles = true; + let processBroadcast = true; + let processNodes = true; - let time_points = profile.intervals; + if (params == undefined) { + init = true; + tasks = []; + logger.debug("-->buildTasks clear tasks"); + } + else { + processLineProfiles = false; + processBroadcast = false; + processNodes = false; - // add name to regular profile timepoint and delete unused end_time key: - time_points.forEach(point => { - point.name = "profileTimepoint" - delete point.end_time; - }); + processLineProfiles = params.processLineProfiles; + processLine = params.line; + } - //monitor.info("buildTasks: time_points", time_points); + let now = new Date(); + //process line profiles + if (processLineProfiles) { - /** - * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. - * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn - * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching - */ - if (profile.astro_clock == true) { + let keys = Object.keys(relaysData); - // if astro clock true, we remove all regular profile points - time_points = []; + for (let i = 0; i < keys.length; i++) { - let sunCalcResult = calculateDuskDawn(new Date(), line); + let line = parseInt(keys[i]); + let profilestr = relaysData[line].profile; - // adding dusk dawn to timpoints - if (profile.dawn_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dawn"], "value": 0, "name": "dawn" }); - if (profile.dusk_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dusk"], "value": 1, "name": "dusk" }); + if (processLine != undefined) { + if (processLine != line) continue; + } - //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) - //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window - if (profile.dawn_lux_sensor == true) { - let [ahours, aminutes] = sunCalcResult["dawn"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dawn_lux_sensor_time_window, 0); + try { - let strDate = ad.getHours() + ":" + ad.getMinutes(); - time_points.push({ "value": 0, "start_time": strDate, "name": "luxOff" }); - } + /** + * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... + */ + if (profilestr === "") throw ("Profile is not defined"); + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("Profile is empty"); - if (profile.dusk_lux_sensor == true) { - let [ahours, aminutes] = sunCalcResult["dusk"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dusk_lux_sensor_time_window, 0); + monitor.info("buildTasks: profile for line", line); + monitor.info("profile:", profile); - let strDate = ad.getHours() + ":" + ad.getMinutes(); - time_points.push({ "value": 1, "start_time": strDate, "name": "luxOn" }); - //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing - } - } + let time_points = profile.intervals; - //sort time_points - time_points.sort(function(a, b) { + // add name to regular profile timepoint and delete unused end_time key: + time_points.forEach(point => { + point.name = "profileTimepoint" + delete point.end_time; + }); - let [ahours, aminutes] = a.start_time.split(':'); - let [bhours, bminutes] = b.start_time.split(':'); + //monitor.info("buildTasks: time_points", time_points); - let ad = new Date(); - ad.setHours(parseInt(ahours), parseInt(aminutes), 0); - let bd = new Date(); - bd.setHours(parseInt(bhours), parseInt(bminutes), 0); + /** + * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. + * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn + * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching + */ + if (profile.astro_clock == true) { - return ad.getTime() - bd.getTime(); - }); + // if astro clock true, we remove all regular profile points + time_points = []; - console.log("line timepoints ........", time_points); + let sunCalcResult = calculateDuskDawn(new Date(), line); - let currentValue = 0; - if (time_points.length > 0) currentValue = time_points[time_points.length - 1].value; + // adding dusk dawn to timpoints + if (profile.dawn_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dawn"], "value": 0, "name": "dawn" }); + if (profile.dusk_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dusk"], "value": 1, "name": "dusk" }); - monitor.info("-->comming events turn on/off lines:"); - for (let t = 0; t < time_points.length; t++) { + //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) + //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window + if (profile.dawn_lux_sensor == true) { + let [ahours, aminutes] = sunCalcResult["dawn"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dawn_lux_sensor_time_window, 0); - let start_time = new Date(); - let [hours, minutes] = time_points[t].start_time.split(':'); - start_time.setHours(parseInt(hours), parseInt(minutes), 0); + let strDate = ad.getHours() + ":" + ad.getMinutes(); + time_points.push({ "value": 0, "start_time": strDate, "name": "luxOff" }); + } - //task is in the past - if (now.getTime() > start_time.getTime()) { - currentValue = time_points[t].value; + if (profile.dusk_lux_sensor == true) { + let [ahours, aminutes] = sunCalcResult["dusk"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dusk_lux_sensor_time_window, 0); - //timepoint is in past, we add 24 hours - start_time.setDate(start_time.getDate() + 1); - } - - let params = getParams(); - params.type = "relay"; - params.line = parseInt(line); - params.value = time_points[t].value; - params.tbname = relaysData[line].tbname; - params.timestamp = start_time.getTime(); - - // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day - if (time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24 * 60; - - //astro timepoints will be recalculated dynamically: - params.timePointName = time_points[t].name; - - // if astro timepoint, we save time window: - if (['luxOn', 'luxOff', 'dusk', 'dawn'].includes(params.timePointName)) { - params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; - params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; - } - - if (params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; - else if (params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; - - params.debug = true; - - //turn on/off line - tasks.push(params); - monitor.info("TimePoint params: ", params.info, start_time); - } - - monitor.info("-->time_points final", line, time_points); - - //ensure to turn on/off according to calculated currentValue - let params = getParams(); - params.type = "relay"; - params.line = parseInt(line); - params.tbname = relaysData[line].tbname; - params.value = currentValue; - params.timestamp = i; - params.debug = true; - - //logger.debug(now.toLocaleString("sk-SK")); - monitor.info("-->currentValue for relay", line, currentValue); - - //turn on/off line - if (params.value == 0) params.info = "turn off line on startup: " + line; - else if (params.value == 1) params.info = "turn on line on startup: " + line; - - tasks.push(params); - - } catch (error) { - if (profilestr !== "") { - //errLogger.error(profilestr, error); - console.log(`Cmd_mngr: Unable to process line profile ${line}. Error: `, error); - errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); - } else { - turnLine("off", line, "No line profile. Switching it off on startup"); - } - } + let strDate = ad.getHours() + ":" + ad.getMinutes(); + time_points.push({ "value": 1, "start_time": strDate, "name": "luxOn" }); + //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing + } + } - } - //logger.debug("tasks:"); - //logger.debug(tasks); - } + //sort time_points + time_points.sort(function(a, b) { + let [ahours, aminutes] = a.start_time.split(':'); + let [bhours, bminutes] = b.start_time.split(':'); - //NOTE: PROCESS DEFAULT BROADCASTS - Time of dusk, Time of dawn, Actual Time - if (processBroadcast) { + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes), 0); - let d = new Date(); - let time = d.getTime(); - let sunCalcResult = calculateDuskDawn(); - - { - let params = getParams(); - - params.address = 0xffffffff;//broadcast - params.byte1 = sunCalcResult["dusk_hours"]; - params.byte2 = sunCalcResult["dusk_minutes"]; - params.recipient = 2;//2 broadcast, - params.register = 6;//Time of dusk - params.rw = 1;//write - params.type = "node-regular-write"; - params.timestamp = time + 60000; - params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk - params.info = "Broadcast-duskTime"; - - tasks.push(params); - } - - { - let params = getParams(); - - params.address = 0xffffffff;//broadcast - params.byte1 = sunCalcResult["dawn_hours"]; - params.byte2 = sunCalcResult["dawn_minutes"]; - params.recipient = 2; //2 broadcast - params.register = 7;//Time of dawn - params.rw = 1;//write - params.type = "node-regular-write"; - params.timestamp = time + 60001; - params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn - params.info = "Broadcast-dawnTime"; - - tasks.push(params); - } - - { - let params = getParams(); - - params.address = 0xffffffff;//broadcast - params.byte1 = d.getHours(); - params.byte2 = d.getMinutes(); - params.recipient = 2; //2 broadcast - params.register = 87;//Actual time - params.rw = 1;//write - params.type = "node-regular-write"; - params.timestamp = time + 60002; - params.addMinutesToTimestamp = 5; - params.info = "run broadcast: Actual time"; - - tasks.push(params); - } - - } - - //process nodes & tasks - read node's data - if (processNodes) { - - let time = Date.now(); - - for (let k in nodesData) { - let address = parseInt(k); - let tbname = nodesData[k].tbname; - let register = 0; - - for (let i = 0; i < listOfCommands.length; i++) { - - register = listOfCommands[i]; - let addMinutesToTimestamp = priorities[register]; - - let params = getParams(); - - params.address = address; - params.recipient = 1; - params.register = register; - params.type = register == 1 ? "node-dimming-read" : "node-regular-read"; - params.tbname = tbname; - params.timestamp = time + 5000 + i * 500 + addMinutesToTimestamp * 1000; //to make slight time difference - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "Node regular read command"; - - tasks.push(params); - } - - } - } - - - //niektore ulohy sa vygeneruju iba 1x pri starte!!! - if (!init) return; - - - //Master node FW version - modifies SETTINGS.masterNodeIsResponding - { - let params = getParams(); - params.type = "cmd-master"; - params.register = 4; - params.address = 0; - params.timestamp = 0; - params.addMinutesToTimestamp = 5; - params.tbname = SETTINGS.rvoTbName; - params.info = "Master node FW verzia"; - //params.debug = true; - - tasks.push(params); - } - - //kazdu hodinu skontrolovat nastavenie profilov - { - let params = getParams(); - params.type = "process_profiles"; - params.timestamp = Date.now() + 60001; - params.addMinutesToTimestamp = 60;//60 = every hour - params.info = "detekcia nespracovaných profilov linie a nodov"; - //params.debug = true; - - tasks.push(params); - } - - monitor.info("tasks created:", tasks.length); - } - - - /** - * We process line profile, where "astro_clock": true - * example profile: - * - "dawn_lux_sensor": true, - "dusk_lux_sensor": true, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 0, - "dusk_astro_clock_offset": 10, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - - * if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line. - * if dusk: we do oposite - * - * dawn: usvit - lux je nad hranicou - vypnem - * dusk: sumrak - lux je pod hranicou - zapnem - */ - function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) { - - let now = new Date(); - let currentTimestamp = now.getTime(); - let keys = Object.keys(relaysData); - - for (let i = 0; i < keys.length; i++) { - - let line = keys[i]; //line is turned off by default - let profilestr = relaysData[line].profile; - const contactor = relaysData[line].contactor; - - try { - - let profile = JSON.parse(profilestr); - if (Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); - - if (profile.astro_clock == true) { - let sunCalcResult = calculateDuskDawn(now, line); - - //usvit - if (profile.dawn_lux_sensor == true) { - let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut - let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); - - if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { - if (lux_sensor_value > profile.dawn_lux_sensor_value) { - if (contactor) turnLine("off", line, "Profile: dawn - turnOff line according to lux sensor"); - } - } - } - - //sumrak - if (profile.dusk_lux_sensor == true) { - let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); - let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); - - if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { - if (lux_sensor_value < profile.dusk_lux_sensor_value) { - if (!contactor) turnLine("on", line, "Profile: dusk - turnOn line according to lux sensor"); - } - } - } - - } - - } catch (error) { - if (profilestr !== "") monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); - } - - } - - } - - /** - * function updates status and time_of_last_communication of node in the dbNodes - * it only updates if conditions are met - * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) - * if newStatus of node is always receiving false, and it is already for more than SETTINGS.node_status_nok_time value, we update status to "NOK" in tb - * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). - */ - function updateNodeStatus(node, newStatus) { - //MASTER - if (node == 0) return; - - let nodeObj = nodesData[node]; - if (nodeObj == undefined) return; - - let nodeCurrentStatus = nodeObj.status; - const now = Date.now(); - - let data = null; - - if (nodeCurrentStatus === "OFFLINE") { - data = { status: newStatus }; - nodeDbStatusModify(node, data); - return; - } - else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; - else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { - data = { time_of_last_communication: now }; - nodeDbStatusModify(node, data); - return; - } - else if (newStatus == false && nodeCurrentStatus == false) return true; - else if (newStatus == false && nodeCurrentStatus == true) { - if (nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; - else { - data = { status: newStatus }; - nodeDbStatusModify(node, data); - return true; - } - } - else if (newStatus == true && nodeCurrentStatus == false) { - data = { status: newStatus, time_of_last_communication: now }; - nodeDbStatusModify(node, data); - return; - } - - } - - - function nodeDbStatusModify(node, data) { - dbNodes.modify(data).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - if (!err) { - nodesData[node] = { ...nodesData[node], ...data }; - } - }); - }); - } - - - async function runTasks() { - - clearInterval(interval); - - let currentTimestamp = Date.now(); - - //report dusk, dawn--------------------------------- - if (reportDuskDawn.dusk_time < currentTimestamp) { - //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { - //reportovali sme? - if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { - //sendNotification("Cmd-mngr: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); - reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; - } - } - - var nextDay = new Date(); - nextDay.setDate(nextDay.getDate() + 1); - - sunCalcResult = calculateDuskDawn(nextDay); - reportDuskDawn.dusk_time = sunCalcResult.dusk_time; - } - - if (reportDuskDawn.dawn_time < currentTimestamp) { - //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { - //reportovali sme? - if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { - //sendNotification(": calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); - reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; - } - } - - var nextDay = new Date(); - nextDay.setDate(nextDay.getDate() + 1); - - sunCalcResult = calculateDuskDawn(nextDay); - reportDuskDawn.dawn_time = sunCalcResult.dawn_time; - - } - //-------------------------------------------------------- - - //sort tasks based on timestamp - tasks.sort(function(a, b) { - if (a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) { - return a.priority - b.priority; - } - return a.timestamp - b.timestamp; - }); - - if (tasks.length == 0) { - instance.send(SEND_TO.debug, "no tasks created"); - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - if (!rsPort.isOpen) { - instance.send(SEND_TO.debug, "!rsPort.isOpen"); - //await rsPort.open(); - //console.log("Cmd-mngr: !rsPort.isOpen"); - } - - let currentTask = tasks[0]; - - if (currentTask.debug) { - //logger.debug("--->task to process", currentTask); - } - - if (currentTask.timestamp <= currentTimestamp) { - let params = { ...tasks[0] }; - - //allow terminal commands - if (SETTINGS.maintenance_mode && params.type !== "cmd-terminal") { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - let type = params.type; - let tbname = params.tbname; - let node = params.address; - let register = params.register; - let line = null; - let itIsNodeCommand; - - if (nodesData[node] !== undefined) { - line = nodesData[node].line; - itIsNodeCommand = true; - } - - if (params.line !== undefined) line = params.line; - - if (params.addMinutesToTimestamp > 0 || params.timePointName) { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } else { - tasks.shift(); - } - - //kontrola nespracovanych profilov nodov - if (type == "process_profiles") { - //na vsetky zapnutych liniach sa spracuju nespracovane profily nodov - loadRelaysData(); - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } - - //relay - if (type == "relay") { - - const timePointName = params.timePointName; - const value = params.value; - - let date = new Date(); - date.setDate(date.getDate() + 1);//next day - - let sunCalcResult; - if (timePointName) sunCalcResult = calculateDuskDawn(date, params.line); - - if (timePointName == "dawn") { - tasks[0].timestamp = sunCalcResult.dawn_time; - } - else if (timePointName == "dusk") { - tasks[0].timestamp = sunCalcResult.dusk_time; - } - else if (timePointName == "luxOn") { - tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; - } - else if (timePointName == "luxOff") { - tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; - } - else if (timePointName == "profileTimepoint") { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } - - let info = "aplikovany bod profilu"; - let onOrOff = ""; - value == 1 ? onOrOff = "on" : onOrOff = "off"; - - turnLine(onOrOff, params.line, info); - - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - if (!SETTINGS.masterNodeIsResponding) { - //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal cmd-master - errorHandler.sendMessageToService("Master node is not responding"); - - let stop = true; - - if (type === "cmd-terminal" || type === "cmd-master") stop = false; - if (stop) { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - } - - let contactorStatus = 1; - if (relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; - - if (line === 0 || contactorStatus === 0 || FLOW.deviceStatus.state_of_breaker[line] === "Off") { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors - // check if rotary_switch_state == "Off" - // state_of_braker: disconnected = true? - - if (!rsPort.isOpen) { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - //RE-CALCULATE VALUES - //set actual time for broadcast - if (register == 87 && params.recipient === 2) { - var d = new Date(); - params.byte1 = d.getHours();//h - params.byte2 = d.getMinutes();//m - } - - //SET DUSK/DAWN FOR BROADCAST - //Time of dusk - if (register == 6 && params.recipient === 2) { - - if (type != "cmd-terminal") { - let sunCalcResult = calculateDuskDawn(); - params.byte1 = sunCalcResult["dusk_hours"];//h - params.byte2 = sunCalcResult["dusk_minutes"];//m - } - } - - //Time of dawn - if (register == 7 && params.recipient === 2) { - if (type != "cmd-terminal") { - let sunCalcResult = calculateDuskDawn(); - params.byte1 = sunCalcResult["dawn_hours"];//h - params.byte2 = sunCalcResult["dawn_minutes"];//m - } - } - //----------------------- - - instance.send(SEND_TO.debug, "address: " + node + " register:" + register + "type: " + type); - - var startTime, endTime; - startTime = new Date(); - - let saveToTb = true; - if (!tbname) saveToTb = false; - - let resp = com_generic(node, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); - let readBytes = 11; - let timeout = 4000; - - - // await keyword is important, otherwise incorrect data is returned! - await writeData(rsPort, resp, readBytes, timeout).then(function(data) { - - //sometimes happens, that status of node changes to OK, NOK even if line was turned off and should be status OFFLINE. To prevent this, we return if line contactor is 0: - if (itIsNodeCommand && line && relaysData[line].contactor !== 1) return; - - endTime = new Date(); - var timeDiff = endTime - startTime; - - //data je array z 11 bytov: 1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data a 2 byty CRC - let dataBytes = data.slice(5, 9); - let result = detectIfResponseIsValid(data); - - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - let message = result.message; // OK, NOK - let message_type = result.type; - - if (params.hasOwnProperty("debug")) { - if (params.debug) { - console.log("detected response:", result); - logger.debug("Cmd-mngr: writeData done " + message_type + " duration: " + timeDiff + " type: " + params.debug, params, result); - } - } - - let values = {}; - - //CMD FINISHED - if (message == "OK") { - - updateNodeStatus(node, true); - - //write - if (type == "set_node_profile") { - let result = cmdCounterResolve(node); - if (result == 0) { - dbNodes.modify({ processed: true }).where("node", node).make(function(builder) { - builder.callback(function(err, response) { - - sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); - - logger.debug("--> profil úspešne odoslaný na node č. " + node); - nodesData[node].processed = true; - nodeProfileSendFail.delete(node); - }); - }); - } - } - - //parse read response - if (params.rw == 0) { - values = processResponse(register, dataBytes); //read - } - - if (itIsNodeCommand) { - values.comm_status = "OK"; - values.status = "OK"; - nodesData[node].readout = { ...nodesData[node].readout, ...values }; - } - - //master node - if (node == 0) { - sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); - SETTINGS.masterNodeIsResponding = true; - if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; - } - - if (params.debug) { - //logger.debug("saveToTb", saveToTb, tbname, values); - } - - if (saveToTb && type != "node-regular-read") { - sendTelemetry(values, tbname); - } - else { - if (type == "cmd-terminal") { - terminalCommandResponse(params, "SUCCESS", data); - } - } - - } - else { - terminalCommandResponse(params, "ERROR", data); - handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); - - if (params.hasOwnProperty("debug")) { - if (params.debug) { - //logger.debug("writeData err: ", error, result, params); - logger.debug("writeData err: ", tbname, node, register, values); - } - } - - //logger.debug(error, result, params); - } - }).catch(function(reason) { - - //console.log("writeData catch exception", reason); - instance.send(SEND_TO.debug, reason); - - terminalCommandResponse(params, "FAILURE", null, reason); - handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); - - if (params.hasOwnProperty("debug")) { - if (params.debug) { - logger.debug("-->WRITE FAILED: " + reason, params.debug, params); - } - } - - }); - - } - else { - if (currentTask.debug) { - // currentTask.timestamp <= currentTimestamp && logger.debug("currentTask is not processed - task is in the future", currentTask); - } - - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - //console.log("----->runTasks - setInterval", new Date()); - interval = setInterval(runTasks, SHORT_INTERVAL); - } - - - // if node does not respond to request, we repeat request 3 times: - function repeatCommand(params) { - params.repeatCounter++; - if (params.repeatCounter < 4) { - params.timestamp = 0; - params.addMinutesToTimestamp = 0; - tasks.push(params); - } - } - - function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb) { - - let node = params.address; - let register = params.register; - let type = params.type; - let tbName = params.tbname; - if (!tbName) return; - - let values = {}; - - let updateStatus = updateNodeStatus(node, false); - - if (itIsNodeCommand) { - values.comm_status = "NOK"; - nodesData[node].readout.comm_status = "NOK"; - repeatCommand(params); - } - - if (updateStatus) { - values.status = "NOK"; - nodesData[node].readout.status = "NOK"; - } - - if (type === "node-regular-read") return; - - //master node - if (node == 0) { - sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); - logger.debug("master_node_is_not_responding", params); - SETTINGS.masterNodeIsResponding = false; - - if (register == 4) values["master_node_version"] = "NOK"; - } - - if (type == "set_node_profile") { - delete cmdCounter[node]; - logger.debug("profil nebol úspešne odoslaný na node č. ", params); - - if (!nodeProfileSendFail.has(node)) { - sendNotification("Cmd-mngr: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); - nodeProfileSendFail.add(node); - } - } - - // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); - if (saveToTb) { - sendTelemetry(values, tbName); - } - - } - - - function sendNodesData() { - Object.keys(nodesData).forEach(node => { - if (nodesData[node]["status"] !== "OFFLINE") { - sendTelemetry(nodesData[node].readout, nodesData[node].tbname); - nodesData[node].readout = {}; - } - }) - } - - - /** - * function handles requests from terminal - * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. - * FAILURE means, that we got into catch block of writeData function. - */ - function terminalCommandResponse(params, responseType, data = null, reason = "") { //success, error, failure - - if (params.refFlowdataKey === undefined) { - //console.log("params.refFlowdataKey is undefined", params); - return; - } - - let message = null; - let type = null; - - switch (responseType) { - case "SUCCESS": - message = "cmd-terminal SUCCESS"; - type = "SUCCESS"; - break; - case "ERROR": - message = "cmd-terminal FAILED"; - type = "ERROR"; - break; - case "FAILURE": - message = "ERROR WRITE FAILED: " + reason; - type = "ERROR"; - break; - default: - type = undefined; - } - - logger.debug(message); - - //make http response - let responseObj = {} - responseObj["type"] = type; - - if (responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; - else responseObj["bytes"] = data; - - let refFlowdata = refFlowdataObj[params.refFlowdataKey]; //holds reference to httprequest flowdata - if (refFlowdata) { - refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, refFlowdata); - } - } - - - /** - * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function - */ - function reportEdgeDateTimeAndNumberOfLuminaires() { - - //Number of ok and nok nodes on platform does not equals to total number of nodes. - //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: - let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); - - const ts = Date.now(); - const keys = Object.keys(nodesData_clone); - - const number_of_luminaires = keys.length; - let number_of_ok_luminaires = 0; - let number_of_nok_luminaires = 0; - - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let nodeObj = nodesData_clone[key]; - if (nodeObj.tbname == undefined) continue; - - if (nodeObj.status === "OFFLINE") { - nodeObj.node_status_before_offline === true ? number_of_ok_luminaires++ : number_of_nok_luminaires++; - } - else if (nodeObj.status == true) number_of_ok_luminaires++; - else number_of_nok_luminaires++; - - } - - const values = { - "number_of_luminaires": number_of_luminaires, - "number_of_ok_luminaires": number_of_ok_luminaires, - "number_of_nok_luminaires": number_of_nok_luminaires, - "edge_date_time": ts - ts % 60000 //round to full minute - }; - - sendTelemetry(values, SETTINGS.rvoTbName, ts); - } - - - function handleRsPort() { - - if (rsPort) { - rsPort.removeAllListeners(); - rsPort = null; - } - - //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" - // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM - // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI - - if (SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; - rsPort = new SerialPort(`/dev/${SETTINGS.serial_port}`, { autoOpen: false }); - //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit - //rsPort.setMaxListeners(0); - - rsPort.on('open', async function() { - - logger.debug("Cmd-mngr: rsPort opened success"); - - await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { - instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); - - logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); - - }).catch(function(reason) { - instance.send(SEND_TO.debug, "Cmd-mngr: RPC runSyncExec - promise rejected:" + reason); - }); - }); - - rsPort.on('error', function(err) { - errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); - monitor.info("Cmd-mngr: Error on rsPort", err.message); - }); - - rsPort.on("close", () => { - monitor.info("Cmd-mngr: rsPort closed, reconnecting..."); - setTimeout(handleRsPort, 1000); - }); - - rsPort.open(); - } - - - instance.on("close", () => { - clearInterval(interval); - clearInterval(customTasksInterval); - clearInterval(setCorrectTime); - clearInterval(sendNodeReadout); - clearInterval(accelerometerInterval); - rsPort.close(); - }); - - instance.on("0", _ => { - main(); - }) - - instance.on("1", async function(flowdata) { - - //instance.send(SEND_TO.debug, "on Data"); - //instance.send(SEND_TO.debug, flowdata); - - //logger.debug(flowdata.data); - - //just testing functions - if (flowdata.data == "open") { - if (!rsPort.isOpen) rsPort.open(); - return; - } - else if (flowdata.data == "close") { - rsPort.close(); - return; - } - else if (flowdata.data == "clean") { - tasks = []; - return; - } - else if (flowdata.data == "buildtasks") { - //build & run - return; - } - else if (flowdata.data == "run") { - //durations = []; - - if (tasks.length == 0) { - - buildTasks(); - - if (rsPort.isOpen) { - interval = setInterval(runTasks, 100); - } - else { - instance.send(SEND_TO.debug, "port is not opened!!!"); - } - } - } - else { - //terminal data - object - //logger.debug("flowdata", flowdata.data); - - if (typeof flowdata.data === 'object') { - //logger.debug("dido", flowdata.data); - if (flowdata.data.hasOwnProperty("sender")) { - //data from dido_controller - if (flowdata.data.sender == "dido_controller") { - - if (flowdata.data.hasOwnProperty("cmd")) { - let cmd = flowdata.data.cmd; - - if (cmd == "buildTasks") { - clearInterval(interval); - - logger.debug("-->Cmd-mngr: BUILD TASKS"); - buildTasks(); - - //logger.debug("tasks:"); - //logger.debug(tasks); - - logger.debug("-->Cmd-mngr: RUN TASKS"); - interval = setInterval(runTasks, 5000); - } - else if (cmd == "reload_relays") { - loadRelaysData(flowdata.data.line); - - if (flowdata.data.dataChanged) { - if (!flowdata.data.value) { - reportOfflineNodeStatus(flowdata.data.line); - } - else { - reportOnlineNodeStatus(flowdata.data.line); - } - } - - } - else if (cmd == "rotary_switch_state") { - let value = flowdata.data.value; - - //state was changed - if (rotary_switch_state != value) { - if (value == "Off") { - //vyreportovat vsetky svietdla - reportOfflineNodeStatus(); - } - - rotary_switch_state = value; - } - } - else if (cmd == "lux_sensor") { - lux_sensor = parseInt(flowdata.data.value); - - // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? - if (lux_sensor < 100) { - - // we send lux_sensor value to all nodes: - let params = getParams(PRIORITY_TYPES.node_broadcast); - - params.recipient = 2;//2 broadcast, address = 0 - params.address = 0xffffffff;//Broadcast - - let ba = longToByteArray(lux_sensor); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - params.timestamp = PRIORITY_TYPES.node_broadcast; - params.info = "run broadcast: Actual Lux level from cabinet"; - params.register = 95;//Actual Lux level from cabinet - params.rw = 1;//write - - tasks.push(params); - - //process profiles - turnOnOffLinesAccordingToLuxSensor(lux_sensor); - } - } - else if (cmd == "state_of_breaker") { - //istic linie - let value = flowdata.data.value; - let line = parseInt(flowdata.data.line); - - let dataChanged = false; - if (state_of_breaker[line] != value) dataChanged = true; - - state_of_breaker[line] = value; + let bd = new Date(); + bd.setHours(parseInt(bhours), parseInt(bminutes), 0); - let status = "OK"; - if (value == "Off") status = "NOK"; - - if (dataChanged) { - - if (relaysData.hasOwnProperty(line)) { - let tbname = relaysData[line].tbname; - - if (value == "Off") sendNotification("Cmd-mngr: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); - else sendNotification("Cmd-mngr: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); - - //report status liniu - sendTelemetry({ status: status }, tbname) - - //current value - if (value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii - } - - } - } - else { - logger.debug("undefined cmd", cmd); - } - } - } - - return; - } - - //data from worksys - if (flowdata.data.hasOwnProperty("topic")) { - - let data = getNested(flowdata.data, "content", "data"); - - //if we get temperature in senica from senica-prod01 - let temperature = getNested(flowdata.data, "content", "senica_temperature"); - - if (temperature !== undefined) { - temperatureInSenica = temperature; - return; - } - - if (data === undefined) { - console.log("Invalid rpc command came from platform"); - return; - } - - let command = data.params.command; - let method = data.method; - let profile = data.params.payload; - if (profile == undefined) profile = ""; - let entity = data.params.entities[0]; - let entity_type = entity.entity_type; - let tbname = entity.tb_name; - - instance.send(SEND_TO.debug, flowdata.data); - logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); - logger.debug("----------------------------"); - - if (entity_type == "street_luminaire" || entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") { - if (method == "set_command") { - - //let command = data.params.command; - let value = data.params.payload.value; - - if (command == "dimming") { - - let nodeWasFound = false; - let keys = Object.keys(nodesData); - - //logger.debug("-----", keys); - - for (let i = 0; i < keys.length; i++) { - let node = keys[i]; - //logger.debug( node, nodesData[node], tbname); - - if (tbname == nodesData[node].tbname) { - let params = getParams(PRIORITY_TYPES.high_priority); - - value = parseInt(value); - if (value > 0) value = value + 128; - - params.type = "node-onetime-write"; - params.tbname = tbname; - params.address = node; - params.register = 1; - params.recipient = 1; - params.byte4 = value; - params.rw = 1; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'set dimming from platform'; - //params.debug = true; - - //debug(params); - logger.debug("dimming", params); - - tasks.push(params); - - setTimeout(function() { - - //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority - //a pridame aj vyreportovanie dimmingu - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 1; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read dimming (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - vykon - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 76; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Power (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - prud svietidla - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 75; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Current (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - power faktor - ucinnik - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 77; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read power factor (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - }, 4000); - - nodeWasFound = true; - break; - } - } - - if (!nodeWasFound) { - logger.debug("set dimming from platform", "unable to find tbname", tbname); - } - } - else { - instance.send(SEND_TO.debug, "undefined command " + command); - logger.debug("undefined command", command); - } - - return; - } - else if (method == "set_profile") { - //nastav profil nodu - logger.debug("-->set_profile for node", data.params); - logger.debug("------profile data", profile); - //instance.send(SEND_TO.debug, "set_profile" + command); - - let keys = Object.keys(nodesData); - for (let i = 0; i < keys.length; i++) { - let node = keys[i]; - if (tbname == nodesData[node].tbname) { - - if (profile != "") profile = JSON.stringify(profile); - dbNodes.modify({ processed: false, profile: profile }).where("node", node).make(function(builder) { - - builder.callback(function(err, response) { - - logger.debug("worksys - update node profile done", profile); - if (profile === "") logger.debug("worksys - update node profile done - profile is empty"); - - //profil úspešne prijatý pre node č. xx - sendNotification("Cmd-mngr", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); - - nodesData[node].processed = false; - nodesData[node].profile = profile; - - processNodeProfile(node); - }); - }); - } - } - } - else { - - instance.send(SEND_TO.debug, "unknown method " + method); - logger.debug("unknown method", method); - - return; - } - } - - //nastav profil linie z platformy - else if (entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") { - //profil linie - //relays.table line:number|tbname:string|contactor:number|profile:string - //najdeme line relaysData - - if (method == "set_profile") { - - logger.debug("-->set_profile for line", data.params); - logger.debug("profile data:", profile); - - let keys = Object.keys(relaysData); - for (let i = 0; i < keys.length; i++) { - let line = keys[i]; - if (tbname == relaysData[line].tbname) { - //zmazeme tasky - removeTask({ type: "relay", line: line }); - - if (profile != "") profile = JSON.stringify(profile); - dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) { - - builder.callback(function(err, response) { - - //update profile - logger.debug("worksys - update relay profile done:", profile); - instance.send(SEND_TO.debug, "worksys - update relay profile done"); - - relaysData[line].profile = profile; - - loadRelaysData(line) - logger.debug("loadRelaysData DONE for line", line); - - buildTasks({ processLineProfiles: true, line: line }); - - sendNotification("Cmd-mngr: set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); - }); - }); - break; - } - } - } - else if (method == "set_command") { - let value = data.params.payload.value; - - if (command === "switch") { - - // if we receive rpc from platform, to switch maintenance mode, we set SETTINGS.maintenance_mode flow variable to value; - if (entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; + return ad.getTime() - bd.getTime(); + }); - const relayObject = getObjectByTbValue(relaysData, tbname); - let line = 0; - if (isObject(relayObject)) line = relayObject.line; + console.log("line timepoints ........", time_points); - // v relaysData je contactor bud 0 alebo 1, ale z platformy prichadza true, false; - if (value == false) turnLine("off", line, "command received from platform"); - else turnLine("on", line, "command received from platform"); - } - } - else { - instance.send(SEND_TO.debug, "undefined method " + method); - logger.debug("undefined method", method); - } + let currentValue = 0; + if (time_points.length > 0) currentValue = time_points[time_points.length - 1].value; - return; - } - else { - instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); - logger.debug("UNKNOW entity_type", entity_type); - } - return; - } + monitor.info("-->comming events turn on/off lines:"); + for (let t = 0; t < time_points.length; t++) { - //terminal - if (!rsPort.isOpen) await rsPort.open(); + let start_time = new Date(); + let [hours, minutes] = time_points[t].start_time.split(':'); + start_time.setHours(parseInt(hours), parseInt(minutes), 0); - let params = flowdata.data.body; - if (params == undefined) { - //logger.debug("Cmd-mngr: flowdata.data.body is undefined"); - return; - } + //task is in the past + if (now.getTime() > start_time.getTime()) { + currentValue = time_points[t].value; - params.priority = PRIORITY_TYPES.terminal; - params.type = "cmd-terminal"; - params.tbname = ""; - params.timestamp = PRIORITY_TYPES.terminal; - params.addMinutesToTimestamp = 0;// do not repeat task!!! - params.debug = true; + //timepoint is in past, we add 24 hours + start_time.setDate(start_time.getDate() + 1); + } + + let params = getParams(); + params.type = "relay"; + params.line = parseInt(line); + params.value = time_points[t].value; + params.tbname = relaysData[line].tbname; + params.timestamp = start_time.getTime(); + + // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day + if (time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24 * 60; + + //astro timepoints will be recalculated dynamically: + params.timePointName = time_points[t].name; + + // if astro timepoint, we save time window: + if (['luxOn', 'luxOff', 'dusk', 'dawn'].includes(params.timePointName)) { + params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; + params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; + } + + if (params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; + else if (params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; + + params.debug = true; + + //turn on/off line + tasks.push(params); + monitor.info("TimePoint params: ", params.info, start_time); + } + + monitor.info("-->time_points final", line, time_points); + + //ensure to turn on/off according to calculated currentValue + let params = getParams(); + params.type = "relay"; + params.line = parseInt(line); + params.tbname = relaysData[line].tbname; + params.value = currentValue; + params.timestamp = i; + params.debug = true; + + //logger.debug(now.toLocaleString("sk-SK")); + monitor.info("-->currentValue for relay", line, currentValue); + + //turn on/off line + if (params.value == 0) params.info = "turn off line on startup: " + line; + else if (params.value == 1) params.info = "turn on line on startup: " + line; + + tasks.push(params); + + } catch (error) { + if (profilestr !== "") { + //errLogger.error(profilestr, error); + console.log(`Cmd_manager: Unable to process line profile ${line}. Error: `, error); + errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); + } else { + turnLine("off", line, "No line profile. Switching it off on startup"); + } + } - let timestamp = Date.now(); - params.refFlowdataKey = timestamp; - //params.refFlowdata = flowdata; - //refFlowdata = flowdata; + } + //logger.debug("tasks:"); + //logger.debug(tasks); + } - //console.log("flowdata", flowdata); - cleanUpRefFlowdataObj(); + //NOTE: PROCESS DEFAULT BROADCASTS - Time of dusk, Time of dawn, Actual Time + if (processBroadcast) { - refFlowdataObj[timestamp] = flowdata; + let d = new Date(); + let time = d.getTime(); + let sunCalcResult = calculateDuskDawn(); + + { + let params = getParams(); + + params.address = 0xffffffff;//broadcast + params.byte1 = sunCalcResult["dusk_hours"]; + params.byte2 = sunCalcResult["dusk_minutes"]; + params.recipient = 2;//2 broadcast, + params.register = 6;//Time of dusk + params.rw = 1;//write + params.type = "node-regular-write"; + params.timestamp = time + 60000; + params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk + params.info = "Broadcast-duskTime"; + + tasks.push(params); + } + + { + let params = getParams(); + + params.address = 0xffffffff;//broadcast + params.byte1 = sunCalcResult["dawn_hours"]; + params.byte2 = sunCalcResult["dawn_minutes"]; + params.recipient = 2; //2 broadcast + params.register = 7;//Time of dawn + params.rw = 1;//write + params.type = "node-regular-write"; + params.timestamp = time + 60001; + params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn + params.info = "Broadcast-dawnTime"; + + tasks.push(params); + } + + { + let params = getParams(); + + params.address = 0xffffffff;//broadcast + params.byte1 = d.getHours(); + params.byte2 = d.getMinutes(); + params.recipient = 2; //2 broadcast + params.register = 87;//Actual time + params.rw = 1;//write + params.type = "node-regular-write"; + params.timestamp = time + 60002; + params.addMinutesToTimestamp = 5; + params.info = "run broadcast: Actual time"; + + tasks.push(params); + } + + } + + //process nodes & tasks - read node's data + if (processNodes) { + + let time = Date.now(); + + for (let k in nodesData) { + let address = parseInt(k); + let tbname = nodesData[k].tbname; + let register = 0; + + for (let i = 0; i < listOfCommands.length; i++) { + + register = listOfCommands[i]; + let addMinutesToTimestamp = priorities[register]; + + let params = getParams(); + + params.address = address; + params.recipient = 1; + params.register = register; + params.type = register == 1 ? "node-dimming-read" : "node-regular-read"; + params.tbname = tbname; + params.timestamp = time + 5000 + i * 500 + addMinutesToTimestamp * 1000; //to make slight time difference + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "Node regular read command"; + + tasks.push(params); + } + + } + } + + + //niektore ulohy sa vygeneruju iba 1x pri starte!!! + if (!init) return; + + + //Master node FW version - modifies SETTINGS.masterNodeIsResponding + { + let params = getParams(); + params.type = "cmd-master"; + params.register = 4; + params.address = 0; + params.timestamp = 0; + params.addMinutesToTimestamp = 5; + params.tbname = SETTINGS.rvoTbName; + params.info = "Master node FW verzia"; + //params.debug = true; + + tasks.push(params); + } + + //kazdu hodinu skontrolovat nastavenie profilov + { + let params = getParams(); + params.type = "process_profiles"; + params.timestamp = Date.now() + 60001; + params.addMinutesToTimestamp = 60;//60 = every hour + params.info = "detekcia nespracovaných profilov linie a nodov"; + //params.debug = true; + + tasks.push(params); + } + + monitor.info("tasks created:", tasks.length); + } + + + /** + * We process line profile, where "astro_clock": true + * example profile: + * + "dawn_lux_sensor": true, + "dusk_lux_sensor": true, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 0, + "dusk_astro_clock_offset": 10, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + + * if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line. + * if dusk: we do oposite + * + * dawn: usvit - lux je nad hranicou - vypnem + * dusk: sumrak - lux je pod hranicou - zapnem + */ + function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) { + + let now = new Date(); + let currentTimestamp = now.getTime(); + let keys = Object.keys(relaysData); + + for (let i = 0; i < keys.length; i++) { + + let line = keys[i]; //line is turned off by default + let profilestr = relaysData[line].profile; + const contactor = relaysData[line].contactor; + + try { + + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); + + if (profile.astro_clock == true) { + let sunCalcResult = calculateDuskDawn(now, line); + + //usvit + if (profile.dawn_lux_sensor == true) { + let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut + let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); + + if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { + if (lux_sensor_value > profile.dawn_lux_sensor_value) { + if (contactor) turnLine("off", line, "Profile: dawn - turnOff line according to lux sensor"); + } + } + } + + //sumrak + if (profile.dusk_lux_sensor == true) { + let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); + let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); + + if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { + if (lux_sensor_value < profile.dusk_lux_sensor_value) { + if (!contactor) turnLine("on", line, "Profile: dusk - turnOn line according to lux sensor"); + } + } + } + + } + + } catch (error) { + if (profilestr !== "") monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); + } + + } + + } + + /** + * function updates status and time_of_last_communication of node in the dbNodes + * it only updates if conditions are met + * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) + * if newStatus of node is always receiving false, and it is already for more than SETTINGS.node_status_nok_time value, we update status to "NOK" in tb + * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). + */ + function updateNodeStatus(node, newStatus) { + //MASTER + if (node == 0) return; + + let nodeObj = nodesData[node]; + if (nodeObj == undefined) return; + + let nodeCurrentStatus = nodeObj.status; + const now = Date.now(); + + let data = null; + + if (nodeCurrentStatus === "OFFLINE") { + data = { status: newStatus }; + nodeDbStatusModify(node, data); + return; + } + else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; + else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { + data = { time_of_last_communication: now }; + nodeDbStatusModify(node, data); + return; + } + else if (newStatus == false && nodeCurrentStatus == false) return true; + else if (newStatus == false && nodeCurrentStatus == true) { + if (nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; + else { + data = { status: newStatus }; + nodeDbStatusModify(node, data); + return true; + } + } + else if (newStatus == true && nodeCurrentStatus == false) { + data = { status: newStatus, time_of_last_communication: now }; + nodeDbStatusModify(node, data); + return; + } + + } + + + function nodeDbStatusModify(node, data) { + dbNodes.modify(data).where("node", node).callback(function(err, response) { + if (!err) { + nodesData[node] = { ...nodesData[node], ...data }; + } + }); + } + + + async function runTasks() { + + clearInterval(interval); + + let currentTimestamp = Date.now(); + + //report dusk, dawn--------------------------------- + if (reportDuskDawn.dusk_time < currentTimestamp) { + //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund + if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { + //reportovali sme? + if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { + //sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); + reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; + } + } + + var nextDay = new Date(); + nextDay.setDate(nextDay.getDate() + 1); + + sunCalcResult = calculateDuskDawn(nextDay); + reportDuskDawn.dusk_time = sunCalcResult.dusk_time; + } + + if (reportDuskDawn.dawn_time < currentTimestamp) { + //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund + if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { + //reportovali sme? + if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { + //sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); + reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; + } + } + + var nextDay = new Date(); + nextDay.setDate(nextDay.getDate() + 1); + + sunCalcResult = calculateDuskDawn(nextDay); + reportDuskDawn.dawn_time = sunCalcResult.dawn_time; + + } + //-------------------------------------------------------- + + //sort tasks based on timestamp + tasks.sort(function(a, b) { + if (a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) { + return a.priority - b.priority; + } + return a.timestamp - b.timestamp; + }); + + if (tasks.length == 0) { + instance.send(SEND_TO.debug, "no tasks created"); + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + if (!rsPort.isOpen) { + instance.send(SEND_TO.debug, "!rsPort.isOpen"); + //await rsPort.open(); + //console.log("Cmd_manager - !rsPort.isOpen"); + } + + let currentTask = tasks[0]; + + if (currentTask.debug) { + //logger.debug("--->task to process", currentTask); + } + + if (currentTask.timestamp <= currentTimestamp) { + let params = { ...tasks[0] }; + + //allow terminal commands + if (SETTINGS.maintenance_mode && params.type !== "cmd-terminal") { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + let type = params.type; + let tbname = params.tbname; + let node = params.address; + + let register = params.register; + let line = null; + let itIsNodeCommand; + + if (nodesData[node] !== undefined) { + line = nodesData[node].line; + itIsNodeCommand = true; + } + + if (params.line !== undefined) line = params.line; + + if (params.addMinutesToTimestamp > 0 || params.timePointName) { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } else { + tasks.shift(); + } + + //kontrola nespracovanych profilov nodov + if (type == "process_profiles") { + //na vsetky zapnutych liniach sa spracuju nespracovane profily nodov + loadRelaysData(); + interval = setInterval(runTasks, SHORT_INTERVAL); + return; + } + + //relay + if (type == "relay") { + + const timePointName = params.timePointName; + const value = params.value; + + let date = new Date(); + date.setDate(date.getDate() + 1);//next day + + let sunCalcResult; + if (timePointName) sunCalcResult = calculateDuskDawn(date, params.line); + + if (timePointName == "dawn") { + tasks[0].timestamp = sunCalcResult.dawn_time; + } + else if (timePointName == "dusk") { + tasks[0].timestamp = sunCalcResult.dusk_time; + } + else if (timePointName == "luxOn") { + tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; + } + else if (timePointName == "luxOff") { + tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; + } + else if (timePointName == "profileTimepoint") { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } + + let info = "aplikovany bod profilu"; + let onOrOff = ""; + value == 1 ? onOrOff = "on" : onOrOff = "off"; + + turnLine(onOrOff, params.line, info); + + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + if (!SETTINGS.masterNodeIsResponding) { + //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal cmd-master + errorHandler.sendMessageToService("Master node is not responding"); + + let stop = true; + + if (type === "cmd-terminal" || type === "cmd-master") stop = false; + if (stop) { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + } + + let contactorStatus = 1; + if (relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; + + if (line === 0 || contactorStatus === 0 || FLOW.deviceStatus.state_of_breaker[line] === "Off") { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors + //check if rotary_switch_state == "Off" + // state_of_braker: disconnected = true? + + if (!rsPort.isOpen) { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + //RE-CALCULATE VALUES + //set actual time for broadcast + if (register == 87 && params.recipient === 2) { + var d = new Date(); + params.byte1 = d.getHours();//h + params.byte2 = d.getMinutes();//m + } + + //SET DUSK/DAWN FOR BROADCAST + //Time of dusk + if (register == 6 && params.recipient === 2) { + + if (type != "cmd-terminal") { + let sunCalcResult = calculateDuskDawn(); + params.byte1 = sunCalcResult["dusk_hours"];//h + params.byte2 = sunCalcResult["dusk_minutes"];//m + } + } + + //Time of dawn + if (register == 7 && params.recipient === 2) { + if (type != "cmd-terminal") { + let sunCalcResult = calculateDuskDawn(); + params.byte1 = sunCalcResult["dawn_hours"];//h + params.byte2 = sunCalcResult["dawn_minutes"];//m + } + } + //----------------------- + + instance.send(SEND_TO.debug, "address: " + node + " register:" + register + "type: " + type); + + var startTime, endTime; + startTime = new Date(); + + let saveToTb = true; + if (!tbname) saveToTb = false; + + let resp = com_generic(node, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); + let readBytes = 11; + let timeout = 4000; + + + // await keyword is important, otherwise incorrect data is returned! + await writeData(rsPort, resp, readBytes, timeout).then(function(data) { + + //sometimes happens, that status of node changes even if line was turned off and should be offline. To prevent this, we return if line contactor is 0: + if (itIsNodeCommand && line && relaysData[line].contactor !== 1) return; + + endTime = new Date(); + var timeDiff = endTime - startTime; + + //data je array z 11 bytov: 1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data a 2 byty CRC + let dataBytes = data.slice(5, 9); + let result = detectIfResponseIsValid(data); + + if(register === 79) console.log("node responsee: ", dataBytes); + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let message = result.message; // OK, NOK + let message_type = result.type; + + if (params.hasOwnProperty("debug")) { + if (params.debug) { + console.log("detected response:", result); + logger.debug("Cmd-mngr: writeData done " + message_type + " duration: " + timeDiff + " type: " + params.debug, params, result); + } + } + + let values = {}; + + //CMD FINISHED + if (message == "OK") { + + updateNodeStatus(node, true); + + //write + if (type == "set_node_profile") { + let result = cmdCounterResolve(node); + if (result == 0) { + dbNodes.modify({ processed: true }).where("node", node).callback(function(err, response) { + + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); + + logger.debug("--> profil úspešne odoslaný na node č. " + node); + nodesData[node].processed = true; + nodeProfileSendFail.delete(node); + }); + } + } + + //parse read response + if (params.rw == 0) { + values = processResponse(register, dataBytes); //read + console.log("command params: ", params.address, register); + } + + if (itIsNodeCommand) { + values.comm_status = "OK"; + values.status = "OK"; + nodesData[node].readout = { ...nodesData[node].readout, ...values }; + } + + //master node + if (node == 0) { + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); + SETTINGS.masterNodeIsResponding = true; + if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; + } + + if (params.debug) { + //logger.debug("saveToTb", saveToTb, tbname, values); + } + + if (saveToTb && type != "node-regular-read") { + sendTelemetry(values, tbname); + } + else { + if (type == "cmd-terminal") { + terminalCommandResponse(params, "SUCCESS", data); + } + } + + } + else { + + terminalCommandResponse(params, "ERROR", data); + handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); - //fix - //params.address = params.adress; - logger.debug("received from terminal", params); - logger.debug("date/time:", new Date()); - logger.debug("tasks length:", tasks.length); + if (params.hasOwnProperty("debug")) { + if (params.debug) { + //logger.debug("writeData err: ", error, result, params); + logger.debug("writeData err: ", tbname, node, register, values); + } + } + + //logger.debug(error, result, params); + } + }).catch(function(reason) { + + //console.log("writeData catch exception", reason); + instance.send(SEND_TO.debug, reason); + + terminalCommandResponse(params, "FAILURE", null, reason); + handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); + + if (params.hasOwnProperty("debug")) { + if (params.debug) { + logger.debug("-->WRITE FAILED: " + reason, params.debug, params); + } + } + + }); + + } + else { + if (currentTask.debug) { + // currentTask.timestamp <= currentTimestamp && logger.debug("currentTask is not processed - task is in the future", currentTask); + } + + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + //console.log("----->runTasks - setInterval", new Date()); + interval = setInterval(runTasks, SHORT_INTERVAL); + } + + + function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb) { + + let node = params.address; + let register = params.register; + let type = params.type; + let tbName = params.tbname; + if (!tbName) return; + + let values = {}; + + let updateStatus = updateNodeStatus(node, false); + + if (itIsNodeCommand) { + values.comm_status = "NOK"; + nodesData[node].readout.comm_status = "NOK"; + } + + if (updateStatus) { + values.status = "NOK"; + nodesData[node].readout.status = "NOK"; + } + + if (type === "node-regular-read") return; + + //master node + if (node == 0) { + sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); + logger.debug("master_node_is_not_responding", params); + SETTINGS.masterNodeIsResponding = false; + + if (register == 4) values["master_node_version"] = "NOK"; + } + + if (type == "set_node_profile") { + delete cmdCounter[node]; + logger.debug("profil nebol úspešne odoslaný na node č. ", params); + + if (!nodeProfileSendFail.has(node)) { + sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); + nodeProfileSendFail.add(node); + } + } + + // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); + if (saveToTb) { + sendTelemetry(values, tbName); + } + + } + + function sendNodesData() { + Object.keys(nodesData).forEach(node => { + if (nodesData[node]["status"] !== "OFFLINE") { + sendTelemetry(nodesData[node].readout, nodesData[node].tbname); + nodesData[node].readout = {}; + } + }) + } + + + /** + * function handles requests from terminal + * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. + * FAILURE means, that we got into catch block of writeData function. + */ + function terminalCommandResponse(params, responseType, data = null, reason = "") { //success, error, failure + + if (params.refFlowdataKey === undefined) { + //console.log("params.refFlowdataKey is undefined", params); + return; + } + else { + console.log("params.refFlowdataKey: ", params); + } + + let message = null; + let type = null; + + switch (responseType) { + case "SUCCESS": + message = "cmd-terminal SUCCESS"; + type = "SUCCESS"; + break; + case "ERROR": + message = "cmd-terminal FAILED"; + type = "ERROR"; + break; + case "FAILURE": + message = "ERROR WRITE FAILED: " + reason; + type = "ERROR"; + break; + default: + type = undefined; + } + + logger.debug(message); + logger.debug(params); + + //make http response + let responseObj = {} + responseObj["type"] = type; + + if (responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; + else responseObj["bytes"] = data; + + let refFlowdata = refFlowdataObj[params.refFlowdataKey]; //holds reference to httprequest flowdata + if (refFlowdata) { + refFlowdata.data = responseObj; + instance.send(SEND_TO.http_response, refFlowdata); + } + } + + + /** + * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function + */ + function reportEdgeDateTimeAndNumberOfLuminaires() { + + //Number of ok and nok nodes on platform does not equals to total number of nodes. + //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + + const ts = Date.now(); + const keys = Object.keys(nodesData_clone); + + const number_of_luminaires = keys.length; + let number_of_ok_luminaires = 0; + let number_of_nok_luminaires = 0; + + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + let nodeObj = nodesData_clone[key]; + if (nodeObj.tbname == undefined) continue; + + if (nodeObj.status === "OFFLINE") { + nodeObj.node_status_before_offline === true ? number_of_ok_luminaires++ : number_of_nok_luminaires++; + } + else if (nodeObj.status == true) number_of_ok_luminaires++; + else number_of_nok_luminaires++; + + } + + const values = { + "number_of_luminaires": number_of_luminaires, + "number_of_ok_luminaires": number_of_ok_luminaires, + "number_of_nok_luminaires": number_of_nok_luminaires, + "edge_date_time": ts - ts % 60000 //round to full minute + }; + + sendTelemetry(values, SETTINGS.rvoTbName, ts); + } + + + function handleRsPort() { + + console.log("cmd_man: handleRsPort called"); + //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" + // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM + // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI + + if (SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; + console.log('SETTINGS.serial_port', SETTINGS.serial_port); + //rsPort = new SerialPort({path: `/dev/${SETTINGS.serial_port}`, baudRate: 57600, autoOpen: false }); + rsPort = new SerialPort({ path: "/dev/ttyUSB0", baudRate: 9600, autoOpen: false }); + //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit + //rsPort.setMaxListeners(0); + + rsPort.on('open', async function() { + + logger.debug("CMD manager - rsPort opened success"); + console.log("CMD manager - rsPort opened success"); + + await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { + instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); + + logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); + + }).catch(function(reason) { + instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); + console.log("cmd_man: rsport error", reason); + }); + }); + + rsPort.on('error', function(err) { + + //TODO report to service!!! + //errLogger.error(exports.title, "unable to open port", SETTINGS.serial_port, err.message); + errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); + console.log("cmd_manager: unable to open rsport", SETTINGS.serial_port, err.message); + instance.send(SEND_TO.debug, err.message); + }); + + rsPort.on("close", () => { + setTimeout(() => rsPort.open(), 1000); + }); + + + rsPort.open(function(err) { + if (err) console.log('rsport open error', err); + }) + } + + + instance.on("close", () => { + clearInterval(interval); + clearInterval(customTasksInterval); + clearInterval(setCorrectTime); + clearInterval(sendNodeReadout); + clearInterval(accelerometerInterval); + rsPort.close(); + }); + + instance.on("0", _ => { + main(); + }) + + instance.on("1", async function(flowdata) { + + //instance.send(SEND_TO.debug, "on Data"); + //instance.send(SEND_TO.debug, flowdata); + + //logger.debug(flowdata.data); + + //just testing functions + if (flowdata.data == "open") { + if (!rsPort.isOpen) rsPort.open(); + return; + } + else if (flowdata.data == "close") { + rsPort.close(); + return; + } + else if (flowdata.data == "clean") { + tasks = []; + return; + } + else if (flowdata.data == "buildtasks") { + //build & run + return; + } + else if (flowdata.data == "run") { + //durations = []; + + if (tasks.length == 0) { + + buildTasks(); + + if (rsPort.isOpen) { + interval = setInterval(runTasks, 100); + } + else { + instance.send(SEND_TO.debug, "port is not opened!!!"); + } + } + } + else { + //terminal data - object + //logger.debug("flowdata", flowdata.data); + + if (typeof flowdata.data === 'object') { + //logger.debug("dido", flowdata.data); + if (flowdata.data.hasOwnProperty("sender")) { + //data from dido_controller + if (flowdata.data.sender == "dido_controller") { + + if (flowdata.data.hasOwnProperty("cmd")) { + let cmd = flowdata.data.cmd; + + if (cmd == "buildTasks") { + clearInterval(interval); + + logger.debug("-->CMD MANAGER - BUILD TASKS"); + buildTasks(); + + //logger.debug("tasks:"); + //logger.debug(tasks); + + logger.debug("-->CMD MANAGER - RUN TASKS"); + interval = setInterval(runTasks, 5000); + } + else if (cmd == "reload_relays") { + loadRelaysData(flowdata.data.line); + + if (flowdata.data.dataChanged) { + if (!flowdata.data.value) { + reportOfflineNodeStatus(flowdata.data.line); + } + else { + reportOnlineNodeStatus(flowdata.data.line); + } + } + + } + else if (cmd == "rotary_switch_state") { + let value = flowdata.data.value; + + //state was changed + if (rotary_switch_state != value) { + if (value == "Off") { + //vyreportovat vsetky svietdla + reportOfflineNodeStatus(); + } + + rotary_switch_state = value; + } + } + else if (cmd == "lux_sensor") { + lux_sensor = parseInt(flowdata.data.value); + + // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? + if (lux_sensor < 100) { + + // we send lux_sensor value to all nodes: + let params = getParams(PRIORITY_TYPES.node_broadcast); + + params.recipient = 2;//2 broadcast, address = 0 + params.address = 0xffffffff;//Broadcast + + let ba = longToByteArray(lux_sensor); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + params.timestamp = PRIORITY_TYPES.node_broadcast; + params.info = "run broadcast: Actual Lux level from cabinet"; + params.register = 95;//Actual Lux level from cabinet + params.rw = 1;//write + + tasks.push(params); + + //process profiles + turnOnOffLinesAccordingToLuxSensor(lux_sensor); + } + } + else if (cmd == "state_of_breaker") { + //istic linie + let value = flowdata.data.value; + let line = parseInt(flowdata.data.line); + + let dataChanged = false; + if (state_of_breaker[line] != value) dataChanged = true; + + state_of_breaker[line] = value; - //tasks = []; + let status = "OK"; + if (value == "Off") status = "NOK"; + + if (dataChanged) { + + if (relaysData.hasOwnProperty(line)) { + let tbname = relaysData[line].tbname; + + if (value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + + //report status liniu + sendTelemetry({ status: status }, tbname) + + //current value + if (value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii + } + + } + } + else { + logger.debug("undefined cmd", cmd); + } + } + } + + return; + } + + //data from worksys + if (flowdata.data.hasOwnProperty("topic")) { + + let data = getNested(flowdata.data, "content", "data"); + + //if we get temperature in senica from senica-prod01 + let temperature = getNested(flowdata.data, "content", "senica_temperature"); + + if (temperature !== undefined) { + temperatureInSenica = temperature; + return; + } + + if (data === undefined) { + console.log("Invalid rpc command came from platform"); + return; + } + + let command = data.params.command; + let method = data.method; + let profile = data.params.payload; + if (profile == undefined) profile = ""; + let entity = data.params.entities[0]; + let entity_type = entity.entity_type; + let tbname = entity.tb_name; + + instance.send(SEND_TO.debug, flowdata.data); + logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); + logger.debug("----------------------------"); + + if (entity_type == "street_luminaire" || entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") { + if (method == "set_command") { + + //let command = data.params.command; + let value = data.params.payload.value; + + if (command == "dimming") { + + let nodeWasFound = false; + let keys = Object.keys(nodesData); + + //logger.debug("-----", keys); + + for (let i = 0; i < keys.length; i++) { + let node = keys[i]; + //logger.debug( node, nodesData[node], tbname); + + if (tbname == nodesData[node].tbname) { + let params = getParams(PRIORITY_TYPES.high_priority); + + value = parseInt(value); + if (value > 0) value = value + 128; + + params.type = "node-onetime-write"; + params.tbname = tbname; + params.address = node; + params.register = 1; + params.recipient = 1; + params.byte4 = value; + params.rw = 1; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'set dimming from platform'; + //params.debug = true; + + //debug(params); + logger.debug("dimming", params); + + tasks.push(params); + + setTimeout(function() { + + //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority + //a pridame aj vyreportovanie dimmingu + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 1; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read dimming (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - vykon + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 76; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Power (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - prud svietidla + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 75; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Current (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - power faktor - ucinnik + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 77; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read power factor (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + }, 4000); + + nodeWasFound = true; + break; + } + } + + if (!nodeWasFound) { + logger.debug("set dimming from platform", "unable to find tbname", tbname); + } + } + else { + instance.send(SEND_TO.debug, "undefined command " + command); + logger.debug("undefined command", command); + } + + return; + } + else if (method == "set_profile") { + //nastav profil nodu + logger.debug("-->set_profile for node", data.params); + logger.debug("------profile data", profile); + //instance.send(SEND_TO.debug, "set_profile" + command); + + let keys = Object.keys(nodesData); + for (let i = 0; i < keys.length; i++) { + let node = keys[i]; + if (tbname == nodesData[node].tbname) { + + if (profile != "") profile = JSON.stringify(profile); + dbNodes.modify({ processed: false, profile: profile }).where("node", node).callback(function(err, response) { + + logger.debug("worksys - update node profile done", profile); + if (profile === "") logger.debug("worksys - update node profile done - profile is empty"); + + //profil úspešne prijatý pre node č. xx + sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); + + nodesData[node].processed = false; + nodesData[node].profile = profile; + + processNodeProfile(node); + }); + } + } + } + else { + + instance.send(SEND_TO.debug, "unknown method " + method); + logger.debug("unknown method", method); + + return; + } + } + + //nastav profil linie z platformy + else if (entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") { + //profil linie + //relays.table line:number|tbname:string|contactor:number|profile:string + //najdeme line relaysData + + if (method == "set_profile") { + + logger.debug("-->set_profile for line", data.params); + logger.debug("profile data:", profile); + + let keys = Object.keys(relaysData); + for (let i = 0; i < keys.length; i++) { + let line = keys[i]; + if (tbname == relaysData[line].tbname) { + //zmazeme tasky + removeTask({ type: "relay", line: line }); + + if (profile != "") profile = JSON.stringify(profile); + dbRelays.modify({ profile: profile }).where("line", line).callback(function(err, response) { + + //update profile + logger.debug("worksys - update relay profile done:", profile); + instance.send(SEND_TO.debug, "worksys - update relay profile done"); + + relaysData[line].profile = profile; + + loadRelaysData(line) + logger.debug("loadRelaysData DONE for line", line); + + buildTasks({ processLineProfiles: true, line: line }); + + sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); + }); + break; + } + } + } + else if (method == "set_command") { + let value = data.params.payload.value; + + if (command === "switch") { + + // if we receive rpc from platform, to switch maintenance mode, we set SETTINGS.maintenance_mode flow variable to value; + if (entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; + + const relayObject = getObjectByTbValue(relaysData, tbname); + let line = 0; + if (isObject(relayObject)) line = relayObject.line; - //add to tasks - tasks.push(params); + // v relaysData je contactor bud 0 alebo 1, ale z platformy prichadza true, false; + if (value == false) turnLine("off", line, "command received from platform"); + else turnLine("on", line, "command received from platform"); + } + } + else { + instance.send(SEND_TO.debug, "undefined method " + method); + logger.debug("undefined method", method); + } - } - } - }) + return; + } + else { + instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); + logger.debug("UNKNOW entity_type", entity_type); + } + return; + } + //terminal + if (!rsPort.isOpen) await rsPort.open(); - //function gets value of a nested property in an object and returns undefined if it does not exists: - function getNested(obj, ...args) { - return args.reduce((obj, level) => obj && obj[level], obj) - } + let params = flowdata.data.body; + if (params == undefined) { + //logger.debug("CMD manager flowdata.data.body is undefined"); + return; + } + params.priority = PRIORITY_TYPES.terminal; + params.type = "cmd-terminal"; + params.tbname = ""; + params.timestamp = PRIORITY_TYPES.terminal; + params.addMinutesToTimestamp = 0;// do not repeat task!!! + params.debug = true; - /** - * setCorrectTime function runs once per hour - * If it is 3 o'clock, it sets actual time, which is got from services - * https://service-prod01.worksys.io/gettime - * If also detects Read Only Filesystem once a day - */ - function setCorrectPlcTimeOnceADay() { + let timestamp = Date.now(); + params.refFlowdataKey = timestamp; + //params.refFlowdata = flowdata; + //refFlowdata = flowdata; - const currentTime = new Date(); - if (currentTime.getHours() != 3) return; + //console.log("flowdata", flowdata); - RESTBuilder.make(function(builder) { + cleanUpRefFlowdataObj(); - if (!builder) return; + refFlowdataObj[timestamp] = flowdata; - builder.method('GET'); - builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); + //fix + //params.address = params.adress; + logger.debug("received from terminal", params); + logger.debug("date/time:", new Date()); + logger.debug("tasks length:", tasks.length); - builder.callback(function(err, response, output) { + //tasks = []; - if (err) { - console.log(err); - return; - } + //add to tasks + tasks.push(params); - const res = output.response; + } + } + }) - try { - const obj = JSON.parse(res); - let d = new Date(obj.date); + //function gets value of a nested property in an object and returns undefined if it does not exists: + function getNested(obj, ...args) { + return args.reduce((obj, level) => obj && obj[level], obj) + } - const now = new Date(); - let diffInMinutes = now.getTimezoneOffset(); - console.log("---->TimezoneOffset", diffInMinutes); + /** + * setCorrectTime function runs once per hour + * If it is 3 o'clock, it sets actual time, which is got from services + * https://service-prod01.worksys.io/gettime + * If also detects Read Only Filesystem once a day + */ + function setCorrectPlcTimeOnceADay() { - if (d instanceof Date) { + const currentTime = new Date(); + if (currentTime.getHours() != 3) return; - // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); + RESTBuilder.make(function(builder) { - let year = d.getFullYear(); - let month = addZeroBefore(d.getMonth() + 1); - let day = addZeroBefore(d.getDate()); + if (!builder) return; - let hours = addZeroBefore(d.getHours()); - let minutes = addZeroBefore(d.getMinutes()); - let seconds = addZeroBefore(d.getSeconds()); + builder.method('GET'); + builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); - let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + builder.callback(function(err, response, output) { - exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); - console.log(dateStr); + if (err) { + console.log(err); + return; + } - monitor.info("failed timedatectl set-time", err, stderr); - } - else { - monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); - } + const res = output.response; - }); - } + try { - } catch (error) { - logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); - monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); - } + const obj = JSON.parse(res); + let d = new Date(obj.date); - // we detect readOnlyFileSystem once an hour as well - detectReadOnlyFilesystem(); + const now = new Date(); - }); - }); + let diffInMinutes = now.getTimezoneOffset(); + console.log("---->TimezoneOffset", diffInMinutes); - } + if (d instanceof Date) { + // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); - function detectReadOnlyFilesystem() { - exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); + let year = d.getFullYear(); + let month = addZeroBefore(d.getMonth() + 1); + let day = addZeroBefore(d.getDate()); - } else { - //console.log("Read-only", stdout); + let hours = addZeroBefore(d.getHours()); + let minutes = addZeroBefore(d.getMinutes()); + let seconds = addZeroBefore(d.getSeconds()); - let lines = stdout + ""; - lines = lines.split("\n"); + let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - let readOnlyDetected = ""; - for (let i = 0; i < lines.length; i++) { - if (lines[i].startsWith("/dev/mmcblk0p2")) { - readOnlyDetected = lines[i]; - } - } + exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); + console.log(dateStr); - if (readOnlyDetected !== "") { - errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); - monitor.info("Read only filesystem detected"); - } + monitor.info("failed timedatectl set-time", err, stderr); + } + else { + monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); + } - } - }); - } + }); + } + } catch (error) { + logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); + monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); + } + // we detect readOnlyFileSystem once an hour as well + detectReadOnlyFilesystem(); + }); + }); + } + function detectReadOnlyFilesystem() { + exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); + } else { + //console.log("Read-only", stdout); - ///helper functions - function sendTelemetry(values, tbname, date = Date.now()) { - const dataToTb = { - [tbname]: [ - { - "ts": date, - "values": values - } - ] - } + let lines = stdout + ""; + lines = lines.split("\n"); - tbHandler.sendToTb(dataToTb, instance); - } + let readOnlyDetected = ""; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith("/dev/mmcblk0p2")) { + readOnlyDetected = lines[i]; + } + } - function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) { + if (readOnlyDetected !== "") { + errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); + monitor.info("Read only filesystem detected"); + } - if (date === undefined) date = new Date(); - //if(duskOffset === undefined) duskOffset = 0; - //if(dawnOffset === undefined) dawnOffset = 0; + } + }); + } - //let line = keys[i]; - let profilestr = ""; - if (relaysData[line] != undefined) profilestr = relaysData[line].profile; - let result = {}; - var times = SunCalc.getTimes(date, latitude, longitude); - let dawn = new Date(times.sunrise);//usvit - let dusk = new Date(times.sunset);//sumrak - //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 - //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 - let dusk_astro_clock_offset = duskOffset;//minutes - let dawn_astro_clock_offset = dawnOffset;//minutes - try { + ///helper functions + function sendTelemetry(values, tbname, date = Date.now()) { + const dataToTb = { + [tbname]: [ + { + "ts": date, + "values": values + } + ] + } - let profile = JSON.parse(profilestr); - if (Object.keys(profile).length === 0) throw ("profile is not defined"); + tbHandler.sendToTb(dataToTb, instance); + } - //Jednoduchý režim - if (profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) { + function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) { - } + if (date === undefined) date = new Date(); + //if(duskOffset === undefined) duskOffset = 0; + //if(dawnOffset === undefined) dawnOffset = 0; - //Režim astrohodín - if (profile.astro_clock == true) { - //if(profile.dusk_lux_sensor == false) - { - if (profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt(profile.dusk_astro_clock_offset); - } + //let line = keys[i]; + let profilestr = ""; + if (relaysData[line] != undefined) profilestr = relaysData[line].profile; - //if(profile.dawn_lux_sensor == false) - { - if (profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt(profile.dawn_astro_clock_offset); - } + let result = {}; - } + var times = SunCalc.getTimes(date, latitude, longitude); + let dawn = new Date(times.sunrise);//usvit + let dusk = new Date(times.sunset);//sumrak - //dusk - súmrak - //down, sunrise - svitanie - } catch (error) { - if (profilestr != "") { - logger.debug(profilestr); - logger.debug(error); - } - } + //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 + //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 - result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); - dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset * 60000); - dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset * 60000); + let dusk_astro_clock_offset = duskOffset;//minutes + let dawn_astro_clock_offset = dawnOffset;//minutes - result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dusk_hours = dusk.getHours(); - result.dusk_minutes = dusk.getMinutes(); + try { - result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); - result.dawn_hours = dawn.getHours(); - result.dawn_minutes = dawn.getMinutes(); + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("profile is not defined"); - result.dusk_time = dusk.getTime(); - result.dawn_time = dawn.getTime(); + //Jednoduchý režim + if (profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) { - result.dusk_astro_clock_offset = dusk_astro_clock_offset; - result.dawn_astro_clock_offset = dawn_astro_clock_offset; + } - return result; - } + //Režim astrohodín + if (profile.astro_clock == true) { + //if(profile.dusk_lux_sensor == false) + { + if (profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt(profile.dusk_astro_clock_offset); + } + //if(profile.dawn_lux_sensor == false) + { + if (profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt(profile.dawn_astro_clock_offset); + } - function processResponse(register, bytes) { - - let values = {}; - - let byte3 = bytes[0]; - let byte2 = bytes[1]; - let byte1 = bytes[2]; - let byte0 = bytes[3]; - - //status - if (register == 0) { - let statecode = bytesToInt(bytes); - values = { "statecode": statecode }; - return values; - } - - //Dimming, CCT - else if (register == 1) { - let brightness = 0; - let dimming = byte0; - if (dimming > 128) { - //dimming = -128; - brightness = dimming - 128; - } - - //cct - //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 - let cct; - if (byte3 == 1) cct = byte2 * 256 + byte1; - else cct = bytesToInt(bytes.slice(0, 3)); - - //cct podla auditu - - values["dimming"] = brightness; - return values; - } - - // - else if (register == 4) { - values["master_node_version"] = bytes[1] + "." + bytes[2]; - //logger.debug("FW Version", register, bytes); - } - - //Napätie - else if (register == 74) { - let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); - values["voltage"] = Number(voltage); - } - - //Prúd - else if (register == 75) { - let current = bytesToInt(bytes); - values["current"] = current; - } - - //výkon - else if (register == 76) { - let power = (bytesToInt(bytes) * 0.1).toFixed(2); - values["power"] = Number(power); - } - - //účinník - else if (register == 77) { - let power_factor = Math.cos(bytesToInt(bytes) * 0.1 * (Math.PI / 180)).toFixed(2); - values["power_factor"] = Number(power_factor); - } - - //frekvencia - else if (register == 78) { - let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); - values["frequency"] = Number(frequency); - } - - //energia - else if (register == 79) { - let energy = bytesToInt(bytes); - values["energy"] = energy / 1000; //energia v kWh -> delit 1000 - } - - //doba života - else if (register == 80) { - let lifetime = (bytesToInt(bytes) / 60).toFixed(2); - values["lifetime"] = Number(lifetime); - } - - //nastavenie profilu - else if (register == 8) { - let time_schedule_settings = bytesToInt(bytes); - values["time_schedule_settings"] = time_schedule_settings; - } - - //naklon - nateraz sa z nodu nevycitava! kvoli problemom s accelerometrom a vracanymi hodnotami, posielame temp a x y z vo funkcii accelerometerData() - else if (register == 84) { - values["temperature"] = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; - values["inclination_x"] = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; - values["inclination_y"] = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; - values["inclination_z"] = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; - } - - //FW verzia nodu - else if (register == 89) { - //formát: "Byte3: Byte2.Byte1 (Byte0)" - values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; - } - - else if (register == 87 || register == 6 || register == 7) { - var d = new Date(); - d.setHours(byte3, byte2, 0, 0); - let timestamp = d.getTime(); - - //aktuálny čas - if (register == 87) values["actual_time"] = timestamp; - //čas súmraku - else if (register == 6) values["dusk_time"] = timestamp; - //čas úsvitu - else if (register == 7) values["dawn_time"] = timestamp; - } - - return values; - } - - - //byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB - function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { - let resp = []; - - let cmd = register; - - if (typeof adresa === 'string') adresa = parseInt(adresa); - if (typeof byte1 === 'string') byte1 = parseInt(byte1); - if (typeof byte2 === 'string') byte2 = parseInt(byte2); - if (typeof byte3 === 'string') byte3 = parseInt(byte3); - if (typeof byte4 === 'string') byte4 = parseInt(byte4); - - if (rw === 0) { - cmd = cmd + 0x8000; - } - - //master - if (rec === 0) adresa = 0; - - if (rec === 2) { - adresa = 0xffffffff;//Broadcast - } - - //recipient - if (rec === 3) { - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push(adresa & 0xFF);//band - } - else { - resp.push((adresa >> 24) & 0xFF);//rshift - resp.push((adresa >> 16) & 0xFF); - resp.push((adresa >> 8) & 0xFF); - resp.push(adresa & 0xFF); - - if (rec === 2) { - resp.push(0xFF); - } - else resp.push(0); - } - - resp.push((cmd >> 8) & 0xFF);//rshift - resp.push(cmd & 0xFF);//band - resp.push(byte1 & 0xFF);//band - resp.push(byte2 & 0xFF);//band - resp.push(byte3 & 0xFF);//band - resp.push(byte4 & 0xFF);//band + } - //let data = '12345'; - let crc = crc16('ARC', resp); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; + //dusk - súmrak + //down, sunrise - svitanie - resp.push(c1); - resp.push(c2); + } catch (error) { + if (profilestr != "") { + logger.debug(profilestr); + logger.debug(error); + } + } - //logger.debug("checksum", crc); - //logger.debug("resp", resp); + result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); - return resp; + dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset * 60000); + dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset * 60000); - } + result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dusk_hours = dusk.getHours(); + result.dusk_minutes = dusk.getMinutes(); - function getObjectByTbValue(object, tbname) { - return object[Object.keys(object).find(key => object[key].tbname === tbname)]; - } + result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + result.dawn_hours = dawn.getHours(); + result.dawn_minutes = dawn.getMinutes(); - function isObject(item) { - return (typeof item === "object" && !Array.isArray(item) && item !== null); - } + result.dusk_time = dusk.getTime(); + result.dawn_time = dawn.getTime(); + result.dusk_astro_clock_offset = dusk_astro_clock_offset; + result.dawn_astro_clock_offset = dawn_astro_clock_offset; - // we fake data, that should be received from accelerometer, as they are a bit unreliable. (temperature, x,y,z) - function accelerometerData() { + return result; + } - if (temperatureInSenica === null) return; - //clone nodesData and relaysData objects - let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); - let relaysData_clone = JSON.parse(JSON.stringify(relaysData)); + function processResponse(register, bytes) { + + let values = {}; + + let byte3 = bytes[0]; + let byte2 = bytes[1]; + let byte1 = bytes[2]; + let byte0 = bytes[3]; + + //status + if (register == 0) { + let statecode = bytesToInt(bytes); + values = { "statecode": statecode }; + return values; + } + + //Dimming, CCT + else if (register == 1) { + let brightness = 0; + let dimming = byte0; + if (dimming > 128) { + //dimming = -128; + brightness = dimming - 128; + } + + //cct + //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 + let cct; + if (byte3 == 1) cct = byte2 * 256 + byte1; + else cct = bytesToInt(bytes.slice(0, 3)); + + //cct podla auditu + + values["dimming"] = brightness; + return values; + } + + // + else if (register == 4) { + values["master_node_version"] = bytes[1] + "." + bytes[2]; + //logger.debug("FW Version", register, bytes); + } + + //Napätie + else if (register == 74) { + let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); + values["voltage"] = Number(voltage); + } + + //Prúd + else if (register == 75) { + let current = bytesToInt(bytes); + values["current"] = current; + } + + //výkon + else if (register == 76) { + let power = (bytesToInt(bytes) * 0.1).toFixed(2); + values["power"] = Number(power); + } + + //účinník + else if (register == 77) { + let power_factor = Math.cos(bytesToInt(bytes) * 0.1 * (Math.PI / 180)).toFixed(2); + values["power_factor"] = Number(power_factor); + } + + //frekvencia + else if (register == 78) { + let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); + values["frequency"] = Number(frequency); + } + + //energia + else if (register == 79) { + let energy = bytesToInt(bytes); + console.log("bytesToIng ",bytesToInt(bytes)) + //Energiu treba reportovať v kWh -> delit 1000 + values["energy"] = energy / 1000; + } + + //doba života + else if (register == 80) { + let lifetime = (bytesToInt(bytes) / 60).toFixed(2); + values["lifetime"] = Number(lifetime); + } + + //nastavenie profilu + else if (register == 8) { + let time_schedule_settings = bytesToInt(bytes); + values["time_schedule_settings"] = time_schedule_settings; + } + + //naklon - nateraz sa z nodu nevycitava! kvoli problemom s accelerometrom a vracanymi hodnotami, posielame temp a x y z vo funkcii accelerometerData() + else if (register == 84) { + values["temperature"] = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; + values["inclination_x"] = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; + values["inclination_y"] = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; + values["inclination_z"] = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; + } + + //FW verzia nodu + else if (register == 89) { + //formát: "Byte3: Byte2.Byte1 (Byte0)" + values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; + } + + else if (register == 87 || register == 6 || register == 7) { + var d = new Date(); + d.setHours(byte3, byte2, 0, 0); + let timestamp = d.getTime(); + + //aktuálny čas + if (register == 87) values["actual_time"] = timestamp; + //čas súmraku + else if (register == 6) values["dusk_time"] = timestamp; + //čas úsvitu + else if (register == 7) values["dawn_time"] = timestamp; + } + + return values; + } + + + //byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB + function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { + let resp = []; + + let cmd = register; + + if (typeof adresa === 'string') adresa = parseInt(adresa); + if (typeof byte1 === 'string') byte1 = parseInt(byte1); + if (typeof byte2 === 'string') byte2 = parseInt(byte2); + if (typeof byte3 === 'string') byte3 = parseInt(byte3); + if (typeof byte4 === 'string') byte4 = parseInt(byte4); + + if (rw === 0) { + cmd = cmd + 0x8000; + } + + //master + if (rec === 0) adresa = 0; + + if (rec === 2) { + adresa = 0xffffffff;//Broadcast + } + + //recipient + if (rec === 3) { + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(adresa & 0xFF);//band + } + else { + resp.push((adresa >> 24) & 0xFF);//rshift + resp.push((adresa >> 16) & 0xFF); + resp.push((adresa >> 8) & 0xFF); + resp.push(adresa & 0xFF); + + if (rec === 2) { + resp.push(0xFF); + } + else resp.push(0); + } + + resp.push((cmd >> 8) & 0xFF);//rshift + resp.push(cmd & 0xFF);//band + resp.push(byte1 & 0xFF);//band + resp.push(byte2 & 0xFF);//band + resp.push(byte3 & 0xFF);//band + resp.push(byte4 & 0xFF);//band + + //let data = '12345'; + let crc = crc16('ARC', resp); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; - for (const key in relaysData_clone) { + resp.push(c1); + resp.push(c2); - const lineData = relaysData_clone[key]; - const lineNumber = lineData.line; - const contactor = lineData.contactor; + //logger.debug("checksum", crc); + //logger.debug("resp", resp); - if (lineNumber === 0) continue; + return resp; - if (contactor === 1) { + } - let date = Date.now(); + function getObjectByTbValue(object, tbname) { + return object[Object.keys(object).find(key => object[key].tbname === tbname)]; + } - Object.keys(nodesData_clone).forEach((node, index) => { + function isObject(item) { + return (typeof item === "object" && !Array.isArray(item) && item !== null); + } - setTimeout(function() { - if (nodesData_clone[node].line === lineNumber) { + // we fake data, that should be received from accelerometer, as they are a bit unreliable. (temperature, x,y,z) + function accelerometerData() { - // NOTE: if status of luminaire is NOK or OFFLINE, we do not send data; - let status = nodesData_clone[node].status; - if (status === "OFFLINE" || !status) return; + if (temperatureInSenica === null) return; - let x = null; - if (naklony.hasOwnProperty(node)) x = naklony[node].naklon; - if (x === null) x = 0; + //clone nodesData and relaysData objects + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + let relaysData_clone = JSON.parse(JSON.stringify(relaysData)); - sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData_clone[node].tbname, date); - } + for (const key in relaysData_clone) { - }, (index + 1) * 500); - }) + const lineData = relaysData_clone[key]; + const lineNumber = lineData.line; + const contactor = lineData.contactor; - } - } - } + if (lineNumber === 0) continue; + + if (contactor === 1) { + + let date = Date.now(); + + Object.keys(nodesData_clone).forEach((node, index) => { + + setTimeout(function() { + + if (nodesData_clone[node].line === lineNumber) { + + // NOTE: if status of luminaire is NOK or OFFLINE, we do not send data; + let status = nodesData_clone[node].status; + if (status === "OFFLINE" || !status) return; + + let x = null; + if (naklony.hasOwnProperty(node)) x = naklony[node].naklon; + if (x === null) x = 0; + + sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData_clone[node].tbname, date); + } + + }, (index + 1) * 500); + }) + + } + } + } } // end of instance.export diff --git a/flow/db_init.js b/flow/db_init.js index a759465..2a5caf9 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -6,14 +6,33 @@ exports.version = '1.0.2'; exports.icon = 'sign-out'; exports.output = 2; +exports.html = `
+
+
+
Hostname or IP address (if not empty - setting will override db setting)
+
+
+
Port
+
+
+
+
+
@(Client id)
+
+
+
@(Username)
+
+
+
`; + + exports.readme = ` - # DB initialization +# DB initialization `; const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const { initNotification } = require('./helper/notification_reporter'); const errorHandler = require('./helper/ErrorToServiceHandler'); -const total_energy = require('../databases/total_energy'); const SEND_TO = { db_init: 0, @@ -22,6 +41,7 @@ const SEND_TO = { exports.install = async function(instance) { + const dbNodes = TABLE("nodes"); const dbRelays = TABLE("relays"); const dbSettings = TABLE("settings"); @@ -50,7 +70,7 @@ exports.install = async function(instance) { Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {}) dbs.settings = { - edge_fw_version: "2025-08-08", //rok-mesiac-den + edge_fw_version: "2025-04-24", //rok-mesiac-den language: responseSettings[0]["lang"], rvo_name: responseSettings[0]["rvo_name"], project_id: responseSettings[0]["project_id"], @@ -78,11 +98,6 @@ exports.install = async function(instance) { maintenance_mode: false, } - - let rvo_number = responseSettings[0]["rvo_name"].match(/\D+(\d{1,2})_/)[1]; - dbs.settings.energy_to_switch_lamps = total_energy[rvo_number]; - if (dbs.settings.energy_to_switch_lamps === undefined) console.log('=============== db_init.js: energy_to_switch_lamps is undefined'); - FLOW.dbLoaded = true; errorHandler.setProjectId(dbs.settings.project_id); initNotification(); diff --git a/flow/designer.json b/flow/designer.json index 7565777..e5d2a7c 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -36,13 +36,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#DA4453", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#DA4453", - "notes": "" + } }, { "id": "1612776786008", @@ -89,14 +89,14 @@ "text": "Connected", "color": "green" }, + "color": "#888600", + "notes": "", "options": { "username": "", "clientid": "", "port": "1883", "host": "" - }, - "color": "#888600", - "notes": "" + } }, { "id": "1612778461252", @@ -129,11 +129,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1612783322136", @@ -144,22 +144,20 @@ "y": 324, "connections": {}, "disabledio": { - "input": [ - 0 - ], + "input": [], "output": [] }, "state": { "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615551060773", @@ -179,13 +177,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#DA4453", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#DA4453", - "notes": "" + } }, { "id": "1615563373927", @@ -205,13 +203,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#DA4453", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#DA4453", - "notes": "" + } }, { "id": "1615566865233", @@ -229,11 +227,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1615798582262", @@ -253,13 +251,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615802995322", @@ -277,13 +275,13 @@ "text": "Disabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": false - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615809128443", @@ -301,13 +299,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1615809595184", @@ -325,11 +323,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1616165795916", @@ -358,6 +356,9 @@ "text": "Listening", "color": "green" }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, "options": { "timeout": 10, "cachepolicy": 0, @@ -367,15 +368,12 @@ "method": "POST", "name": "", "flags": [ + 10000, "id:1616165795916", - "post", - 10000 + "post" ], "emptyresponse": false - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false + } }, { "id": "1616165824813", @@ -393,11 +391,11 @@ "text": "", "color": "gray" }, + "color": "#5D9CEC", + "notes": "", "options": { "datatype": "json" - }, - "color": "#5D9CEC", - "notes": "" + } }, { "id": "1617104731852", @@ -417,13 +415,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1617114651703", @@ -448,12 +446,12 @@ "text": "", "color": "gray" }, - "options": { - "data": "{line: 3, command: \"turnOff\", force: true}", - "datatype": "object" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "datatype": "object", + "data": "{line: 2, command: \"off\", force: true}" + } }, { "id": "1617115013095", @@ -471,11 +469,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1617284749681", @@ -500,12 +498,12 @@ "text": "", "color": "gray" }, - "options": { - "datatype": "string", - "data": "profile_nodes" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "data": "profile_nodes", + "datatype": "string" + } }, { "id": "1618235171399", @@ -530,11 +528,11 @@ "text": "", "color": "gray" }, + "color": "#F6BB42", + "notes": "", "options": { "data": "run" - }, - "color": "#F6BB42", - "notes": "" + } }, { "id": "1618300858252", @@ -552,13 +550,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1618393583970", @@ -576,11 +574,11 @@ "text": "from-dido-controller", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "from-dido-controller" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618393674428", @@ -605,11 +603,11 @@ "text": "platform-rpc-call", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "platform-rpc-call" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618393759854", @@ -638,11 +636,11 @@ "text": "cmd_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "cmd_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618393827655", @@ -660,11 +658,11 @@ "text": "cmd_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "cmd_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618558465485", @@ -682,11 +680,11 @@ "text": "platform-rpc-call", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "platform-rpc-call" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1618572059773", @@ -711,12 +709,12 @@ "text": "", "color": "gray" }, - "options": { - "datatype": "object", - "data": "{line: 1, command: \"turnOn\", force: true}" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "data": "{line: 2, command: \"on\", force: true}", + "datatype": "object" + } }, { "id": "1619515097737", @@ -769,9 +767,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": {} }, { "id": "1619605019281", @@ -800,6 +798,9 @@ "text": "Listening", "color": "green" }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -809,14 +810,11 @@ "method": "GET", "name": "", "flags": [ + 5000, "id:1619605019281", - "get", - 5000 + "get" ] - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false + } }, { "id": "1619784672383", @@ -841,12 +839,12 @@ "text": "", "color": "gray" }, - "options": { - "data": "{command: \"turnOnAlarm\"}", - "datatype": "object" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "datatype": "object", + "data": "{command: \"turnOnAlarm\"}" + } }, { "id": "1619784812964", @@ -871,12 +869,12 @@ "text": "", "color": "gray" }, - "options": { - "data": "{command: \"turnOffAlarm\"}", - "datatype": "object" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "datatype": "object", + "data": "{command: \"turnOffAlarm\"}" + } }, { "id": "1621340721628", @@ -894,11 +892,11 @@ "text": "modbus_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "modbus_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1622640022885", @@ -923,6 +921,9 @@ "text": "Listening", "color": "green" }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -931,14 +932,11 @@ "url": "/db_connector", "method": "POST", "flags": [ + 5000, "id:1622640022885", - "post", - 5000 + "post" ] - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false + } }, { "id": "1622640073521", @@ -963,11 +961,11 @@ "text": "", "color": "gray" }, + "color": "#2134B0", + "notes": "", "options": { "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" + } }, { "id": "1622641420685", @@ -985,9 +983,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": {} }, { "id": "1634303504177", @@ -1009,15 +1007,15 @@ "output": [] }, "state": { - "text": "840.05 MB / 985.68 MB", + "text": "595.82 MB / 982.12 MB", "color": "gray" }, + "color": "#F6BB42", + "notes": "", "options": { "enabled": true, "interval": 30000 - }, - "color": "#F6BB42", - "notes": "" + } }, { "id": "1634303533779", @@ -1039,16 +1037,16 @@ "output": [] }, "state": { - "text": "5.78 GB / 7.26 GB", + "text": "3.80 GB / 6.86 GB", "color": "gray" }, + "color": "#F6BB42", + "notes": "", "options": { "enabled": true, "path": "/", "interval": 30000 - }, - "color": "#F6BB42", - "notes": "" + } }, { "id": "1634303595494", @@ -1077,11 +1075,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1634303602169", @@ -1099,11 +1097,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1634303685503", @@ -1121,11 +1119,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1634303743260", @@ -1151,13 +1149,13 @@ "text": "", "color": "gray" }, - "options": { - "stringify": "json", - "method": "POST", - "url": "http://192.168.252.2:8004/sentmessage" - }, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": { + "url": "http://192.168.252.2:8004/sentmessage", + "method": "POST", + "stringify": "json" + } }, { "id": "1634463186563", @@ -1177,13 +1175,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634464580289", @@ -1212,13 +1210,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "let response = {};\nresponse.cpu = value.cpu;\nresponse.uptime = value.uptime;\n\nsend(0, response);", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1634465243324", @@ -1238,13 +1236,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634465281992", @@ -1273,13 +1271,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "value.sender = \"ram\";\n\nlet response = {};\n\nresponse.memory_total = Math.round(value.total / (1024 ** 2));\nresponse.memory_free = Math.round(value.free / (1024 ** 2));\nresponse.memory_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1634465338103", @@ -1299,13 +1297,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634465821120", @@ -1334,13 +1332,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "value.sender = \"hdd\";\n\nlet response = {};\n\nresponse.hdd_total = Math.round(value.total / (1024 ** 2));\nresponse.hdd_free = Math.round(value.free / (1024 ** 2));\nresponse.hdd_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1634465892500", @@ -1360,13 +1358,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634484067516", @@ -1386,13 +1384,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1634488120710", @@ -1421,11 +1419,11 @@ "text": "", "color": "gray" }, + "color": "#2134B0", + "notes": "", "options": { "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" + } }, { "id": "1635327431236", @@ -1445,13 +1443,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1635936391935", @@ -1469,11 +1467,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1637069803394", @@ -1495,9 +1493,11 @@ "output": [] }, "state": { - "text": "2.4% / 74.33 MB", + "text": "2.8% / 99.24 MB", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "monitorfiles": true, "monitorconnections": true, @@ -1505,9 +1505,7 @@ "monitorconsumption": true, "enabled": true, "interval": 30000 - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1683664161036", @@ -1527,13 +1525,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1683981346282", @@ -1562,11 +1560,11 @@ "text": "from-dido-controller", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "from-dido-controller" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1684055037116", @@ -1586,13 +1584,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1684060205000", @@ -1612,13 +1610,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1684179110403", @@ -1636,13 +1634,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1699963668903", @@ -1683,11 +1681,11 @@ "text": "", "color": "gray" }, + "color": "#2134B0", + "notes": "", "options": { "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" + } }, { "id": "1699964678894", @@ -1716,11 +1714,11 @@ "text": "modbus_to_dido", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "modbus_to_dido" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1699964793925", @@ -1740,13 +1738,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1699965957410", @@ -1795,9 +1793,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#2134B0", - "notes": "" + "notes": "", + "options": {} }, { "id": "1700411878636", @@ -1846,9 +1844,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#5CB36D", - "notes": "" + "notes": "", + "options": {} }, { "id": "1714752862828", @@ -1866,13 +1864,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1717441414646", @@ -1901,13 +1899,13 @@ "text": "", "color": "gray" }, + "color": "#656D78", + "notes": "", "options": { "keepmessage": true, "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n\telse if(value.status === \"NOK-thermometer\")\n\t{\n\t\tsend(0, {\"thermometer\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"temperature\"))\n\t{\n\t\tsend(0, {\"thermometer\": \"OK\"});\n\t}\n}", "outputs": 1 - }, - "color": "#656D78", - "notes": "" + } }, { "id": "1717442627834", @@ -1927,13 +1925,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1717442631338", @@ -1951,11 +1949,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1718016045116", @@ -1980,11 +1978,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1718016052341", @@ -2013,15 +2011,15 @@ "text": "Running", "color": "gray" }, + "color": "#30E193", + "notes": "", "options": { "slack_channel": "C071KN2Q8SK", "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "rvo_senica_33_10.0.0.127" - }, - "color": "#30E193", - "notes": "" + "name": "test_rvo_debian12" + } }, { "id": "1718016073501", @@ -2046,13 +2044,13 @@ "text": "", "color": "gray" }, - "options": { - "stringify": "json", - "method": "POST", - "url": "http://192.168.252.2:8004/slack" - }, "color": "#5D9CEC", - "notes": "" + "notes": "", + "options": { + "url": "http://192.168.252.2:8004/slack", + "method": "POST", + "stringify": "json" + } }, { "id": "1718016086212", @@ -2072,13 +2070,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1718016094070", @@ -2103,12 +2101,12 @@ "text": "", "color": "gray" }, - "options": { - "datatype": "object", - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" - }, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": { + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", + "datatype": "object" + } }, { "id": "1729855334955", @@ -2126,11 +2124,11 @@ "text": "platform-rpc-call", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "platform-rpc-call" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1729855371093", @@ -2148,13 +2146,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1731068658334", @@ -2179,11 +2177,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731068754606", @@ -2212,15 +2210,15 @@ "text": "Connected", "color": "green" }, + "color": "#888600", + "notes": "", "options": { "username": "", "clientid": "", "port": "2764", "host": "192.168.252.2", - "topic": "" - }, - "color": "#888600", - "notes": "" + "topic": "u38" + } }, { "id": "1731069001548", @@ -2251,9 +2249,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#888600", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069033416", @@ -2271,11 +2269,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069059135", @@ -2300,9 +2298,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#888600", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069079243", @@ -2320,13 +2318,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1731069116691", @@ -2351,9 +2349,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069131637", @@ -2378,9 +2376,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069137374", @@ -2405,9 +2403,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069179846", @@ -2432,9 +2430,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069192937", @@ -2459,9 +2457,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731069264443", @@ -2486,11 +2484,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069334626", @@ -2515,11 +2513,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069548145", @@ -2544,11 +2542,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731069567152", @@ -2573,11 +2571,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731070156936", @@ -2602,11 +2600,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1731234189516", @@ -2631,9 +2629,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1731234189551", @@ -2658,9 +2656,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} }, { "id": "1732700042559", @@ -2689,9 +2687,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#888600", - "notes": "" + "notes": "", + "options": {} }, { "id": "1732700057052", @@ -2716,11 +2714,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1732700071298", @@ -2738,13 +2736,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1732700642917", @@ -2762,11 +2760,11 @@ "text": "tb-push", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "tb-push" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1732889185927", @@ -2784,13 +2782,13 @@ "text": "Enabled", "color": "gray" }, + "color": "#967ADC", + "notes": "", "options": { "type": "data", "repository": false, "enabled": true - }, - "color": "#967ADC", - "notes": "" + } }, { "id": "1733574412965", @@ -2815,11 +2813,11 @@ "text": "db-init", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "db-init" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1747561603739", @@ -2837,11 +2835,11 @@ "text": "send-to-services", "color": "gray" }, + "color": "#303E4D", + "notes": "", "options": { "wirename": "send-to-services" - }, - "color": "#303E4D", - "notes": "" + } }, { "id": "1747562867845", @@ -2859,9 +2857,9 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#704cff", - "notes": "" + "notes": "", + "options": {} }, { "id": "1749211698385", @@ -2886,10 +2884,10 @@ "text": "", "color": "gray" }, - "options": {}, "color": "#F6BB42", - "notes": "" + "notes": "", + "options": {} } ], - "version": 615 -} + "version": 624 +} \ No newline at end of file diff --git a/flow/designer.json_orig b/flow/designer.json_orig new file mode 100644 index 0000000..eb18e86 --- /dev/null +++ b/flow/designer.json_orig @@ -0,0 +1,2775 @@ +{ + "tabs": [ + { + "name": "MAIN PUSH", + "linker": "main-push", + "id": "1612772287426", + "index": 0 + }, + { + "name": "CMD manager", + "linker": "cmd-manager", + "id": "1615551125555", + "index": 1 + }, + { + "name": "Devices", + "linker": "devices", + "id": "1611921777196", + "index": 2 + } + ], + "components": [ + { + "id": "1611951142547", + "component": "debug", + "tab": "1611921777196", + "name": "ERROR", + "x": 598, + "y": 60, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#DA4453", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1612776786008", + "component": "wsmqttpublish", + "tab": "1612772287426", + "name": "WS MQTT publish", + "x": 290.75, + "y": 189, + "connections": { + "0": [ + { + "index": "0", + "id": "1615551060773" + } + ], + "1": [ + { + "index": "0", + "id": "1618300858252" + }, + { + "index": "0", + "id": "1618558465485" + } + ], + "2": [ + { + "index": "0", + "id": "1634303685503" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Connected", + "color": "green" + }, + "color": "#888600", + "notes": "", + "options": { + "username": "", + "clientid": "", + "port": "1883", + "host": "" + } + }, + { + "id": "1612778461252", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "tb-push", + "x": 72.75, + "y": 328, + "connections": { + "0": [ + { + "index": "0", + "id": "1612783322136" + }, + { + "index": "1", + "id": "1612776786008" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "tb-push" + } + }, + { + "id": "1612783322136", + "component": "debug", + "tab": "1612772287426", + "name": "to TB", + "x": 290.75, + "y": 330, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1615551060773", + "component": "debug", + "tab": "1612772287426", + "name": "errors from MQTT Broker", + "x": 594, + "y": 57, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#DA4453", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1615563373927", + "component": "debug", + "tab": "1615551125555", + "name": "Debug", + "x": 755, + "y": 155, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#DA4453", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1615566865233", + "component": "virtualwireout", + "tab": "1615551125555", + "name": "tb-push", + "x": 755, + "y": 248, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "tb-push" + } + }, + { + "id": "1615798582262", + "component": "debug", + "tab": "1615551125555", + "name": "CMD_debug", + "x": 755, + "y": 346, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1615802995322", + "component": "debug", + "tab": "1611921777196", + "name": "Debug", + "x": 596.8833312988281, + "y": 566.3500061035156, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Disabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": false + } + }, + { + "id": "1615809128443", + "component": "debug", + "tab": "1611921777196", + "name": "tempToTb", + "x": 595.8833312988281, + "y": 658.3500061035156, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1615809595184", + "component": "virtualwireout", + "tab": "1611921777196", + "name": "tb-push", + "x": 597.8833312988281, + "y": 377.25, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "tb-push" + } + }, + { + "id": "1616165795916", + "component": "httproute", + "tab": "1615551125555", + "name": "POST /terminal", + "x": 135, + "y": 547, + "connections": { + "0": [ + { + "index": "0", + "id": "1684060205000" + }, + { + "index": "1", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Listening", + "color": "green" + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, + "options": { + "timeout": 10, + "cachepolicy": 0, + "cacheexpire": "5 minutes", + "size": 5, + "url": "/terminal", + "method": "POST", + "name": "", + "flags": [ + 10000, + "id:1616165795916", + "post" + ], + "emptyresponse": false + } + }, + { + "id": "1616165824813", + "component": "httpresponse", + "tab": "1615551125555", + "name": "HTTP Response", + "x": 753, + "y": 423, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#5D9CEC", + "notes": "", + "options": { + "datatype": "json" + } + }, + { + "id": "1617104731852", + "component": "debug", + "tab": "1615551125555", + "name": "DIDO_Debug", + "x": 669, + "y": 1040, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1617114651703", + "component": "trigger", + "tab": "1615551125555", + "name": "turnOff line", + "x": 133, + "y": 1161, + "connections": { + "0": [ + { + "index": "1", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "data": "{line: 1, command: \"off\", force: true}", + "datatype": "object" + } + }, + { + "id": "1617115013095", + "component": "virtualwireout", + "tab": "1615551125555", + "name": "tb-push", + "x": 669, + "y": 1150, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "tb-push" + } + }, + { + "id": "1617284749681", + "component": "trigger", + "tab": "1615551125555", + "name": "update profile / node", + "x": 112, + "y": 208, + "connections": { + "0": [ + { + "index": "1", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "datatype": "string", + "data": "profile_nodes" + } + }, + { + "id": "1618235171399", + "component": "trigger", + "tab": "1615551125555", + "name": "tun tasks", + "x": 184, + "y": 279, + "connections": { + "0": [ + { + "index": "1", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "data": "run" + } + }, + { + "id": "1618300858252", + "component": "debug", + "tab": "1612772287426", + "name": "wsmqtt-exit1", + "x": 597.8833312988281, + "y": 149, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1618393583970", + "component": "virtualwireout", + "tab": "1615551125555", + "name": "to-cmd-manager", + "x": 668.8833312988281, + "y": 1269, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "from-dido-controller", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "from-dido-controller" + } + }, + { + "id": "1618393674428", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "platform-rpc-call", + "x": 132.88333129882812, + "y": 367, + "connections": { + "0": [ + { + "index": "1", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "platform-rpc-call", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "platform-rpc-call" + } + }, + { + "id": "1618393759854", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "cmd_to_dido", + "x": 119.88333129882812, + "y": 1007, + "connections": { + "0": [ + { + "index": "0", + "id": "1683664161036" + }, + { + "index": "1", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "cmd_to_dido", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "cmd_to_dido" + } + }, + { + "id": "1618393827655", + "component": "virtualwireout", + "tab": "1615551125555", + "name": "cmd_to_dido", + "x": 752.8833312988281, + "y": 527, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "cmd_to_dido", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "cmd_to_dido" + } + }, + { + "id": "1618558465485", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "platform-rpc-call", + "x": 597.8833312988281, + "y": 247, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "platform-rpc-call", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "platform-rpc-call" + } + }, + { + "id": "1618572059773", + "component": "trigger", + "tab": "1615551125555", + "name": "turnOn line", + "x": 132, + "y": 1085, + "connections": { + "0": [ + { + "index": "1", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "datatype": "object", + "data": "{line: 1, command: \"on\", force: true}" + } + }, + { + "id": "1619515097737", + "component": "cmd_manager", + "tab": "1615551125555", + "name": "CMD Manager", + "x": 452.1091003417969, + "y": 341.05455017089844, + "connections": { + "0": [ + { + "index": "0", + "id": "1615563373927" + } + ], + "1": [ + { + "index": "0", + "id": "1615566865233" + }, + { + "index": "0", + "id": "1615798582262" + } + ], + "2": [ + { + "index": "0", + "id": "1616165824813" + } + ], + "3": [ + { + "index": "0", + "id": "1618393827655" + } + ], + "4": [ + { + "index": "0", + "id": "1635936391935" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#5D9CEC", + "notes": "", + "options": {} + }, + { + "id": "1619605019281", + "component": "httproute", + "tab": "1615551125555", + "name": "GET db", + "x": 173, + "y": 653, + "connections": { + "0": [ + { + "index": "0", + "id": "1684060205000" + }, + { + "index": "1", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Listening", + "color": "green" + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, + "options": { + "timeout": 5, + "cachepolicy": 0, + "cacheexpire": "5 minutes", + "size": 5, + "url": "/db", + "method": "GET", + "name": "", + "flags": [ + 5000, + "id:1619605019281", + "get" + ] + } + }, + { + "id": "1619784672383", + "component": "trigger", + "tab": "1615551125555", + "name": "turnOnAlarm", + "x": 117, + "y": 1242, + "connections": { + "0": [ + { + "index": "1", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "data": "{command: \"turnOnAlarm\"}", + "datatype": "object" + } + }, + { + "id": "1619784812964", + "component": "trigger", + "tab": "1615551125555", + "name": "turnOffAlarm", + "x": 118, + "y": 1307, + "connections": { + "0": [ + { + "index": "1", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "data": "{command: \"turnOffAlarm\"}", + "datatype": "object" + } + }, + { + "id": "1621340721628", + "component": "virtualwireout", + "tab": "1611921777196", + "name": "modbus_to_dido", + "x": 599, + "y": 471, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "modbus_to_dido", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "modbus_to_dido" + } + }, + { + "id": "1622640022885", + "component": "httproute", + "tab": "1615551125555", + "name": "POST /db_connector", + "x": 98, + "y": 1586, + "connections": { + "0": [ + { + "index": "0", + "id": "1622640073521" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Listening", + "color": "green" + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false, + "options": { + "timeout": 5, + "cachepolicy": 0, + "cacheexpire": "5 minutes", + "size": 5, + "url": "/db_connector", + "method": "POST", + "flags": [ + 5000, + "id:1622640022885", + "post" + ] + } + }, + { + "id": "1622640073521", + "component": "db_connector", + "tab": "1615551125555", + "name": "DbConnector", + "x": 372, + "y": 1572, + "connections": { + "1": [ + { + "index": "0", + "id": "1622641420685" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#2134B0", + "notes": "", + "options": { + "edge": "undefined" + } + }, + { + "id": "1622641420685", + "component": "httpresponse", + "tab": "1615551125555", + "name": "HTTP Response", + "x": 596, + "y": 1586, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#5D9CEC", + "notes": "", + "options": {} + }, + { + "id": "1634303504177", + "component": "monitormemory", + "tab": "1612772287426", + "name": "RAM", + "x": 69.88333129882812, + "y": 888.5, + "connections": { + "0": [ + { + "index": "0", + "id": "1634465281992" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "704.30 MB / 982.12 MB", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "enabled": true, + "interval": 30000 + } + }, + { + "id": "1634303533779", + "component": "monitordisk", + "tab": "1612772287426", + "name": "disk", + "x": 70.88333129882812, + "y": 982.5, + "connections": { + "0": [ + { + "index": "0", + "id": "1634465821120" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "5.45 GB / 6.86 GB", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "enabled": true, + "path": "/", + "interval": 30000 + } + }, + { + "id": "1634303595494", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "send-to-services", + "x": 51.883331298828125, + "y": 1400.5, + "connections": { + "0": [ + { + "index": "0", + "id": "1634463186563" + }, + { + "index": "1", + "id": "1634488120710" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "send-to-services" + } + }, + { + "id": "1634303602169", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "send-to-services", + "x": 426.8833312988281, + "y": 878.5, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "send-to-services" + } + }, + { + "id": "1634303685503", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "send-to-services", + "x": 600.8833312988281, + "y": 341.5, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "send-to-services" + } + }, + { + "id": "1634303743260", + "component": "httprequest", + "tab": "1612772287426", + "name": "192.168.252.2:8004/sentmessage", + "reference": "", + "x": 506.8833312988281, + "y": 1331.7333374023438, + "connections": { + "0": [ + { + "index": "0", + "id": "1635327431236" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#5D9CEC", + "notes": "", + "options": { + "stringify": "json", + "method": "POST", + "url": "http://192.168.252.2:8004/sentmessage" + } + }, + { + "id": "1634463186563", + "component": "debug", + "tab": "1612772287426", + "name": "Debug", + "x": 305.75, + "y": 1442, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1634464580289", + "component": "code", + "tab": "1612772287426", + "name": "Code", + "x": 245, + "y": 787, + "connections": { + "0": [ + { + "index": "0", + "id": "1634465243324" + }, + { + "index": "0", + "id": "1634303602169" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#656D78", + "notes": "", + "options": { + "keepmessage": true, + "code": "let response = {};\nresponse.cpu = value.cpu;\nresponse.uptime = value.uptime;\n\nsend(0, response);", + "outputs": 1 + } + }, + { + "id": "1634465243324", + "component": "debug", + "tab": "1612772287426", + "name": "Debug", + "x": 428, + "y": 784, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1634465281992", + "component": "code", + "tab": "1612772287426", + "name": "Code", + "x": 245, + "y": 884, + "connections": { + "0": [ + { + "index": "0", + "id": "1634465338103" + }, + { + "index": "0", + "id": "1634303602169" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#656D78", + "notes": "", + "options": { + "keepmessage": true, + "code": "value.sender = \"ram\";\n\nlet response = {};\n\nresponse.memory_total = Math.round(value.total / (1024 ** 2));\nresponse.memory_free = Math.round(value.free / (1024 ** 2));\nresponse.memory_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", + "outputs": 1 + } + }, + { + "id": "1634465338103", + "component": "debug", + "tab": "1612772287426", + "name": "Debug", + "x": 429, + "y": 976, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1634465821120", + "component": "code", + "tab": "1612772287426", + "name": "Code", + "x": 245, + "y": 978, + "connections": { + "0": [ + { + "index": "0", + "id": "1634465892500" + }, + { + "index": "0", + "id": "1634303602169" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#656D78", + "notes": "", + "options": { + "keepmessage": true, + "code": "value.sender = \"hdd\";\n\nlet response = {};\n\nresponse.hdd_total = Math.round(value.total / (1024 ** 2));\nresponse.hdd_free = Math.round(value.free / (1024 ** 2));\nresponse.hdd_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", + "outputs": 1 + } + }, + { + "id": "1634465892500", + "component": "debug", + "tab": "1612772287426", + "name": "Debug", + "x": 432, + "y": 1068, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1634484067516", + "component": "debug", + "tab": "1612772287426", + "name": "Send info", + "x": 513, + "y": 1441, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1634488120710", + "component": "infosender", + "tab": "1612772287426", + "name": "Info sender", + "x": 301, + "y": 1336, + "connections": { + "0": [ + { + "index": "0", + "id": "1634484067516" + }, + { + "index": "0", + "id": "1634303743260" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#2134B0", + "notes": "", + "options": { + "edge": "undefined" + } + }, + { + "id": "1635327431236", + "component": "debug", + "tab": "1612772287426", + "name": "Debug", + "x": 837.8833312988281, + "y": 1325.5, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1635936391935", + "component": "virtualwireout", + "tab": "1615551125555", + "name": "send-to-services", + "x": 753, + "y": 623, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "send-to-services" + } + }, + { + "id": "1637069803394", + "component": "monitorconsumption", + "tab": "1612772287426", + "name": "CPU", + "x": 69, + "y": 791, + "connections": { + "0": [ + { + "index": "0", + "id": "1634464580289" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "3.6% / 111.50 MB", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "monitorfiles": true, + "monitorconnections": true, + "monitorsize": true, + "monitorconsumption": true, + "enabled": true, + "interval": 30000 + } + }, + { + "id": "1683664161036", + "component": "debug", + "tab": "1615551125555", + "name": "CMDtoDIDO", + "x": 392, + "y": 1012, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1683981346282", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "from-dido-controller", + "x": 112, + "y": 459, + "connections": { + "0": [ + { + "index": "0", + "id": "1684055037116" + }, + { + "index": "1", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "from-dido-controller", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "from-dido-controller" + } + }, + { + "id": "1684055037116", + "component": "debug", + "tab": "1615551125555", + "name": "from dido to cmd", + "x": 451, + "y": 532, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1684060205000", + "component": "debug", + "tab": "1615551125555", + "name": "HTTP routes", + "x": 450, + "y": 639, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1684179110403", + "component": "debug", + "tab": "1611921777196", + "name": "MDBToDido", + "x": 598, + "y": 147, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1699963668903", + "component": "dido_controller", + "tab": "1615551125555", + "name": "DIDO_Controller", + "x": 397, + "y": 1131, + "connections": { + "0": [ + { + "index": "0", + "id": "1617104731852" + } + ], + "1": [ + { + "index": "0", + "id": "1617104731852" + }, + { + "index": "0", + "id": "1617115013095" + } + ], + "2": [ + { + "index": "0", + "id": "1618393583970" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#2134B0", + "notes": "", + "options": { + "edge": "undefined" + } + }, + { + "id": "1699964678894", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "modbus_to_dido", + "x": 96, + "y": 924, + "connections": { + "0": [ + { + "index": "0", + "id": "1699963668903" + }, + { + "index": "0", + "id": "1699964793925" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "modbus_to_dido", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "modbus_to_dido" + } + }, + { + "id": "1699964793925", + "component": "debug", + "tab": "1615551125555", + "name": "modbusToDido", + "x": 388, + "y": 920, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1699965957410", + "component": "modbus_reader", + "tab": "1611921777196", + "name": "Modbus reader", + "x": 232, + "y": 175, + "connections": { + "0": [ + { + "index": "0", + "id": "1611951142547" + } + ], + "1": [ + { + "index": "0", + "id": "1621340721628" + }, + { + "index": "0", + "id": "1684179110403" + }, + { + "index": "0", + "id": "1717441414646" + } + ], + "2": [ + { + "index": "0", + "id": "1615809595184" + }, + { + "index": "0", + "id": "1714752862828" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#2134B0", + "notes": "", + "options": {} + }, + { + "id": "1700411878636", + "component": "thermometer", + "tab": "1611921777196", + "name": "Thermometer", + "x": 234.75, + "y": 444, + "connections": { + "0": [ + { + "index": "0", + "id": "1615802995322" + } + ], + "1": [ + { + "index": "0", + "id": "1615809595184" + }, + { + "index": "0", + "id": "1615809128443" + } + ], + "2": [ + { + "index": "0", + "id": "1621340721628" + }, + { + "index": "0", + "id": "1732889185927" + }, + { + "index": "0", + "id": "1717441414646" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#5CB36D", + "notes": "", + "options": {} + }, + { + "id": "1714752862828", + "component": "debug", + "tab": "1611921777196", + "name": "MDBToTb", + "x": 766, + "y": 324, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1717441414646", + "component": "code", + "tab": "1611921777196", + "name": "device-status", + "x": 764.0833282470703, + "y": 222, + "connections": { + "0": [ + { + "index": "0", + "id": "1717442627834" + }, + { + "index": "0", + "id": "1717442631338" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#656D78", + "notes": "", + "options": { + "keepmessage": true, + "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n\telse if(value.status === \"NOK-thermometer\")\n\t{\n\t\tsend(0, {\"thermometer\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"temperature\"))\n\t{\n\t\tsend(0, {\"thermometer\": \"OK\"});\n\t}\n}", + "outputs": 1 + } + }, + { + "id": "1717442627834", + "component": "debug", + "tab": "1611921777196", + "name": "modbus service", + "x": 966.0833282470703, + "y": 165, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1717442631338", + "component": "virtualwireout", + "tab": "1611921777196", + "name": "send-to-services", + "x": 968.0833282470703, + "y": 268, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "send-to-services" + } + }, + { + "id": "1718016045116", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "tb-push", + "x": 77.75, + "y": 1630, + "connections": { + "0": [ + { + "index": "0", + "id": "1718016052341" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "tb-push" + } + }, + { + "id": "1718016052341", + "component": "slack_filter", + "tab": "1612772287426", + "name": "Slack Filter", + "x": 296, + "y": 1671, + "connections": { + "0": [ + { + "index": "0", + "id": "1718016086212" + }, + { + "index": "0", + "id": "1718016073501" + } + ] + }, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Running", + "color": "gray" + }, + "color": "#30E193", + "notes": "", + "options": { + "slack_channel": "C071KN2Q8SK", + "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", + "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", + "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", + "name": "test_rvo_debian12" + } + }, + { + "id": "1718016073501", + "component": "httprequest", + "tab": "1612772287426", + "name": "http://192.168.252.2:8004/slack", + "x": 495, + "y": 1753, + "connections": { + "0": [ + { + "index": "0", + "id": "1718016086212" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#5D9CEC", + "notes": "", + "options": { + "stringify": "json", + "method": "POST", + "url": "http://192.168.252.2:8004/slack" + } + }, + { + "id": "1718016086212", + "component": "debug", + "tab": "1612772287426", + "name": "Debug", + "x": 832, + "y": 1664, + "connections": {}, + "disabledio": { + "input": [ + 0 + ], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1718016094070", + "component": "trigger", + "tab": "1612772287426", + "name": "Trigger", + "x": 79, + "y": 1723, + "connections": { + "0": [ + { + "index": "0", + "id": "1718016052341" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": { + "datatype": "object", + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" + } + }, + { + "id": "1731068658334", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 79.75, + "y": 164, + "connections": { + "0": [ + { + "index": "0", + "id": "1612776786008" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731069001548", + "component": "db_init", + "tab": "1612772287426", + "name": "DB Initialization", + "x": 1003.75, + "y": 240.25, + "connections": { + "0": [ + { + "index": "0", + "id": "1731069033416" + } + ], + "1": [ + { + "index": "0", + "id": "1747561603739" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#888600", + "notes": "", + "options": {} + }, + { + "id": "1731069033416", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "db-init", + "x": 1244.75, + "y": 233.25, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731069059135", + "component": "showdb", + "tab": "1612772287426", + "name": "Show db data", + "x": 1121.75, + "y": 814.25, + "connections": { + "0": [ + { + "index": "0", + "id": "1731069079243" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#888600", + "notes": "", + "options": {} + }, + { + "id": "1731069079243", + "component": "debug", + "tab": "1612772287426", + "name": "dbData", + "x": 1324.75, + "y": 863.25, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1731069116691", + "component": "trigger", + "tab": "1612772287426", + "name": "settings", + "x": 867.75, + "y": 667.75, + "connections": { + "0": [ + { + "index": "0", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1731069131637", + "component": "trigger", + "tab": "1612772287426", + "name": "relaysData", + "x": 798.75, + "y": 733.75, + "connections": { + "0": [ + { + "index": "1", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1731069137374", + "component": "trigger", + "tab": "1612772287426", + "name": "nodesData", + "x": 762.75, + "y": 801.75, + "connections": { + "0": [ + { + "index": "2", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1731069179846", + "component": "trigger", + "tab": "1612772287426", + "name": "pinsData", + "x": 782.75, + "y": 867.75, + "connections": { + "0": [ + { + "index": "3", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1731069192937", + "component": "trigger", + "tab": "1612772287426", + "name": "sample data", + "x": 801.75, + "y": 933.75, + "connections": { + "0": [ + { + "index": "4", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1731069264443", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 63.75, + "y": 1279, + "connections": { + "0": [ + { + "index": "0", + "id": "1634488120710" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731069334626", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "db-init", + "x": 172.88333129882812, + "y": 129, + "connections": { + "0": [ + { + "index": "0", + "id": "1619515097737" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731069548145", + "component": "virtualwirein", + "tab": "1611921777196", + "name": "db-init", + "x": 46.75, + "y": 192, + "connections": { + "0": [ + { + "index": "0", + "id": "1699965957410" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731069567152", + "component": "virtualwirein", + "tab": "1611921777196", + "name": "db-init", + "x": 44.75, + "y": 465, + "connections": { + "0": [ + { + "index": "0", + "id": "1700411878636" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731070156936", + "component": "virtualwirein", + "tab": "1615551125555", + "name": "db-init", + "x": 126.88333129882812, + "y": 1377, + "connections": { + "0": [ + { + "index": "2", + "id": "1699963668903" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1731234189516", + "component": "trigger", + "tab": "1612772287426", + "name": "monitor.txt", + "x": 821.75, + "y": 1000.75, + "connections": { + "0": [ + { + "index": "5", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1731234189551", + "component": "trigger", + "tab": "1612772287426", + "name": "err.txt", + "x": 862.75, + "y": 1064.75, + "connections": { + "0": [ + { + "index": "6", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + }, + { + "id": "1732700042559", + "component": "nodesdb_change_check", + "tab": "1612772287426", + "name": "Nodes DB change check", + "x": 263.8833312988281, + "y": 1993.2333984375, + "connections": { + "0": [ + { + "index": "0", + "id": "1732700071298" + }, + { + "index": "0", + "id": "1732700642917" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#888600", + "notes": "", + "options": {} + }, + { + "id": "1732700057052", + "component": "virtualwirein", + "tab": "1612772287426", + "name": "db-init", + "x": 84.75, + "y": 1994, + "connections": { + "0": [ + { + "index": "0", + "id": "1732700042559" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "db-init", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "db-init" + } + }, + { + "id": "1732700071298", + "component": "debug", + "tab": "1612772287426", + "name": "nodesChange", + "x": 561.8833312988281, + "y": 2055.2333984375, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1732700642917", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "tb-push", + "x": 557.8833312988281, + "y": 1949, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "tb-push", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "tb-push" + } + }, + { + "id": "1732889185927", + "component": "debug", + "tab": "1611921777196", + "name": "tempToDido", + "x": 594.8833312988281, + "y": 753, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "color": "#967ADC", + "notes": "", + "options": { + "type": "data", + "repository": false, + "enabled": true + } + }, + { + "id": "1747561603739", + "component": "virtualwireout", + "tab": "1612772287426", + "name": "send-to-services", + "x": 1243.8833312988281, + "y": 334.5, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "send-to-services", + "color": "gray" + }, + "color": "#303E4D", + "notes": "", + "options": { + "wirename": "send-to-services" + } + }, + { + "id": "1747562867845", + "component": "comment", + "tab": "1612772287426", + "name": "FLOW STARTING POINT", + "x": 1003.5666656494141, + "y": 178, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#704cff", + "notes": "", + "options": {} + }, + { + "id": "1750771612786", + "component": "trigger", + "tab": "1612772287426", + "name": "devices", + "x": 896.75, + "y": 1122.75, + "connections": { + "0": [ + { + "index": "7", + "id": "1731069059135" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "color": "#F6BB42", + "notes": "", + "options": {} + } + ], + "version": 624 +} \ No newline at end of file diff --git a/flow/dido_controller.js b/flow/dido_controller.js index d16fb19..8843fe0 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -364,7 +364,7 @@ exports.install = function(instance) { data.map(item => { let value = item['value']; - let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01" + let pin = item["dev"] + item["circuit"]; // for example "ro1_03" or "di1_01" if (pin == undefined) return; switchLogic(pin, value); @@ -516,9 +516,9 @@ exports.install = function(instance) { } else if (ws) { - //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method + //pin = "ro1_03" or "di1_01" ... we must make just "1_01" with slice method monitor.info(`Dido: turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); - let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value }; + let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(2), "value": value }; ws.send(JSON.stringify(cmd)); } @@ -754,9 +754,9 @@ exports.install = function(instance) { pins = [4, 6]; } } else if (controllerType === "unipi") { - pins = ["input1_01", "input1_04", "input1_05"]; + pins = ["di1_01", "di1_04", "di1_05"]; if (hasMainSwitch === 1) { - pins = ["input1_01", "input1_04"]; + pins = ["di1_01", "di1_04"]; } } @@ -781,7 +781,7 @@ exports.install = function(instance) { for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { - if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && SETTINGS.maintenance_mode) continue; + if ((pinIndex === 6 || pinIndex === 'di1_01' || pinIndex === 'di1_05') && SETTINGS.maintenance_mode) continue; status = "NOK"; break; } @@ -798,7 +798,7 @@ exports.install = function(instance) { // we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]] - // we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1] + // we pass two values in case of websocket ==> switchLogic("ro1_03",1) ==> ["ro1_03",1] const switchLogic = (...args) => { let values = {}; @@ -849,18 +849,18 @@ exports.install = function(instance) { else if (type == "rotary_switch_state") { // combination of these two pins required to get result let pin2, pin3; - if (pinIndex == 2 || pinIndex == "input1_02") { + if (pinIndex == 2 || pinIndex == "di1_02") { pin2 = newPinValue; - pin3 = previousValues[3] || previousValues["input1_03"]; + pin3 = previousValues[3] || previousValues["di1_03"]; if (pin3 == undefined) { previousValues[pinIndex] = newPinValue; return; } } - else if (pinIndex == 3 || pinIndex == "input1_03") { + else if (pinIndex == 3 || pinIndex == "di1_03") { pin3 = newPinValue; - pin2 = previousValues[2] || previousValues["input1_02"]; + pin2 = previousValues[2] || previousValues["di1_02"]; if (pin2 == undefined) { previousValues[pinIndex] = newPinValue; @@ -913,7 +913,7 @@ exports.install = function(instance) { } //Dverovy kontakt - pin 6 - //! Ak je rvo s dvoma dverovymi kontaktami, ked pride z evoku signal z input1_05, co bol predytm "state_of_main switch" handlujeme ho teraz ako 'door_condition' + //! Ak je rvo s dvoma dverovymi kontaktami, ked pride z evoku signal z di1_05, co bol predytm "state_of_main switch" handlujeme ho teraz ako 'door_condition' else if (type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; @@ -1369,60 +1369,60 @@ exports.install = function(instance) { //! pins.table --> from UNIPI // pin:string|type:string|line:number -// *|input1_01|state_of_main_switch|0|........... -// *|input1_02|rotary_switch_state|0|........... -// *|input1_03|rotary_switch_state|0|........... +// *|di1_01|state_of_main_switch|0|........... +// *|di1_02|rotary_switch_state|0|........... +// *|di1_03|rotary_switch_state|0|........... // *|intut1_04|power_supply|0|........... -// *|input1_05|door_condition|0|........... -// *|input1_06|state_of_breaker|1|........... -// *|input1_07|state_of_breaker|2|........... -// *|input1_08|state_of_breaker|3|........... -// *|relay1_02|state_of_contactor|1|........... -// *|relay1_03|state_of_contactor|2|........... -// *|relay1_04|state_of_contactor|3|........... +// *|di1_05|door_condition|0|........... +// *|di1_06|state_of_breaker|1|........... +// *|di1_07|state_of_breaker|2|........... +// *|di1_08|state_of_breaker|3|........... +// *|ro1_02|state_of_contactor|1|........... +// *|ro1_03|state_of_contactor|2|........... +// *|ro1_04|state_of_contactor|3|........... // *|287D8776E0013CE9|temperature|0|........... //! pins_data --> from UNIPI // { -// input1_01: { -// pin: 'input1_01', +// di1_01: { +// pin: 'di1_01', // type: 'door_condition', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// input1_02: { -// pin: 'input1_02', +// di1_02: { +// pin: 'di1_02', // type: 'rotary_switch_state', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// input1_03: { -// pin: 'input1_03', +// di1_03: { +// pin: 'di1_03', // type: 'rotary_switch_state', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// input1_04: { -// pin: 'input1_04', +// di1_04: { +// pin: 'di1_04', // type: 'power_supply', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// input1_05: { -// pin: 'input1_05', +// di1_05: { +// pin: 'di1_05', // type: 'state_of_main_switch', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// input1_06: { -// pin: 'input1_06', +// di1_06: { +// pin: 'di1_06', // type: 'state_of_breaker', // line: 1, // tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo' // }, -// relay1_02: { -// pin: 'relay1_02', +// ro1_02: { +// pin: 'ro1_02', // type: 'state_of_contactor', // line: 1, // tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo' diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index 716ef7b..ef6942a 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -1,186 +1,186 @@ class DataToTbHandler { - constructor(index) { - this.index = index; + constructor(index) { + this.index = index; - // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) - this.timeToHoldTbValue = 30 * 60; //30 minutes - this.previousValues = {}; - this.debug = false; - this.messageCounter = 0; - this.itIsNodeReadout = false; - this.sender = ""; + // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) + this.timeToHoldTbValue = 30 * 60; //30 minutes + this.previousValues = {}; + this.debug = false; + this.messageCounter = 0; + this.itIsNodeReadout = false; + this.sender = ""; - // if attribute change difference is less than limit value, we do not send to tb. - this.attributeChangeLimit = { - temperature: 0.5, - Phase_1_voltage: 2, - Phase_2_voltage: 2, - Phase_3_voltage: 2, - Phase_1_current: 0.1, - Phase_2_current: 0.1, - Phase_3_current: 0.1, - Phase_1_power: 2, - Phase_2_power: 2, - Phase_3_power: 2, - total_power: 2, - total_energy: 1, - Phase_1_pow_factor: 0.1, - Phase_2_pow_factor: 0.1, - Phase_3_pow_factor: 0.1, - power_factor: 0.1, - lifetime: 2, - voltage: 2, - power: 2, - frequency: 3, - energy: 0.1, - current: 2, - inclination_x: 10, - inclination_y: 10, - inclination_z: 10 - }; + // if attribute change difference is less than limit value, we do not send to tb. + this.attributeChangeLimit = { + temperature: 0.5, + Phase_1_voltage: 2, + Phase_2_voltage: 2, + Phase_3_voltage: 2, + Phase_1_current: 0.1, + Phase_2_current: 0.1, + Phase_3_current: 0.1, + Phase_1_power: 2, + Phase_2_power: 2, + Phase_3_power: 2, + total_power: 2, + total_energy: 1, + Phase_1_pow_factor: 0.1, + Phase_2_pow_factor: 0.1, + Phase_3_pow_factor: 0.1, + power_factor: 0.1, + lifetime: 2, + voltage: 2, + power: 2, + frequency: 3, + energy: 0.1, + current: 2, + inclination_x: 10, + inclination_y: 10, + inclination_z: 10 + }; - } + } - dump() { - console.log("----------------------------"); - console.log("previousValues", this.previousValues); - console.log("----------------------------"); - } + dump() { + console.log("----------------------------"); + console.log("previousValues", this.previousValues); + console.log("----------------------------"); + } - setSender(sender) { - this.sender = sender; - } + setSender(sender) { + this.sender = sender; + } - isEmptyObject(obj) { - for (var _ in obj) { - return false; - } - return true; - } + isEmptyObject(obj) { + for (var _ in obj) { + return false; + } + return true; + } - sendToTb(data, instance) { + sendToTb(data, instance) { - //not to modify data object, we do deep copy: - let dataCopy = JSON.parse(JSON.stringify(data)); + //not to modify data object, we do deep copy: + let dataCopy = JSON.parse(JSON.stringify(data)); - let keys = Object.keys(dataCopy); + let keys = Object.keys(dataCopy); - if (keys.length == 0) { - if (this.debug) console.log("sendToTb received empty object", dataCopy); - return; - } + if (keys.length == 0) { + if (this.debug) console.log("sendToTb received empty object", dataCopy); + return; + } - let tbname = keys[0]; - let ts; + let tbname = keys[0]; + let ts; - let arrayOfValues = dataCopy[tbname]; - let arrayOfValuesToSend = []; + let arrayOfValues = dataCopy[tbname]; + let arrayOfValuesToSend = []; - for (let i = 0; i < arrayOfValues.length; i++) { + for (let i = 0; i < arrayOfValues.length; i++) { - ts = arrayOfValues[i].ts; - let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); + ts = arrayOfValues[i].ts; + let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); - if (!this.isEmptyObject(values)) { - arrayOfValuesToSend.push({ ts: ts, values: values }); - } + if (!this.isEmptyObject(values)) { + arrayOfValuesToSend.push({ ts: ts, values: values }); + } - } + } - if (arrayOfValuesToSend.length == 0) { - //if(this.debug) console.log("data not sent - empty array"); - return; - } + if (arrayOfValuesToSend.length == 0) { + //if(this.debug) console.log("data not sent - empty array"); + return; + } - this.messageCounter++; + this.messageCounter++; - let dataToTbModified = { - [tbname]: arrayOfValuesToSend - } + let dataToTbModified = { + [tbname]: arrayOfValuesToSend + } - //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); - //if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); - instance.send(this.index, dataToTbModified); - } + //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); + //if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); + instance.send(this.index, dataToTbModified); + } - getDiffTimestamp(key) { - //TODO set different value for given key!!! - //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h - return this.timeToHoldTbValue * 1000; - } + getDiffTimestamp(key) { + //TODO set different value for given key!!! + //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h + return this.timeToHoldTbValue * 1000; + } - prepareValuesForTb(tbname, timestamp, values) { + prepareValuesForTb(tbname, timestamp, values) { - let keys = Object.keys(values); + let keys = Object.keys(values); - if (keys.includes("lifetime")) this.itIsNodeReadout = true; + if (keys.includes("lifetime")) this.itIsNodeReadout = true; - if (!this.previousValues.hasOwnProperty(tbname)) { - this.previousValues[tbname] = {}; - } + if (!this.previousValues.hasOwnProperty(tbname)) { + this.previousValues[tbname] = {}; + } - //if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values); + //if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values); - for (let i = 0; i < keys.length; i++) { + for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let value = values[key]; + let key = keys[i]; + let value = values[key]; - if (!this.previousValues[tbname].hasOwnProperty(key)) { - this.previousValues[tbname][key] = { ts: timestamp, value: value }; - continue; - } + if (!this.previousValues[tbname].hasOwnProperty(key)) { + this.previousValues[tbname][key] = { ts: timestamp, value: value }; + continue; + } - // attributeData ==> {voltage: {ts:333333, value:5}} - let attributeData = this.previousValues[tbname][key]; - let attributeToChange = false; - if (key in this.attributeChangeLimit) attributeToChange = true; - let limit = this.attributeChangeLimit[key]; - let timestampDiffToRemoveKey; + // attributeData ==> {voltage: {ts:333333, value:5}} + let attributeData = this.previousValues[tbname][key]; + let attributeToChange = false; + if (key in this.attributeChangeLimit) attributeToChange = true; + let limit = this.attributeChangeLimit[key]; + let timestampDiffToRemoveKey; - //this will ensure "node statecode" will be sent just once an hour - if (this.itIsNodeReadout && key === "statecode") { - attributeData.value = value; - this.itIsNodeReadout = false; - timestampDiffToRemoveKey = 1 * 60 * 60 * 1000; // 1 hour - } + //this will ensure "node statecode" will be sent just once an hour + if (this.itIsNodeReadout && key === "statecode") { + attributeData.value = value; + this.itIsNodeReadout = false; + timestampDiffToRemoveKey = 1 * 60 * 60 * 1000; // 1 hour + } - if (key === "twilight_sensor" && value > 100) { - attributeData.value = value; - } + if (key === "twilight_sensor" && value > 100) { + attributeData.value = value; + } - //if edge, master or node version do not change, send just once a day: - if (["edge_fw_version", "master_node_version", "fw_version"].includes(key)) { - timestampDiffToRemoveKey = 24 * 60 * 60 * 1000; - } + //if edge, master or node version do not change, send just once a day: + if (["edge_fw_version", "master_node_version", "fw_version"].includes(key)) { + timestampDiffToRemoveKey = 24 * 60 * 60 * 1000; + } - if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { + if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { - let diff = timestamp - attributeData.ts; - if (!timestampDiffToRemoveKey) timestampDiffToRemoveKey = this.getDiffTimestamp(key); + let diff = timestamp - attributeData.ts; + if (!timestampDiffToRemoveKey) timestampDiffToRemoveKey = this.getDiffTimestamp(key); - if (diff > timestampDiffToRemoveKey) { - attributeData.ts = Date.now(); - //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); - } - else { - delete values[key]; - //if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey); - } - } - else { - attributeData.value = value; - attributeData.ts = timestamp; - } + if (diff > timestampDiffToRemoveKey) { + attributeData.ts = Date.now(); + //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); + } + else { + delete values[key]; + //if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey); + } + } + else { + attributeData.value = value; + attributeData.ts = timestamp; + } - } + } - return values; - } + return values; + } } module.exports = DataToTbHandler; diff --git a/flow/helper/utils.js b/flow/helper/utils.js index 9c054aa..57b0f89 100644 --- a/flow/helper/utils.js +++ b/flow/helper/utils.js @@ -1,112 +1,170 @@ -function bytesToInt(bytes, numberOfBytes) { - let buffer = []; - if (Array.isArray(bytes)) { - buffer = bytes.slice(0); - if (numberOfBytes != undefined) { - buffer = bytes.slice(bytes.length - numberOfBytes); - } - } - else buffer.push(bytes); - - let result = 0; - for (let i = 0; i < buffer.length; i++) { - result = (result << 8) | buffer[i]; - } - - return result >>> 0; //ensure it's an unsigned 32-bit number -} - -function resizeArray(arr, newSize, defaultValue) { - while (newSize > arr.length) - arr.push(defaultValue); - arr.length = newSize; -} - -longToByteArray = function(/*long*/long) { - // we want to represent the input as a 8-bytes array - var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; - - for (var index = 0; index < byteArray.length; index++) { - var byte = long & 0xff; - byteArray[index] = byte; - long = (long - byte) / 256; - } - - return byteArray; -}; - -function addDays(date, days) { - var result = new Date(date); - result.setDate(result.getDate() + days); - return result; -} - -/* -sleep(2000).then(() => { - // Do something after the sleep! - - -}); -*/ - -function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - -function isEmptyObject(obj) { - for (var name in obj) { - return false; - } - return true; -} - -function convertUTCDateToLocalDate(date) { - var newDate = new Date(date); - newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset()); - return newDate; -} - -function addZeroBefore(n) { - return (n < 10 ? '0' : '') + n; -} - -var convertBase = function() { - - function convertBase(baseFrom, baseTo) { - return function(num) { - return parseInt(num, baseFrom).toString(baseTo); - - }; - } - - // binary to decimal - convertBase.bin2dec = convertBase(2, 10); - - // binary to hexadecimal - convertBase.bin2hex = convertBase(2, 16); - - // decimal to binary - convertBase.dec2bin = convertBase(10, 2); - - // decimal to hexadecimal - convertBase.dec2hex = convertBase(10, 16); - - // hexadecimal to binary - convertBase.hex2bin = convertBase(16, 2); - - // hexadecimal to decimal - convertBase.hex2dec = convertBase(16, 10); - - return convertBase; -}(); - -module.exports = { - bytesToInt, - longToByteArray, - addDays, - addZeroBefore, - resizeArray, - isEmptyObject, - sleep, - convertUTCDateToLocalDate -} +function bytesToInt_orig(bytes, numberOfBytes) { + + let buffer = []; + if (Array.isArray(bytes)) { + buffer = bytes.slice(0); + if (numberOfBytes != undefined) { + buffer = bytes.slice(bytes.length - numberOfBytes); + } + } + else buffer.push(bytes); + //var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; + + let l = (buffer.length - 1) * 8; + let decimal = 0; + for (let i = 0; i < buffer.length; i++) { + var s = buffer[i] << l; + if (l < 8) s = buffer[i] + decimal = decimal + s; + l = l - 8; + } + // console.log("decimal utils.js: ", decimal); + + let decimal1 = 0n; + for (let i = 0; i < buffer.length; i++) { + decimal1 += BigInt(buffer[i]) * (2n ** BigInt((buffer.length - 1 - i) * 8)); + } + // console.log("decimal biging utils.js: ", decimal1); + return decimal; +} + +//bytestouintBE +function bytesToInt(bytes, numberOfBytes) { + + let buffer = []; + if (Array.isArray(bytes)) { + buffer = bytes.slice(0); + if (numberOfBytes != undefined) { + buffer = bytes.slice(bytes.length - numberOfBytes); + } + } + else buffer.push(bytes); + + console.log(bytes, buffer); + + let result = 0; + for (let i = 0; i < buffer.length; i++) { + result = (result << 8) | bytes[i]; + } + // console.log("decimal biging utils.js: ", decimal1); + + console.log("originall: ", bytesToInt_orig(buffer)); + console.log("uint little endian: ", bytesToUintLE(buffer)); + console.log('neww: ', result >>> 0); + return result >>> 0; +} + +function bytesToUintLE(bytes, numberOfBytes) { + + let buffer = []; + if (Array.isArray(bytes)) { + buffer = bytes.slice(0); + if (numberOfBytes != undefined) { + buffer = bytes.slice(bytes.length - numberOfBytes); + } + } + else buffer.push(bytes); + //var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; + + let result = 0; + for (let i = buffer.length - 1; i <= 0; i--) { + result = (result << 8) | bytes[i]; + } + return result >>> 0; +} + + +function resizeArray(arr, newSize, defaultValue) { + while (newSize > arr.length) + arr.push(defaultValue); + arr.length = newSize; +} + +longToByteArray = function(/*long*/long) { + // we want to represent the input as a 8-bytes array + var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (var index = 0; index < byteArray.length; index++) { + var byte = long & 0xff; + byteArray[index] = byte; + long = (long - byte) / 256; + } + + return byteArray; +}; + +function addDays(date, days) { + var result = new Date(date); + result.setDate(result.getDate() + days); + return result; +} + +/* +sleep(2000).then(() => { + // Do something after the sleep! + + +}); +*/ + +function sleep(time) { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +function isEmptyObject(obj) { + for (var name in obj) { + return false; + } + return true; +} + +function convertUTCDateToLocalDate(date) { + var newDate = new Date(date); + newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset()); + return newDate; +} + +function addZeroBefore(n) { + return (n < 10 ? '0' : '') + n; +} + +var convertBase = function() { + + function convertBase(baseFrom, baseTo) { + return function(num) { + return parseInt(num, baseFrom).toString(baseTo); + + }; + } + + // binary to decimal + convertBase.bin2dec = convertBase(2, 10); + + // binary to hexadecimal + convertBase.bin2hex = convertBase(2, 16); + + // decimal to binary + convertBase.dec2bin = convertBase(10, 2); + + // decimal to hexadecimal + convertBase.dec2hex = convertBase(10, 16); + + // hexadecimal to binary + convertBase.hex2bin = convertBase(16, 2); + + // hexadecimal to decimal + convertBase.hex2dec = convertBase(16, 10); + + return convertBase; +}(); + +module.exports = { + bytesToInt, + longToByteArray, + addDays, + addZeroBefore, + resizeArray, + isEmptyObject, + sleep, + convertUTCDateToLocalDate +} diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index d6d9dad..5852ff3 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -16,7 +16,7 @@ exports.readme = ` `; const modbus = require('jsmodbus'); -const SerialPort = require('serialport'); +const {SerialPort} = require('serialport'); const { timeoutInterval, deviceConfig } = require("../databases/modbus_config"); const { sendNotification } = require('./helper/notification_reporter'); @@ -36,7 +36,6 @@ let mainSocket; let phases; //phases where voltage is 0 (set) let noVoltage; -let energyToSwitchLamps; exports.install = function(instance) { @@ -77,13 +76,8 @@ exports.install = function(instance) { let obj = this; - if (this.socket) { - this.socket.removeAllListeners(); - this.socket = null; - } - - this.socket = new SerialPort("/dev/ttymxc0", { - baudRate: 9600, + this.socket = new SerialPort({path: "/dev/ttymxc0", + baudRate: 9600 }) // we create a client for every deviceAddress ( = address) in list and push them into dictionary @@ -92,11 +86,15 @@ exports.install = function(instance) { } this.socket.on('error', function(e) { - console.log('Modbus_reader: Socket connection error', e); //'ECONNREFUSED' or 'ECONNRESET' ?? + console.log('socket connection error', e); + if (e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') { + console.log(exports.title + ' Waiting 10 seconds before trying to connect again'); + setTimeout(obj.startSocket, 10000); + } }); this.socket.on('close', function() { - console.log('Modbus_reader: Socket connection closed - Waiting 10 seconds before connecting again'); + console.log('Socket connection closed ' + exports.title + ' Waiting 10 seconds before trying to connect again'); setTimeout(obj.startSocket, 10000); }); @@ -117,8 +115,7 @@ exports.install = function(instance) { this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number this.device = dev.device; //em340, twilight_sensor - //if we just start to loop devices from the beginning, or there is just 1 device in config, we wait whole timeoutInterval - if (this.indexInDeviceConfig == 0 || deviceConfig.length === 1) setTimeout(this.readRegisters, this.timeoutInterval); + if (this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval); else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES); } @@ -307,12 +304,15 @@ exports.install = function(instance) { const actualTotalPower = values.total_power; - if (actualTotalPower > energyToSwitchLamps && this.onNotificationSent == false) { + const numberOfNodes = Object.keys(FLOW.GLOBALS.nodesData).length; + if (numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300 + + if (actualTotalPower > numberOfNodes * 15 && this.onNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance); this.onNotificationSent = true; this.offNotificationSent = false; } - else if (actualTotalPower <= energyToSwitchLamps && this.offNotificationSent == false) { + else if (actualTotalPower <= numberOfNodes * 15 && this.offNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance); this.onNotificationSent = false; this.offNotificationSent = true; @@ -330,9 +330,9 @@ exports.install = function(instance) { phases = FLOW.GLOBALS.settings.phases; tbName = FLOW.GLOBALS.settings.rvoTbName; noVoltage = FLOW.GLOBALS.settings.no_voltage; - energyToSwitchLamps = FLOW.GLOBALS.settings.energy_to_switch_lamps / 2.5; //half value is enought to show if lamps are turned on or off - if (deviceConfig.length) mainSocket = new SocketWithClients(); - else console.log("Modbus_reader: no modbus device in configuration"); + mainSocket = new SocketWithClients(); + + console.log("novoltage: ", noVoltage, typeof noVoltage); // this notification is to show, that flow (unipi) has been restarted sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance); diff --git a/flow/variables.txt b/flow/variables.txt new file mode 100644 index 0000000..e69de29 diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index 0eba804..acc3c33 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -44,9 +44,9 @@ const fs = require('fs'); const mqtt = require('mqtt'); const SEND_TO = { - debug: 0, - rpcCall: 1, - services: 2 + debug: 0, + rpcCall: 1, + services: 2 } //CONFIG @@ -72,13 +72,13 @@ let sendClientError = true; process.on('uncaughtException', function(err) { - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); + errLogger.error('uncaughtException:', err.message) + errLogger.error(err.stack); - //TODO - //send to service + //TODO + //send to service - //process.exit(1); + //process.exit(1); }) const nosql = NOSQL('tbdata'); @@ -87,362 +87,364 @@ const nosqlBackup = NOSQL('/backup/tbdata'); exports.install = function(instance) { - var client; - var opts; - var clientReady = false; - - // wsmqtt status for notification purposes on projects.worksys.io database - let wsmqttName = null; - let sendWsStatusVar = null; - let wsmqtt_status = 'disconnected'; - - function getWsmqttName(host) { - if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; - else if (host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; - else if (host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; - } - - function sendWsStatus() { - instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status }); - } - - - function main() { - if (!FLOW.dbLoaded) return; - - loadSettings(); - clearInterval(sendWsStatus); - sendWsStatusVar = setInterval(sendWsStatus, 180000); - } - - //set opts according to db settings - function loadSettings() { - - if (instance.options.host !== "") { - //override settings from database - var o = instance.options; - opts = { - host: o.host, - port: o.port, - clientId: o.clientid, - username: o.username, - rejectUnauthorized: false, - resubscribe: false - }; - - wsmqttName = getWsmqttName(o.host); - - console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); - } - else { - - const SETTINGS = FLOW.GLOBALS.settings; - backup_on_failure = SETTINGS.backup_on_failure; - saveTelemetryOnError = backup_on_failure; - - restore_from_backup = SETTINGS.restore_from_backup; - restore_backup_wait = SETTINGS.restore_backup_wait; - - let mqtt_host = SETTINGS.mqtt_host; - let mqtt_clientid = SETTINGS.mqtt_clientid; - let mqtt_username = SETTINGS.mqtt_username; - let mqtt_port = SETTINGS.mqtt_port; - - opts = { - host: mqtt_host, - port: mqtt_port, - keepalive: 10, - clientId: mqtt_clientid, - username: mqtt_username, - rejectUnauthorized: false, - resubscribe: false - }; - - wsmqttName = getWsmqttName(mqtt_host); - } - - connectToTbServer(); - } - - function connectToTbServer() { - var url = "mqtt://" + opts.host + ":" + opts.port; - console.log("MQTT URL: ", url); - - client = mqtt.connect(url, opts); - - client.on('connect', function() { - instance.status("Connected", "green"); - //monitor.info("MQTT client connected"); - - sendClientError = true; - clientReady = true; - wsmqtt_status = 'connected'; - }); - - client.on('reconnect', function() { - instance.status("Reconnecting", "yellow"); - clientReady = false; - }); - - client.on('message', function(topic, message) { - // message is type of buffer - message = message.toString(); - if (message[0] === '{') { - TRY(function() { - - message = JSON.parse(message); - if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { - client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); - instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); - } - - }, () => instance.debug('MQTT: Error parsing data', message)); - } - - instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message }); - }); - - client.on('close', function() { - clientReady = false; - wsmqtt_status = 'disconnected'; - - instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); - }); - - client.on('error', function(err) { - instance.status("Err: " + err.code, "red"); - instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); - if (sendClientError) { - monitor.info('MQTT client error', err); - sendClientError = false; - } - clientReady = false; - wsmqtt_status = 'disconnected'; - }); - - } - - - instance.on("0", _ => { - main(); - }) + var client; + var opts; + var clientReady = false; + + // wsmqtt status for notification purposes on projects.worksys.io database + let wsmqttName = null; + let sendWsStatusVar = null; + let wsmqtt_status = 'disconnected'; + + function getWsmqttName(host) { + if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; + else if (host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; + else if (host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; + } + + function sendWsStatus() { + instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status }); + } + + + function main() { + if (!FLOW.dbLoaded) return; + + loadSettings(); + clearInterval(sendWsStatus); + sendWsStatusVar = setInterval(sendWsStatus, 180000); + } + + //set opts according to db settings + function loadSettings() { + + if (instance.options.host !== "") { + //override settings from database + var o = instance.options; + opts = { + host: o.host, + port: o.port, + clientId: o.clientid, + username: o.username, + rejectUnauthorized: false, + resubscribe: false + }; + + wsmqttName = getWsmqttName(o.host); + + console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); + } + else { + + const SETTINGS = FLOW.GLOBALS.settings; + backup_on_failure = SETTINGS.backup_on_failure; + saveTelemetryOnError = backup_on_failure; + + restore_from_backup = SETTINGS.restore_from_backup; + restore_backup_wait = SETTINGS.restore_backup_wait; + + let mqtt_host = SETTINGS.mqtt_host; + let mqtt_clientid = SETTINGS.mqtt_clientid; + let mqtt_username = SETTINGS.mqtt_username; + let mqtt_port = SETTINGS.mqtt_port; + + opts = { + host: mqtt_host, + port: mqtt_port, + keepalive: 10, + clientId: mqtt_clientid, + username: mqtt_username, + rejectUnauthorized: false, + resubscribe: false, + }; + + wsmqttName = getWsmqttName(mqtt_host); + } + + connectToTbServer(); + } + + function connectToTbServer() { + var url = "mqtt://" + opts.host + ":" + opts.port; + console.log("MQTT URL: ", url); + + client = mqtt.connect(url, opts); + + client.on('connect', function() { + instance.status("Connected", "green"); + //monitor.info("MQTT client connected"); + + sendClientError = true; + clientReady = true; + wsmqtt_status = 'connected'; + }); + + client.on('reconnect', function() { + instance.status("Reconnecting", "yellow"); + clientReady = false; + }); + + client.on('message', function(topic, message) { + // message is type of buffer + message = message.toString(); + if (message[0] === '{') { + + try { + message = JSON.parse(message); + if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { + client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); + instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); + } + + } catch (e) { + console.log('MQTT: Error parsing data', e); + } + } + + instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message }); + }); + + client.on('close', function() { + clientReady = false; + wsmqtt_status = 'disconnected'; + + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); + }); + + client.on('error', function(err) { + instance.status("Err: " + err.code, "red"); + instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); + if (sendClientError) { + monitor.info('MQTT client error', err); + sendClientError = false; + } + clientReady = false; + wsmqtt_status = 'disconnected'; + }); + + } + + + instance.on("0", _ => { + main(); + }) - instance.on('1', function(data) { + instance.on('1', function(data) { - if (clientReady) { - //do we have some data in backup file? if any, process data from database - if (saveTelemetryOnError) { - //read telemetry data and send back to server - if (!processingData) processDataFromDatabase(); - } + if (clientReady) { + //do we have some data in backup file? if any, process data from database + if (saveTelemetryOnError) { + //read telemetry data and send back to server + if (!processingData) processDataFromDatabase(); + } - let stringifiedJson = JSON.stringify(data.data); - client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 }); + let stringifiedJson = JSON.stringify(data.data); + client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 }); - //backup telemetry - if (createTelemetryBackup) { - data.data.id = UID(); - nosqlBackup.insert(data.data); + //backup telemetry + if (createTelemetryBackup) { + data.data.id = UID(); + nosqlBackup.insert(data.data); - insertBackupNoSqlCounter++; - if (insertBackupNoSqlCounter > 150) { - let options = { compress: true }; - let path = __dirname + "/../databases/backup/tbdata.nosql"; - var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options); - stream.write(""); - stream.end(); + insertBackupNoSqlCounter++; + if (insertBackupNoSqlCounter > 150) { + let options = { compress: true }; + let path = __dirname + "/../databases/backup/tbdata.nosql"; + var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options); + stream.write(""); + stream.end(); - insertBackupNoSqlCounter = 0; - } - } + insertBackupNoSqlCounter = 0; + } + } - } - else { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); - instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); + } + else { + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); - if (saveTelemetryOnError) { - //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql - makeBackupFromDbFile(); + if (saveTelemetryOnError) { + //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql + makeBackupFromDbFile(); - //write to tb - data.data.id = UID(); - nosql.insert(data.data); - } - } - }); + //write to tb + data.data.id = UID(); + nosql.insert(data.data); + } + } + }); - instance.close = function(done) { - if (clientReady) { - client.end(); - clearInterval(sendWsStatusVar); - } - }; + instance.close = function(done) { + if (clientReady) { + client.end(); + clearInterval(sendWsStatusVar); + } + }; - function getDbBackupFileCounter(type) { - var files = fs.readdirSync(__dirname + "/../databases"); + function getDbBackupFileCounter(type) { + var files = fs.readdirSync(__dirname + "/../databases"); - let counter = 0; - for (var i = 0; i < files.length; i++) { + let counter = 0; + for (var i = 0; i < files.length; i++) { - if (files[i] == "tbdata.nosql") continue; + if (files[i] == "tbdata.nosql") continue; - if (files[i].endsWith(".nosql")) { + if (files[i].endsWith(".nosql")) { - let pos = files[i].indexOf("."); - if (pos > -1) { + let pos = files[i].indexOf("."); + if (pos > -1) { - let fileCounter = counter; - let firstDigit = files[i].slice(0, pos); + let fileCounter = counter; + let firstDigit = files[i].slice(0, pos); - fileCounter = parseInt(firstDigit); - if (isNaN(fileCounter)) fileCounter = 0; - //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); + fileCounter = parseInt(firstDigit); + if (isNaN(fileCounter)) fileCounter = 0; + //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); - if (type == "max") { - if (fileCounter > counter) { - counter = fileCounter; - } - } - else if (type == "min") { - if (counter == 0) counter = fileCounter; + if (type == "max") { + if (fileCounter > counter) { + counter = fileCounter; + } + } + else if (type == "min") { + if (counter == 0) counter = fileCounter; - if (fileCounter < counter) { - counter = fileCounter; - } - } - } - } + if (fileCounter < counter) { + counter = fileCounter; + } + } + } + } - } + } - if (type == "max") counter++; + if (type == "max") counter++; - return counter; - } + return counter; + } - const makeBackupFromDbFile = async () => { + const makeBackupFromDbFile = async () => { - if (!saveTelemetryOnError) return; + if (!saveTelemetryOnError) return; - //to avoid large file: tbdata.nosql + //to avoid large file: tbdata.nosql - //init value is 0! - if (insertNoSqlCounter > 0) { - --insertNoSqlCounter; - return; - } + //init value is 0! + if (insertNoSqlCounter > 0) { + --insertNoSqlCounter; + return; + } - insertNoSqlCounter = 100; + insertNoSqlCounter = 100; - let source = __dirname + "/../databases/tbdata.nosql"; + let source = __dirname + "/../databases/tbdata.nosql"; - var stats = fs.statSync(source); - var fileSizeInBytes = stats.size; + var stats = fs.statSync(source); + var fileSizeInBytes = stats.size; - if (fileSizeInBytes > noSqlFileSizeLimit) { + if (fileSizeInBytes > noSqlFileSizeLimit) { - let counter = 1; - counter = getDbBackupFileCounter("max"); + let counter = 1; + counter = getDbBackupFileCounter("max"); - let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql"; + let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql"; - //make backup file - fs.copyFileSync(source, destination); - //fs.renameSync(p, p + "." + counter); + //make backup file + fs.copyFileSync(source, destination); + //fs.renameSync(p, p + "." + counter); - //clear tbdata.nosql - fs.writeFileSync(source, ""); - fs.truncateSync(source, 0); + //clear tbdata.nosql + fs.writeFileSync(source, ""); + fs.truncateSync(source, 0); - } - } + } + } - const processDataFromDatabase = async () => { + const processDataFromDatabase = async () => { - if (restore_from_backup <= 0) return; + if (restore_from_backup <= 0) return; - //calculate diff - const now = new Date(); - let currentTime = now.getTime(); - let diff = currentTime - lastRestoreTime; + //calculate diff + const now = new Date(); + let currentTime = now.getTime(); + let diff = currentTime - lastRestoreTime; - if ((diff / 1000) < restore_backup_wait) { - //console.log("*********restore_backup_wait", diff, restore_backup_wait); - return; - } + if ((diff / 1000) < restore_backup_wait) { + //console.log("*********restore_backup_wait", diff, restore_backup_wait); + return; + } - processingData = true; + processingData = true; - //get filename to process - let counter = getDbBackupFileCounter("min"); + //get filename to process + let counter = getDbBackupFileCounter("min"); - //we have some backup files - let dataBase = 'tbdata'; + //we have some backup files + let dataBase = 'tbdata'; - var nosql; - if (counter == 0) dataBase = 'tbdata'; - else dataBase = counter + "." + 'tbdata'; + var nosql; + if (counter == 0) dataBase = 'tbdata'; + else dataBase = counter + "." + 'tbdata'; - nosql = NOSQL(dataBase); + nosql = NOSQL(dataBase); - //select all data - use limit restore_from_backup - let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); + //select all data - use limit restore_from_backup + let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); - for (let i = 0; i < records.length; i++) { - if (clientReady) { + for (let i = 0; i < records.length; i++) { + if (clientReady) { - let item = records[i]; - let id = item.id; + let item = records[i]; + let id = item.id; - if (id !== undefined) { - //console.log("------------processDataFromDatabase - remove", id, dataBase, i); + if (id !== undefined) { + //console.log("------------processDataFromDatabase - remove", id, dataBase, i); - try { + try { - let message = JSON.parse(JSON.stringify(item)); - delete message.id; + let message = JSON.parse(JSON.stringify(item)); + delete message.id; - client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 }); + client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 }); - //remove from database - await promisifyBuilder(nosql.remove().where("id", id)); + //remove from database + await promisifyBuilder(nosql.remove().where("id", id)); - } catch (error) { - //process error - console.log("processDataFromDatabase", error); - } + } catch (error) { + //process error + console.log("processDataFromDatabase", error); + } - } + } - } - else { - processingData = false; - return; - } - } + } + else { + processingData = false; + return; + } + } - if (records.length > 0) { - //clean backup file - if (counter > 0) nosql.clean(); - } + if (records.length > 0) { + //clean backup file + if (counter > 0) nosql.clean(); + } - //no data in db, remove - if (records.length == 0) { - if (counter > 0) nosql.drop(); - } + //no data in db, remove + if (records.length == 0) { + if (counter > 0) nosql.drop(); + } - const d = new Date(); - lastRestoreTime = d.getTime(); + const d = new Date(); + lastRestoreTime = d.getTime(); - processingData = false; + processingData = false; - } + } - instance.on('options', main); - //instance.reconfigure(); + instance.on('options', main); + //instance.reconfigure(); };