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); + }) +};