From a30a8588a761d067ef23fe578488108d2e01d2df Mon Sep 17 00:00:00 2001 From: rasta5man Date: Sat, 13 Apr 2024 21:46:15 +0200 Subject: [PATCH] Actual code running on CEZ rvo --- config | 5 +- databases/modbus_config.js | 113 -- flow/cmd_manager.js | 1527 +++++++++++------ flow/designer.json | 662 +++---- ...dido_controller.js => di_do_controller.js} | 442 ++--- flow/gettemperature.js | 40 +- flow/modbus_citysys.js | 1135 ++++++++++++ flow/modbus_reader.js | 336 ---- flow/thermometer.js | 220 --- flow/wsmqttpublish.js | 11 +- package.json | 4 +- saved_data/modbus_settings | 174 ++ 12 files changed, 2841 insertions(+), 1828 deletions(-) delete mode 100644 databases/modbus_config.js rename flow/{dido_controller.js => di_do_controller.js} (81%) create mode 100644 flow/modbus_citysys.js delete mode 100644 flow/modbus_reader.js delete mode 100644 flow/thermometer.js create mode 100644 saved_data/modbus_settings diff --git a/config b/config index b10cd71..60af4d9 100644 --- a/config +++ b/config @@ -1,12 +1,13 @@ name : Total.js Flow -default_timezone : Europe/Bratislava + +default_timezone : Europe/Bratislava // Packages settings package#flow (Object) : { url: '/' } table.relays : line:number|tbname:string|contactor:number|profile:string -table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean +table.nodes : node:number|tbname:string|line:number|dimming:number|profile:string|processed:boolean|status:boolean table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number table.pins : pin:string|type:string|line:number table.notifications : key:string|weight:string|sk:string|en:string diff --git a/databases/modbus_config.js b/databases/modbus_config.js deleted file mode 100644 index d63430f..0000000 --- a/databases/modbus_config.js +++ /dev/null @@ -1,113 +0,0 @@ -const timeoutInterval = 300000; -const deviceConfig = [ - { - device: "em340", - deviceAddress: 1, - stream: [ - { - "tbAttribute": "Phase_1_voltage", - "register": 0, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "Phase_2_voltage", - "register": 2, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "Phase_3_voltage", - "register": 4, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "Phase_1_current", - "register": 12, - "size": 2, - "multiplier": 0.001 - }, - { - "tbAttribute": "Phase_2_current", - "register": 14, - "size": 2, - "multiplier": 0.001 - }, - { - "tbAttribute": "Phase_3_current", - "register": 16, - "size": 2, - "multiplier": 0.001 - }, - { - "tbAttribute": "Phase_1_power", - "register": 18, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "Phase_2_power", - "register": 20, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "Phase_3_power", - "register": 22, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "total_power", - "register": 40, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "total_energy", - "register": 52, - "size": 2, - "multiplier": 0.1 - }, - { - "tbAttribute": "Phase_1_pow_factor", - "register": 46, - "size": 1, - "multiplier": 0.001 - }, - { - "tbAttribute": "Phase_2_pow_factor", - "register": 47, - "size": 1, - "multiplier": 0.001 - }, - { - "tbAttribute": "Phase_3_pow_factor", - "register": 48, - "size": 1, - "multiplier": 0.001 - }, - { - "tbAttribute": "power_factor", - "register": 49, - "size": 1, - "multiplier": 0.001 - } - ] - }, - { - device: "twilight_sensor", - deviceAddress: 2, - stream: [ - { - "tbAttribute": "twilight_sensor", - "register": 60, - "size": 2, - "multiplier": 1 - } - ] - } -]; - -module.exports = { timeoutInterval, deviceConfig }; diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index c1efd18..f92e8d4 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -19,15 +19,15 @@ exports.html = `
RPC - run RPC calls

-
@(User)
-
-
-
@(Password)
-
-
-
@(My edge)
-
-
+
@(User)
+
+
+
@(Password)
+
+
+
@(My edge)
+
+
`; @@ -89,9 +89,13 @@ const gmtOffset = 0; //priorities for registers let priorities = []; -let minutes = 1; -priorities["0"] = minutes; +let minutes = 0.2; priorities["1"] = minutes; +priorities["42"] = minutes; + +minutes = 1; +priorities["0"] = minutes; +priorities["104"] = minutes; minutes = 5; priorities["74"] = minutes; @@ -110,9 +114,10 @@ priorities["80"] = minutes; priorities["8"] = minutes; priorities["3"] = minutes; priorities["89"] = minutes; +priorities["95"] = minutes; //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app -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,42,74,75,76,77,78,79,80,84,87,89,95,104]; //1 - dimming @@ -236,9 +241,31 @@ function processNodeProfile(node) logger.debug("processNodeProfile: start - set profile for ", node, profile); let nodeProfile; - try{ + try { + nodeProfile = JSON.parse( profile ); - if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined"); + + if(Object.keys(nodeProfile).length === 0) { + + //ak nie je pre node nastaveny profil, nastavime dimming nodu podla hodnoty v nodes.table + console.log('____________ttttt', nodeObj.tbname, node, nodeObj.dimming) + let params = getParams(priorityTypes.high_priority); + params.type = "cmd"; + params.tbname = nodeObj.tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.byte4 = nodeObj.dimming; + params.rw = 1;//write + params.timestamp = priorityTypes.high_priority; + params.info = 'setNodeDimming'; + //params.debug = true; + logger.debug("dimming", params); + tasks.push(params); + + throw ("profile is not defined"); + + } } catch (error) {} //test reset profilu @@ -299,7 +326,7 @@ function processNodeProfile(node) //let timestamp = priorityTypes.node_cmd; - //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu + //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu let params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; @@ -320,6 +347,21 @@ function processNodeProfile(node) logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); +// "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" +// } +// ] + //TS1 Time point a TS1 Time Point Levels let register = 9; for(let i = 0; i < nodeProfile.intervals.length; i++) @@ -327,7 +369,7 @@ function processNodeProfile(node) let obj = nodeProfile.intervals[i]; //let timePoint = obj.time_point; let dim_value = obj.value; - + let cct = obj.cct; //Reg 9 až Reg 40 @@ -336,16 +378,16 @@ function processNodeProfile(node) 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é + 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 - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //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(":"); @@ -373,13 +415,18 @@ function processNodeProfile(node) register++; timestamp++; + // ked zapisujeme Time point levels, zapisujeme: + // byte1 = 1, byte2 = CCT_H, byte3 = CCT_L, byte 4 = dimming + byte2 = Math.floor(cct/256); + byte3 = cct - (byte2 * 256); + params = getParams(priorityTypes.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.byte1 = 1; + params.byte2 = byte2; + params.byte3 = byte3; + params.byte4 = parseInt(dim_value) + 128; params.recipient = 1; params.register = register; params.rw = 1;//write @@ -483,8 +530,8 @@ function processNodeProfile(node) { //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) + //Bity 0 – 6: hodnota v minútach + //Bit 7: znamienko (1 – mínus) logger.debug("processNodeProfile: Static offset", node); @@ -557,7 +604,7 @@ function processNodeProfile(node) //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) bits.push(0); - //Bity 6-7 - zatiaľ nepoužité + //Bity 6-7 - zatiaľ nepoužité bits.push(0); bits.push(0); @@ -612,7 +659,7 @@ const instanceSendTo = { debug: 0, tb: 1, http_response: 2, - dido_controller: 3, + di_do_controller: 3, infoSender: 4 } @@ -783,17 +830,44 @@ exports.install = function(instance) { for (let k in nodesData) { //node:number|tbname:string|line:number|profile:string|processed:boolean + + + let node = nodesData[k]; + //potrebujem nody k danej linii - if(record.line == nodesData[k].line) + if(record.line == node.line) { - let node = nodesData[k].node; - let processed = nodesData[k].processed; + let address = node.node; + console.log('^^^^ggggggg',address) + let processed = node.processed; + let dimming = node.dimming; + if(!node.profile) + { + console.log('+-+-+----llllll - posielam dimming') + let params = getParams(priorityTypes.high_priority); + + //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime + params.type = "cmd"; + params.tbname = node.tbname; + params.address = address; + params.register = 1;//dimming + params.recipient = 1;//slave + params.byte4 = dimming; + params.rw = 1;//write + params.timestamp = priorityTypes.high_priority; + params.info = 'setNodeDimming'; + //params.debug = true; + + tasks.push(params) + } if(!processed) { - processNodeProfile(node); + console.log('=====procesujem profille') + processNodeProfile(address); } - else{ + else + { //logger.debug( `node ${node} profile for line ${nodesData[k].line} was already processed`); } } @@ -996,7 +1070,7 @@ exports.install = function(instance) { logger.debug("linia", line, obj); - instance.send(instanceSendTo.dido_controller, obj); + instance.send(instanceSendTo.di_do_controller, obj); } function turnOffLine(line, info) @@ -1009,7 +1083,7 @@ exports.install = function(instance) { logger.debug("linia", line, obj); - instance.send(instanceSendTo.dido_controller, obj); + instance.send(instanceSendTo.di_do_controller, obj); } function detectIfResponseIsValid(bytes) @@ -1131,7 +1205,6 @@ exports.install = function(instance) { let sunCalcResult = calculateDuskDown(new Date(), undefined, duskOffset, dawnOffset); console.log(sunCalcResult); - monitor.info("--> dusk - dawn", sunCalcResult); //if(isDusk) time_points[t].value = 1;//sumrak - zapneme svetlo //if(isDawn) time_points[t].value = 0;//vychod - vypneme svetlo @@ -1313,7 +1386,7 @@ exports.install = function(instance) { let time_points = profile.time_points; if(time_points == undefined) time_points = profile.intervals; - //monitor.info("buildTasks: time_points", time_points); + // monitor.info("buildTasks: time_points", time_points); let currentValue = 0; if(time_points.length > 0) currentValue = time_points[ time_points.length - 1].value; @@ -1324,7 +1397,7 @@ exports.install = function(instance) { //let now = new Date().toLocaleString("en-US", {timeZone: "Europe/Bratislava"}); let sunCalcResult = calculateDuskDown(new Date(), line); - // monitor.info("dusk and dawn sunCalcResult", line, sunCalcResult); + monitor.info("dusk and dawn sunCalcResult", line, sunCalcResult); //add to timpoints if(profile.dawn_lux_sensor == false) time_points.push( {"start_time": sunCalcResult["dawn"], "value": 1, "isDawn": true} ); @@ -1507,8 +1580,7 @@ exports.install = function(instance) { { //run broadcast Time of dusk - // addMinutesToTimestamp = 60*5; - addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk + addMinutesToTimestamp = 60*3; let params = getParams(priorityTypes.node_broadcast); @@ -1540,16 +1612,14 @@ exports.install = function(instance) { params.timestamp = timestampStart; 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 + addMinutesToTimestamp = 60*3; let params = getParams(priorityTypes.node_broadcast); @@ -1824,7 +1894,6 @@ exports.install = function(instance) { { let line = keys[i];//line is turned off by default let profilestr = relaysData[line].profile; - let contactor = relaysData[line].contactor; // 0 or 1 - vypnuta/zapnuta try{ @@ -1833,7 +1902,7 @@ exports.install = function(instance) { if(profile.astro_clock == true) { - let sunCalcResult = calculateDuskDown(now, line); + let sunCalcResult = calculateDuskDown(date, line); //dawn: usvit/vychod - lux je nad hranicou - vypnem //dusk: zapad pod hranicou - zapnem @@ -1842,13 +1911,10 @@ exports.install = function(instance) { //"dusk_lux_sensor_time_window": 30, //vychod - // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut if(profile.dawn_lux_sensor == true) { - let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); - let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); - //console.log('------>>>', new Date(lux_sensor_time_window1), new Date(lux_sensor_time_window2), lux_sensor_time_window1, lux_sensor_time_window2) - //console.log('++++-->>>', new Date(sunCalcResult.dusk_time), new Date(sunCalcResult.dawn_time)) + let lux_sensor_time_window1 = sunCalcResult.dawn_time - parseInt( profile.dawn_lux_sensor_time_window ); + let lux_sensor_time_window2 = sunCalcResult.dawn_time + parseInt( profile.dawn_lux_sensor_time_window ); if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { @@ -1856,13 +1922,13 @@ exports.install = function(instance) { if(lux_sensor_value > profile.dawn_lux_sensor_value) { //vypnem - if(contactor) turnOffLine(line, "profile: dawn - turnOff line according to lux sensor"); + turnOffLine(line, "profile: dawn - turnOff line according to lux sensor"); + } + else + { + //zapnem + turnOnLine(line, "profile: dawn - turnOn line according to lux sensor"); } - // else - // { - // //zapnem - // if(!contactor) turnOnLine(line, "profile: dawn - turnOn line according to lux sensor"); - // } } @@ -1877,8 +1943,8 @@ exports.install = function(instance) { //zapad 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); + let lux_sensor_time_window1 = sunCalcResult.dusk_time - parseInt( profile.dusk_lux_sensor_time_window ); + let lux_sensor_time_window2 = sunCalcResult.dusk_time + parseInt( profile.dusk_lux_sensor_time_window ); if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { @@ -1886,13 +1952,13 @@ exports.install = function(instance) { if(lux_sensor_value < profile.dusk_lux_sensor_value) { //zapnem - if(!contactor) turnOnLine(line, "profile: dusk - turnOn line according to lux sensor"); + turnOnLine(line, "profile: dusk - turnOff line according to lux sensor"); + } + else + { + //vypnem + turnOffLine(line, "profile: dusk - turnOff line according to lux sensor"); } - // else - // { - // //vypnem - // if(contactor) turnOffLine(line, "profile: dusk - turnOff line according to lux sensor"); - // } } } @@ -1938,7 +2004,28 @@ exports.install = function(instance) { }); } } - + + async function updateNodeDimming(node, value) { + if(node == 0) return; + + + let nodeObj = nodesData[node]; + console.log('-------nodeObjjjj', nodeObj); + + if(nodeObj == undefined || value == undefined) return; + + console.log('*-*-**-pppppp',nodeObj.dimming, value) + if(nodeObj.dimming !== value) + { + await dbNodes.modify({ dimming: value }).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if(err == null) nodesData[node].dimming = value; + console.log('+++//** NDffff',nodesData) + }); + }); + } + } + async function runTasks() { @@ -2124,8 +2211,6 @@ exports.install = function(instance) { } } - - //kontrola nespracovanych profilov nodov if(type == "process_profiles") { @@ -2373,6 +2458,8 @@ exports.install = function(instance) { //set dusk/down for broadcast + + //Time of dusk if(params.register == 6 && params.recipient === 2) { @@ -2507,17 +2594,101 @@ exports.install = function(instance) { } } + // ak nastavujeme dimming z platformy, zapiseme ho do nodes.table. Ak sa spusta flow, a node nema profil, nacitame dimming z nodes.table + if(params.info == "setNodeDimming") + { + console.log('++++++eeeeeeeee',params) + console.log('*********aaaa',params.address, params.byte4); + updateNodeDimming(params.address, params.byte4); + } + //parse read response let values = {}; if(params.rw == 0) { values = processResponse(register, dataBytes);//read + // console.log('-------****', values); } + if(params.rw == 1) - { //write command + { + //write command //set command dimming - if(params.register == 1) values = {"comm_status": message}; + + if(params.register == 1) + { + values = {"comm_status": message}; + } + } + if(params.info == 'Dimming for CCT') + { + //Now we have node dimming value. We use it to set cct with following task (dimming value is byte 4): + let setCCT = getParams(priorityTypes.high_priority); + setCCT.type = "cmd"; + setCCT.tbname = tbname; + setCCT.address = params.address; + setCCT.register = 1; //dimming + setCCT.recipient = 1; //slave + setCCT.byte1 = 1; + setCCT.byte2 = params.cct.byte2; + setCCT.byte3 = params.cct.byte3; + setCCT.byte4 = dataBytes[3] //dimming value + 128; + // setCCT.cct = params.cct.value; // we have cct value from platform call. + setCCT.rw = 1;//write + setCCT.timestamp = priorityTypes.high_priority; + setCCT.info = 'SetCCT'; + setCCT.debug = true; + + tasks.push(setCCT); + return; + } + + if(params.info == 'dimming_no_mov') + { + //Now we have 4 bytes from 42 register. We leave first 3 bytes, and overwrite byte 4 with value from platform: + let setDNM = getParams(priorityTypes.high_priority); + setDNM.type = "cmd"; + setDNM.tbname = tbname; + setDNM.address = params.address; + setDNM.register = 42; //dimming + setDNM.recipient = 1; //slave + setDNM.byte1 = 1; + setDNM.byte2 = dataBytes[1]; + setDNM.byte3 = dataBytes[2]; + setDNM.byte4 = params.value + 128; + setDNM.rw = 1;//write + setDNM.timestamp = priorityTypes.high_priority; + setDNM.info = 'setDNM'; + setDNM.debug = true; + + tasks.push(setDNM); + return; + } + + if(params.info == "cct_no_mov") + { + //Now we have 4 bytes from register 42. We leave byte 0 and byte 4, and overwrite byte 2 and 3 with values from platform: + let setCCT = getParams(priorityTypes.high_priority); + setCCT.type = "cmd"; + setCCT.tbname = tbname; + setCCT.address = params.address; + setCCT.register = 42; //dimming + setCCT.recipient = 1; //slave + setCCT.byte1 = 1; + setCCT.byte2 = params.cct.byte2; + setCCT.byte3 = params.cct.byte3; + setCCT.byte4 = dataBytes[3]; // should be byte4, that we got from register 42 + setCCT.rw = 1;//write + setCCT.timestamp = priorityTypes.high_priority; + setCCT.info = 'setCCT'; + setCCT.debug = true; + + tasks.push(setCCT); + return; + } + + if(params.register == 0) values["status"] = message; //fw version - register == 4 @@ -2654,7 +2825,9 @@ exports.install = function(instance) { delete cmdCounter[params.address]; let tbname = nodesData[ params.address ].tbname; - logger.debug( "profil nebol úspešne odoslaný na node č. ", params, result, resp); + + //! LOG SPRACOVANIA PROFILU + //logger.debug( "profil nebol úspešne odoslaný na node č. ", params, result, resp); //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "profil nebol úspešne odoslaný na node č. " + params.address, "", instanceSendTo.tb, instance, null ); sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", instanceSendTo.tb, instance ); @@ -2855,24 +3028,24 @@ exports.install = function(instance) { } //! 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 + // 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 = "ttymxc4"; if(FLOW.OMS_serial_port == undefined) FLOW.OMS_serial_port = "ttymxc4"; if(FLOW.OMS_serial_port.length === 1) FLOW.OMS_serial_port = "ttymxc4"; - const rsPort = new SerialPort(`/dev/${FLOW.OMS_serial_port}`, { autoOpen: false }); + 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() { + rsPort.on('open', async function() { logger.debug("CMD manager - rsPort opened sucess"); await 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) { + 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(instanceSendTo.debug, "RPC runSyncExec - Promise Resolved:" + status); logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); @@ -2909,554 +3082,712 @@ exports.install = function(instance) { }); }); - }).catch(function (reason) { - instance.send(instanceSendTo.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); - }); + }).catch(function (reason) { + instance.send(instanceSendTo.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); + }); - }); + }); - rsPort.on('error', function(err) { + 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(instanceSendTo.debug, err.message); + instance.send(instanceSendTo.debug, err.message); }); - rsPort.on("close", () => { - rsPort.close(); - }); + rsPort.on("close", () => { + rsPort.close(); + }); //loadRelaysData(); rsPort.open(); - instance.on("close", () => { + instance.on("close", () => { clearInterval(interval); - rsPort.close(); - }); + rsPort.close(); + }); + + + // list of available node commands from platform + const rpcNodeCommands = ["dimming", "dimming_no_mov", "cct", "cct_no_mov", "timeout"]; + + //finds node, whose value needs to be modified from rpc platform call + function findNode(tbname) + { + let keys = Object.keys(nodesData); + let node = null; + + for(let i = 0; i < keys.length; i++) + { + node = keys[i]; + if(tbname == nodesData[node].tbname.trim()) return node; + } + + return null; + } //onData instance.on("data", async function(flowdata) { - //instance.on("data", (data) => { + //instance.on("data", (data) => { - //instance.send(instanceSendTo.debug, "on Data"); - //instance.send(instanceSendTo.debug, flowdata); - - //logger.debug(flowdata.data); + //instance.send(instanceSendTo.debug, "on Data"); + //instance.send(instanceSendTo.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 = []; + //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) + if(tasks.length == 0) + { + + buildTasks(); + + if(rsPort.isOpen) { - - buildTasks(); - - if(rsPort.isOpen) - { - interval = setInterval(runTasks, 100); - } - else - { - instance.send(instanceSendTo.debug, "port is not opened!!!"); - } + interval = setInterval(runTasks, 100); + } + else + { + instance.send(instanceSendTo.debug, "port is not opened!!!"); } } - else + } + else + { + //terminal data - object + //logger.debug("flowdata", flowdata.data); + + if(typeof flowdata.data === 'object') { - //terminal data - object - //logger.debug("flowdata", flowdata.data); - - if(typeof flowdata.data === 'object') + //logger.debug("dido", flowdata.data); + if(flowdata.data.hasOwnProperty("sender")) { - //logger.debug("dido", flowdata.data); - if(flowdata.data.hasOwnProperty("sender")) + //data from di_do_controller + if(flowdata.data.sender == "di_do_controller") { - //data from dido_controller - if(flowdata.data.sender == "dido_controller") + + if(flowdata.data.hasOwnProperty("cmd")) { + let cmd = flowdata.data.cmd; - if(flowdata.data.hasOwnProperty("cmd")) + + if(cmd == "buildTasks") { - let cmd = flowdata.data.cmd; + clearInterval(interval); + logger.debug("-->CMD MANAGER - BUILD TASKS"); + buildTasks(); + + //logger.debug("tasks:"); + //logger.debug(tasks); + + logger.debug("-->CMD MANAGER - RUN TASKS"); + interval = setInterval(runTasks, longInterval); + } + else if(cmd == "reload_relays") + { + await loadRelaysData(flowdata.data.line); + + if(flowdata.data.dataChanged) + { + if(!flowdata.data.value) + { + reportOfflineNodeStatus(flowdata.data.line); + } + else + { + reportOnlineNodeStatus(flowdata.data.line); + } + } - if(cmd == "buildTasks") + } + else if(cmd == "rotary_switch_state") + { + //state was changed + if(rotary_switch_state != flowdata.data.value) { - clearInterval(interval); - - logger.debug("-->CMD MANAGER - BUILD TASKS"); - buildTasks(); - - //logger.debug("tasks:"); - //logger.debug(tasks); - - logger.debug("-->CMD MANAGER - RUN TASKS"); - interval = setInterval(runTasks, longInterval); - } - else if(cmd == "reload_relays") - { - await loadRelaysData(flowdata.data.line); - - if(flowdata.data.dataChanged) + if(rotary_switch_state == "Off") { - if(!flowdata.data.value) - { - reportOfflineNodeStatus(flowdata.data.line); - } - else - { - reportOnlineNodeStatus(flowdata.data.line); - } + //vyreportovat vsetky svietdla + reportOfflineNodeStatus(); } - + else reportOnlineNodeStatus(); + } - else if(cmd == "rotary_switch_state") + + rotary_switch_state = flowdata.data.value; + } + else if(cmd == "lux_sensor") + { + lux_sensor = parseInt(flowdata.data.value); + + //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"; + let weight = ERRWEIGHT.NOTICE; + let message = `zapnutý istič línie č. ${line}`; + if(value == "Off") { - //state was changed - if(rotary_switch_state != flowdata.data.value) + weight = ERRWEIGHT.ERROR; + message = `vypnutý istič línie č. ${line}`; + status = "NOK"; + } + + if(dataChanged) { + + if(relaysData.hasOwnProperty(line)) { - if(rotary_switch_state == "Off") - { - //vyreportovat vsetky svietdla - reportOfflineNodeStatus(); - } - else reportOnlineNodeStatus(); + let tbname = relaysData[line].tbname; - } + if(value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", {line: line}, "", instanceSendTo.tb, instance, "circuit_breaker"); + else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", instanceSendTo.tb, instance, "circuit_breaker"); - rotary_switch_state = flowdata.data.value; - } - else if(cmd == "lux_sensor") - { - lux_sensor = parseInt(flowdata.data.value); - - //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"; - let weight = ERRWEIGHT.NOTICE; - let message = `zapnutý istič línie č. ${line}`; - if(value == "Off") - { - weight = ERRWEIGHT.ERROR; - message = `vypnutý istič línie č. ${line}`; - status = "NOK"; - } - - if(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}, "", instanceSendTo.tb, instance, "circuit_breaker"); - else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", instanceSendTo.tb, instance, "circuit_breaker"); - - //report status liniu - let values = { - "status": status - }; - - let dataToTb = { - [tbname]: [ - { - "ts": Date.now(), - "values": values - } - ] - } - - //instance.send(instanceSendTo.tb, dataToTb); - tbHandler.sendToTb(dataToTb, instance); - - //current value - if(value == "Off") - { - //vyreportovat vsetky svietdla na linii - reportOfflineNodeStatus(line); + //report status liniu + let values = { + "status": status + }; + + let dataToTb = { + [tbname]: [ + { + "ts": Date.now(), + "values": values + } + ] } - else reportOnlineNodeStatus(line); - } + + //instance.send(instanceSendTo.tb, dataToTb); + tbHandler.sendToTb(dataToTb, instance); + //current value + if(value == "Off") + { + //vyreportovat vsetky svietdla na linii + reportOfflineNodeStatus(line); + } + else reportOnlineNodeStatus(line); } + } - else{ - logger.debug("undefined cmd", cmd); - } + } + else{ + logger.debug("undefined cmd", cmd); } } - - return; } - //data from worksys - if(flowdata.data.hasOwnProperty("topic")) + 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(instanceSendTo.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") + { - - 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(instanceSendTo.debug, flowdata.data); - logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); - logger.debug("----------------------------"); - - if(entity_type == "street_luminaire") + if(method == "set_command") { - if(method == "set_command") + + //let command = data.params.command; + let value = data.params.payload.value; + + //we find node to set new value to from nodes.table + let node = null; + + if(rpcNodeCommands.includes(command)) { - - //let command = data.params.command; - let value = data.params.payload.value; - - if(command == "dimming") + node = findNode(tbname); + if(!node) { - - 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(priorityTypes.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 = priorityTypes.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 priorityTypes.high_priority - //a pridame aj vyreportovanie dimmingu - { - let params = getParams(priorityTypes.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 = priorityTypes.high_priority; - params.info = 'read dimming (after set dimming from platform)'; - params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - vykon - { - let params = getParams(priorityTypes.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 76; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = priorityTypes.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(priorityTypes.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 75; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = priorityTypes.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(priorityTypes.high_priority); - - params.type = "cmd"; - params.tbname = tbname; - params.address = node; - params.register = 77; - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = priorityTypes.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(instanceSendTo.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(instanceSendTo.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, ERRWEIGHT.INFO, `profil úspešne poslaný z platformy na RVO pre node č. ${node}`, profile, instanceSendTo.tb, instance, null ); - sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, instanceSendTo.tb, instance ); - - nodesData[node].processed = false; - nodesData[node].profile = profile; - - let line = nodesData[node].line; - processNodeProfile(node); - - }); - }); - } + logger.debug(`rpc set command - ${command} : unable to find tbname: ${tbname}`); + return; } } else { - - instance.send(instanceSendTo.debug, "unknown method " + method); - logger.debug("unknown method", method); - + instance.send(instanceSendTo.debug, "undefined command " + command); + logger.debug("undefined command", command); 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") + if(command == "dimming") { - logger.debug("-->set_profile for line", data.params); - logger.debug("profile data:", profile); + let params = getParams(priorityTypes.high_priority); - let keys = Object.keys(relaysData); - for(let i = 0; i < keys.length; i++) - { - let line = keys[i]; - if(tbname == relaysData[line].tbname) + 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 = priorityTypes.high_priority; + params.info = 'setNodeDimming'; + //params.debug = true; + + //ak linia je + + //debug(params); + logger.debug("dimming", params); + + tasks.push(params); + + updateNodeDimming(node, value); + + setTimeout(function() { + + //spustime o 4 sekundy neskor, s prioritou priorityTypes.high_priority + //a pridame aj vyreportovanie dimmingu { - //zmazeme tasky - removeTask({type: "relay", line: line}); - - if(profile != "") profile = JSON.stringify(profile); - dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) { + let params = getParams(priorityTypes.high_priority); - builder.callback(function(err, response) { + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = priorityTypes.high_priority; + params.info = 'read dimming (after set dimming from platform)'; + params.debug = true; - //update profile - logger.debug("worksys - update relay profile done:", profile); - instance.send(instanceSendTo.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, instanceSendTo.tb, instance ); - - }); - }); - - break; + tasks.push(params); } - } + + //pridame aj vyreportovanie - vykon + { + let params = getParams(priorityTypes.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 76; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = priorityTypes.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(priorityTypes.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 75; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = priorityTypes.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(priorityTypes.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 77; + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = priorityTypes.high_priority; + params.info = 'read power factor - Cos phi (after set dimming from platform)'; + params.debug = true; + + tasks.push(params); + } + + },5000); + } - else if(method == "set_command") + else if(command === "cct") { - let value = data.params.payload.value; + + let params = getParams(priorityTypes.high_priority); - if(command === "switch") - { + value = parseInt(value); + byte2 = Math.floor(value/256); + byte3 = value - (byte2 * 256); - // 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; + // To set cct, we need to get node dimming first. We send this command. After we get dimming + // value from rsPort (in writeData function), we send command to set cct. Byte2 and 3 are cct + // byte4 is dimming (for example 100 or 50) + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1; //dimming + params.recipient = 1; //slave + params.rw = 0; //read + params.timestamp = priorityTypes.high_priority; + params.info = 'Dimming for CCT'; + params.cct = {byte2: byte2, byte3: byte3, value: value}; + params.debug = true; - let responseRelays = await promisifyBuilder(dbRelays.find().where("tbname", tbname)); + console.log('-------- cct queued in tasks'); - 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"); - } + tasks.push(params); + } + else if(command == "timeout") + { + let params = getParams(priorityTypes.high_priority); + + value = parseInt(value); + byte3 = Math.floor(value/256); + byte4 = value - (byte3 * 256); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 104; //timeout + params.recipient = 1; //slave + params.rw = 1;//write + params.byte3 = byte3; + params.byte4 = byte4; + params.value = value; + params.timestamp = priorityTypes.high_priority; + params.info = 'SetNodeTimeout'; + params.debug = true; + + console.log('-------- SetNodeTimeout queued in tasks'); + + tasks.push(params); + + setTimeout(function() { + + // vyreportujeme nodeTimeoutMovement na platformu + let params = getParams(priorityTypes.high_priority); + + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 104;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = priorityTypes.high_priority; + params.info = 'ReadNodeTimeout'; + params.debug = false; + + tasks.push(params); + + },5000); } - else + else if(command == "dimming_no_mov") { - instance.send(instanceSendTo.debug, "undefined method " + method); - logger.debug("undefined method", method); + let params = getParams(priorityTypes.high_priority); + + value = parseInt(value); + + // To set dimming_no_mov, we need to get node cct_no_mov first. We send this command. After we get cct + // value from rsPort (in writeData function), we send command to set dimming_no_mov. Byte2 and 3 are cct + // byte4 is dimming from platform (for example 100 or 50) + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 42; //dimming + params.recipient = 1; //slave + params.rw = 0; //read + params.timestamp = priorityTypes.high_priority; + params.info = 'dimming_no_mov'; + params.value = value; + params.debug = true; + + console.log('-------- dimming_no_mov queued in tasks'); + + tasks.push(params); + } + else if(command == "cct_no_mov") + { + let params = getParams(priorityTypes.high_priority); + + value = parseInt(value); + byte2 = Math.floor(value/256); + byte3 = value - (byte2 * 256); + + // To set cct_no_mov, we need to get node dimming_no_mov first. We send this command. After we get dimming + // value from rsPort (in writeData function), we send command to set cct_no_mov. Byte2 and 3 are cct from platform + // byte4 is dimming (for example 100 or 50) + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 42; //dimming_no_mov + params.recipient = 1; //slave + params.rw = 0; //read + params.timestamp = priorityTypes.high_priority; + params.info = "cct_no_mov"; + params.cct = {byte2: byte2, byte3: byte3}; + params.debug = true; + + console.log('-------- cct_no_mov queued in tasks'); + + tasks.push(params); } return; } - else{ - instance.send(instanceSendTo.debug, "UNKNOW entity_type " + entity_type); - logger.debug("UNKNOW entity_type", entity_type); + else if(method == "set_profile") + { + //nastav profil nodu + logger.debug("-->set_profile for node", data.params); + logger.debug("------profile data", profile); + //instance.send(instanceSendTo.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 == "") + { + // ak nie je profile, nastavime dimming svietidla podla nodes.table + let params = getParams(priorityTypes.high_priority); + params.type = "cmd"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.byte4 = nodesData[node].dimming; + params.rw = 1;//write + params.timestamp = priorityTypes.high_priority; + params.info = 'setNodeDimming'; + //params.debug = true; + logger.debug("dimming", params); + tasks.push(params); + + } + else + { + 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, ERRWEIGHT.INFO, `profil úspešne poslaný z platformy na RVO pre node č. ${node}`, profile, instanceSendTo.tb, instance, null ); + sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, instanceSendTo.tb, instance ); + + nodesData[node].processed = false; + nodesData[node].profile = profile; + + let line = nodesData[node].line; + processNodeProfile(node); + + }); + }); + } + } + } + else + { + + instance.send(instanceSendTo.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") + { + //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(instanceSendTo.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, instanceSendTo.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(instanceSendTo.debug, "undefined method " + method); + logger.debug("undefined method", method); } return; + + } + else{ + instance.send(instanceSendTo.debug, "UNKNOW entity_type " + entity_type); + logger.debug("UNKNOW entity_type", entity_type); } - //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 = priorityTypes.terminal; - params.type = "cmd-terminal"; - params.tbname = ""; - params.timestamp = priorityTypes.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); - + return; } - } - }) -} // end of instance.export + //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 = priorityTypes.terminal; + params.type = "cmd-terminal"; + params.tbname = ""; + params.timestamp = priorityTypes.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 exports.install = function(instance) + /** @@ -3585,6 +3916,7 @@ setCorrectPlcTimeOnceADay(); + ///helper functions function calculateDuskDown(date, line, duskOffset = 0, dawnOffset = 0) @@ -3696,22 +4028,33 @@ function processResponse(register, bytes) 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; + + let cct; + if(byte3 == 1) cct = (byte2 * 256) + byte1; + //else cct = bytesToInt(bytes.slice(0, 3)); + + if(cct) values["cct"] = cct; return values; } + //Dimming_no_mov + if(register == 42) + { + values["dimming_no_mov"] = byte0 - 128; + + let cct; + if(byte3 == 1) cct = (byte2 * 256) + byte1; + + if(cct) values["cct_no_mov"] = cct; + + return values; + } + + // if(register == 4) { @@ -3905,6 +4248,17 @@ function processResponse(register, bytes) values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; } + if(register == 95) + { + values["lux_level"] = (byte1 * 256) + byte0; + } + + if(register == 104) + { + values["movement_timeout"] = (byte1 * 256) + byte0; + values["movement_state"] = byte2; + } + return values; } @@ -3981,3 +4335,76 @@ function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4 + + + +// Node profile rpc +// { +// "topic": "v1/gateway/rpc", +// "content": { +// "device": "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", +// "data": { +// "id": 53, +// "method": "set_profile", +// "params": { +// "entities": [ +// { +// "entity_type": "street_luminaire", +// "tb_name": "LpkVlmq4b3jMwJQxBZ8akayrXAP6o97Ke0aOYEg2" +// } +// ], +// "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 +// } +// } +// } +// } +// } diff --git a/flow/designer.json b/flow/designer.json index 9fe9d06..ffdca36 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -20,6 +20,61 @@ } ], "components": [ + { + "id": "1611938185451", + "component": "modbus_citysys", + "tab": "1611921777196", + "name": "Modbus_citysys", + "x": 101.5, + "y": 151.5, + "connections": { + "0": [ + { + "index": "0", + "id": "1611951142547" + } + ], + "1": [ + { + "index": "0", + "id": "1611938192035" + } + ], + "2": [ + { + "index": "0", + "id": "1612772119611" + }, + { + "index": "0", + "id": "1611938192035" + } + ], + "3": [ + { + "index": "0", + "id": "1621340721628" + }, + { + "index": "0", + "id": "1684179110403" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Running", + "color": "green" + }, + "options": { + "edge": "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV" + }, + "color": "#2134B0", + "notes": "" + }, { "id": "1611938192035", "component": "debug", @@ -72,7 +127,7 @@ "id": "1612772119611", "component": "virtualwireout", "tab": "1611921777196", - "name": "tb-push", + "name": "tb-demo-push", "x": 399.75, "y": 211.5, "connections": {}, @@ -81,11 +136,11 @@ "output": [] }, "state": { - "text": "tb-push", + "text": "tb-demo-push", "color": "gray" }, "options": { - "wirename": "tb-push" + "wirename": "tb-demo-push" }, "color": "#303E4D", "notes": "" @@ -96,7 +151,7 @@ "tab": "1612772287426", "name": "WS MQTT publish", "x": 311.75, - "y": 268, + "y": 128, "connections": { "0": [ { @@ -146,9 +201,9 @@ "id": "1612778461252", "component": "virtualwirein", "tab": "1612772287426", - "name": "tb-push", + "name": "tb-demo-push", "x": 68.75, - "y": 289, + "y": 149, "connections": { "0": [ { @@ -166,11 +221,11 @@ "output": [] }, "state": { - "text": "tb-push", + "text": "tb-demo-push", "color": "gray" }, "options": { - "wirename": "tb-push" + "wirename": "tb-demo-push" }, "color": "#303E4D", "notes": "" @@ -181,7 +236,7 @@ "tab": "1612772287426", "name": "to TB", "x": 317.75, - "y": 174, + "y": 34, "connections": {}, "disabledio": { "input": [ @@ -207,7 +262,7 @@ "tab": "1612772287426", "name": "In case broker is not ready, we save data to database, and when connected, we resend data", "x": 72.88333129882812, - "y": 32, + "y": -108, "connections": {}, "disabledio": { "input": [], @@ -227,12 +282,10 @@ "tab": "1612772287426", "name": "errors from MQTT Broker", "x": 610, - "y": 131, + "y": -9, "connections": {}, "disabledio": { - "input": [ - 0 - ], + "input": [], "output": [] }, "state": { @@ -252,8 +305,8 @@ "component": "debug", "tab": "1615551125555", "name": "Debug", - "x": 763, - "y": 123, + "x": 755, + "y": 19, "connections": {}, "disabledio": { "input": [ @@ -277,20 +330,20 @@ "id": "1615566865233", "component": "virtualwireout", "tab": "1615551125555", - "name": "tb-push", - "x": 761, - "y": 216, + "name": "tb-demo-push", + "x": 753, + "y": 112, "connections": {}, "disabledio": { "input": [], "output": [] }, "state": { - "text": "tb-push", + "text": "tb-demo-push", "color": "gray" }, "options": { - "wirename": "tb-push" + "wirename": "tb-demo-push" }, "color": "#303E4D", "notes": "" @@ -300,8 +353,8 @@ "component": "debug", "tab": "1615551125555", "name": "CMD_debug", - "x": 758, - "y": 301, + "x": 750, + "y": 197, "connections": {}, "disabledio": { "input": [ @@ -377,7 +430,7 @@ "id": "1615809595184", "component": "virtualwireout", "tab": "1611921777196", - "name": "tb-push", + "name": "tb-demo-push", "x": 404.8833312988281, "y": 653.25, "connections": {}, @@ -386,22 +439,65 @@ "output": [] }, "state": { - "text": "tb-push", + "text": "tb-demo-push", "color": "gray" }, "options": { - "wirename": "tb-push" + "wirename": "tb-demo-push" }, "color": "#303E4D", "notes": "" }, + { + "id": "1615809105191", + "component": "gettemperature", + "tab": "1611921777196", + "name": "Get RVO temperature", + "x": 98.88333129882812, + "y": 457.3500061035156, + "connections": { + "0": [ + { + "index": "0", + "id": "1615802995322" + } + ], + "1": [ + { + "index": "0", + "id": "1615809128443" + }, + { + "index": "0", + "id": "1615809595184" + } + ], + "2": [ + { + "index": "0", + "id": "1621340721628" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#5CB36D", + "notes": "" + }, { "id": "1616165795916", "component": "httproute", "tab": "1615551125555", "name": "POST /terminal", - "x": 110, - "y": 508, + "x": 72, + "y": 350, "connections": { "0": [ { @@ -446,15 +542,15 @@ "component": "httpresponse", "tab": "1615551125555", "name": "HTTP Response", - "x": 759, - "y": 377, + "x": 751, + "y": 273, "connections": {}, "disabledio": { "input": [], "output": [] }, "state": { - "text": "", + "text": "1.17 sec.", "color": "gray" }, "options": { @@ -468,8 +564,8 @@ "component": "debug", "tab": "1615551125555", "name": "DIDO_Debug", - "x": 743, - "y": 839, + "x": 739, + "y": 635, "connections": {}, "disabledio": { "input": [ @@ -495,12 +591,12 @@ "tab": "1615551125555", "name": "turnOff line", "x": 75, - "y": 1033, + "y": 596, "connections": { "0": [ { - "index": "1", - "id": "1699963668903" + "index": "0", + "id": "1618232536546" } ] }, @@ -523,20 +619,20 @@ "id": "1617115013095", "component": "virtualwireout", "tab": "1615551125555", - "name": "tb-push", - "x": 751, - "y": 940, + "name": "tb-demo-push", + "x": 744, + "y": 733, "connections": {}, "disabledio": { "input": [], "output": [] }, "state": { - "text": "tb-push", + "text": "tb-demo-push", "color": "gray" }, "options": { - "wirename": "tb-push" + "wirename": "tb-demo-push" }, "color": "#303E4D", "notes": "" @@ -546,8 +642,8 @@ "component": "debug", "tab": "1615551125555", "name": "Debug", - "x": 628, - "y": 1246, + "x": 605, + "y": 995, "connections": {}, "disabledio": { "input": [], @@ -570,8 +666,8 @@ "component": "trigger", "tab": "1615551125555", "name": "start import", - "x": 258, - "y": 1254, + "x": 232, + "y": 995, "connections": { "0": [ { @@ -600,8 +696,8 @@ "component": "csv_import", "tab": "1615551125555", "name": "CsvImport", - "x": 437, - "y": 1235, + "x": 408, + "y": 987, "connections": { "0": [ { @@ -629,8 +725,8 @@ "component": "comment", "tab": "1615551125555", "name": "import data from csv", - "x": 424, - "y": 1168, + "x": 398, + "y": 920, "connections": {}, "disabledio": { "input": [], @@ -649,8 +745,8 @@ "component": "trigger", "tab": "1615551125555", "name": "update profile / node", - "x": 119, - "y": 130, + "x": 80, + "y": 13, "connections": { "0": [ { @@ -674,13 +770,58 @@ "color": "#F6BB42", "notes": "" }, + { + "id": "1618232536546", + "component": "di_do_controller", + "tab": "1615551125555", + "name": "DI_DO_Controller", + "x": 415, + "y": 723, + "connections": { + "0": [ + { + "index": "0", + "id": "1617104731852" + } + ], + "1": [ + { + "index": "0", + "id": "1617115013095" + }, + { + "index": "0", + "id": "1617104731852" + } + ], + "2": [ + { + "index": "0", + "id": "1618393583970" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": { + "edge": "R3JjOWdylwgNLzxVab7NEBkZ2vG64rq8PEB5QmDo" + }, + "color": "#2134B0", + "notes": "" + }, { "id": "1618235171399", "component": "trigger", "tab": "1615551125555", - "name": "run tasks", - "x": 122, - "y": 206, + "name": "tun tasks", + "x": 77, + "y": 84, "connections": { "0": [ { @@ -709,7 +850,7 @@ "tab": "1612772287426", "name": "wsmqtt-exit1", "x": 610.8833312988281, - "y": 219, + "y": 79, "connections": {}, "disabledio": { "input": [], @@ -733,7 +874,7 @@ "tab": "1612772287426", "name": "wsmqtt-exit2", "x": 611.8833312988281, - "y": 394, + "y": 254, "connections": {}, "disabledio": { "input": [ @@ -758,8 +899,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "to-cmd-manager", - "x": 744.8833312988281, - "y": 1032, + "x": 746.8833312988281, + "y": 824, "connections": {}, "disabledio": { "input": [], @@ -780,8 +921,8 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "platform-rpc-call", - "x": 115.88333129882812, - "y": 316, + "x": 77.88333129882812, + "y": 173, "connections": { "0": [ { @@ -808,18 +949,18 @@ "id": "1618393759854", "component": "virtualwirein", "tab": "1615551125555", - "name": "cmd_to_dido", - "x": 78.88333129882812, - "y": 864, + "name": "di_do_controller-in", + "x": 75.88333129882812, + "y": 763, "connections": { "0": [ { "index": "0", - "id": "1683664161036" + "id": "1618232536546" }, { - "index": "1", - "id": "1699963668903" + "index": "0", + "id": "1683664161036" } ] }, @@ -828,11 +969,11 @@ "output": [] }, "state": { - "text": "cmd_to_dido", + "text": "di_do_controller-in", "color": "gray" }, "options": { - "wirename": "cmd_to_dido" + "wirename": "di_do_controller-in" }, "color": "#303E4D", "notes": "" @@ -841,20 +982,20 @@ "id": "1618393827655", "component": "virtualwireout", "tab": "1615551125555", - "name": "cmd_to_dido", - "x": 756.8833312988281, - "y": 477, + "name": "di_do_controller-in", + "x": 748.8833312988281, + "y": 373, "connections": {}, "disabledio": { "input": [], "output": [] }, "state": { - "text": "cmd_to_dido", + "text": "di_do_controller-in", "color": "gray" }, "options": { - "wirename": "cmd_to_dido" + "wirename": "di_do_controller-in" }, "color": "#303E4D", "notes": "" @@ -865,7 +1006,7 @@ "tab": "1612772287426", "name": "platform-rpc-call", "x": 611.8833312988281, - "y": 307, + "y": 167, "connections": {}, "disabledio": { "input": [], @@ -886,13 +1027,13 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOn line", - "x": 76, - "y": 947, + "x": 77, + "y": 678, "connections": { "0": [ { - "index": "1", - "id": "1699963668903" + "index": "0", + "id": "1618232536546" } ] }, @@ -906,7 +1047,7 @@ }, "options": { "datatype": "object", - "data": "{line: 1, command: \"turnOn\", force: true}" + "data": "{line: 3, command: \"turnOn\", force: true}" }, "color": "#F6BB42", "notes": "" @@ -916,8 +1057,8 @@ "component": "cmd_manager", "tab": "1615551125555", "name": "CMD Manager", - "x": 442, - "y": 290, + "x": 420, + "y": 156, "connections": { "0": [ { @@ -971,8 +1112,8 @@ "component": "httproute", "tab": "1615551125555", "name": "GET db", - "x": 104, - "y": 619, + "x": 73, + "y": 455, "connections": { "0": [ { @@ -1016,13 +1157,13 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOnAlarm", - "x": 72, - "y": 1106, + "x": 73, + "y": 833, "connections": { "0": [ { - "index": "1", - "id": "1699963668903" + "index": "0", + "id": "1618232536546" } ] }, @@ -1046,13 +1187,13 @@ "component": "trigger", "tab": "1615551125555", "name": "turnOffAlarm", - "x": 71, - "y": 1179, + "x": 74, + "y": 909, "connections": { "0": [ { - "index": "1", - "id": "1699963668903" + "index": "0", + "id": "1618232536546" } ] }, @@ -1075,7 +1216,7 @@ "id": "1621340721628", "component": "virtualwireout", "tab": "1611921777196", - "name": "modbus_to_dido", + "name": "di_do_controller-in", "x": 396, "y": 390, "connections": {}, @@ -1084,11 +1225,11 @@ "output": [] }, "state": { - "text": "modbus_to_dido", + "text": "di_do_controller-in", "color": "gray" }, "options": { - "wirename": "modbus_to_dido" + "wirename": "di_do_controller-in" }, "color": "#303E4D", "notes": "" @@ -1175,20 +1316,103 @@ "output": [] }, "state": { - "text": "", + "text": "0 sec.", "color": "gray" }, "options": {}, "color": "#5D9CEC", "notes": "" }, + { + "id": "1631174769920", + "component": "debug", + "tab": "1611921777196", + "name": "Debug", + "x": 600.8833312988281, + "y": 750.2999877929688, + "connections": {}, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "Enabled", + "color": "gray" + }, + "options": { + "type": "data", + "repository": false, + "enabled": true + }, + "color": "#967ADC", + "notes": "" + }, + { + "id": "1631174856021", + "component": "httprequest", + "tab": "1611921777196", + "name": "HTTP Request", + "reference": "192.168.252.2:8004", + "x": 249.88333129882812, + "y": 751.2999877929688, + "connections": { + "0": [ + { + "index": "0", + "id": "1631174769920" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": { + "stringify": "json", + "url": "http://192.168.252.2:9999/gettime", + "method": "GET" + }, + "color": "#5D9CEC", + "notes": "" + }, + { + "id": "1631174946263", + "component": "trigger", + "tab": "1611921777196", + "name": "Trigger", + "x": 96.88333129882812, + "y": 754.2999877929688, + "connections": { + "0": [ + { + "index": "0", + "id": "1631174856021" + } + ] + }, + "disabledio": { + "input": [], + "output": [] + }, + "state": { + "text": "", + "color": "gray" + }, + "options": {}, + "color": "#F6BB42", + "notes": "" + }, { "id": "1634303504177", "component": "monitormemory", "tab": "1612772287426", "name": "RAM", "x": 71.88333129882812, - "y": 629.5, + "y": 489.5, "connections": { "0": [ { @@ -1202,7 +1426,7 @@ "output": [] }, "state": { - "text": "826.22 MB / 985.68 MB", + "text": "788.61 MB / 987.80 MB", "color": "gray" }, "options": { @@ -1218,7 +1442,7 @@ "tab": "1612772287426", "name": "disk", "x": 72.88333129882812, - "y": 726.5, + "y": 586.5, "connections": { "0": [ { @@ -1232,7 +1456,7 @@ "output": [] }, "state": { - "text": "5.85 GB / 7.26 GB", + "text": "5.92 GB / 7.26 GB", "color": "gray" }, "options": { @@ -1249,7 +1473,7 @@ "tab": "1612772287426", "name": "send-to-services", "x": 5.883331298828125, - "y": 1089.5, + "y": 949.5, "connections": { "0": [ { @@ -1282,7 +1506,7 @@ "tab": "1612772287426", "name": "send-to-services", "x": 428.8833312988281, - "y": 622.5, + "y": 482.5, "connections": {}, "disabledio": { "input": [], @@ -1304,7 +1528,7 @@ "tab": "1612772287426", "name": "send-to-services", "x": 612.8833312988281, - "y": 482.5, + "y": 342.5, "connections": {}, "disabledio": { "input": [], @@ -1327,7 +1551,7 @@ "name": "http://192.168.252.2:8004/sentmessage", "reference": "", "x": 439.8833312988281, - "y": 1096.7333374023438, + "y": 956.7333374023438, "connections": { "0": [ { @@ -1358,7 +1582,7 @@ "tab": "1612772287426", "name": "Debug", "x": 234.75, - "y": 1044, + "y": 904, "connections": {}, "disabledio": { "input": [ @@ -1384,7 +1608,7 @@ "tab": "1612772287426", "name": "Code", "x": 255, - "y": 532, + "y": 392, "connections": { "0": [ { @@ -1419,7 +1643,7 @@ "tab": "1612772287426", "name": "Debug", "x": 430, - "y": 528, + "y": 388, "connections": {}, "disabledio": { "input": [ @@ -1445,7 +1669,7 @@ "tab": "1612772287426", "name": "Code", "x": 244, - "y": 628, + "y": 488, "connections": { "0": [ { @@ -1480,7 +1704,7 @@ "tab": "1612772287426", "name": "Debug", "x": 431, - "y": 720, + "y": 580, "connections": {}, "disabledio": { "input": [ @@ -1506,7 +1730,7 @@ "tab": "1612772287426", "name": "Code", "x": 247, - "y": 722, + "y": 582, "connections": { "0": [ { @@ -1541,7 +1765,7 @@ "tab": "1612772287426", "name": "Debug", "x": 434, - "y": 812, + "y": 672, "connections": {}, "disabledio": { "input": [ @@ -1567,7 +1791,7 @@ "tab": "1612772287426", "name": "Send info", "x": 438, - "y": 1205, + "y": 1065, "connections": {}, "disabledio": { "input": [ @@ -1593,7 +1817,7 @@ "tab": "1612772287426", "name": "Info sender", "x": 233, - "y": 1142, + "y": 1002, "connections": { "0": [ { @@ -1626,7 +1850,7 @@ "tab": "1612772287426", "name": "Debug", "x": 811.8833312988281, - "y": 1090.5, + "y": 950.5, "connections": {}, "disabledio": { "input": [ @@ -1651,8 +1875,8 @@ "component": "virtualwireout", "tab": "1615551125555", "name": "send-to-services", - "x": 756, - "y": 568, + "x": 748, + "y": 464, "connections": {}, "disabledio": { "input": [], @@ -1674,7 +1898,7 @@ "tab": "1612772287426", "name": "CPU", "x": 71, - "y": 535, + "y": 395, "connections": { "0": [ { @@ -1688,7 +1912,7 @@ "output": [] }, "state": { - "text": "0.9% / 86.46 MB", + "text": "1% / 84.20 MB", "color": "gray" }, "options": { @@ -1706,9 +1930,9 @@ "id": "1683664161036", "component": "debug", "tab": "1615551125555", - "name": "CMDtoDIDO", - "x": 417, - "y": 858, + "name": "toDIDO", + "x": 416, + "y": 646, "connections": {}, "disabledio": { "input": [ @@ -1733,8 +1957,8 @@ "component": "virtualwirein", "tab": "1615551125555", "name": "from-dido-controller", - "x": 113, - "y": 423, + "x": 71, + "y": 260, "connections": { "0": [ { @@ -1766,8 +1990,8 @@ "component": "debug", "tab": "1615551125555", "name": "from dido to cmd", - "x": 441, - "y": 461, + "x": 423, + "y": 331, "connections": {}, "disabledio": { "input": [ @@ -1792,8 +2016,8 @@ "component": "debug", "tab": "1615551125555", "name": "HTTP routes", - "x": 441, - "y": 564, + "x": 423, + "y": 422, "connections": {}, "disabledio": { "input": [ @@ -1817,112 +2041,10 @@ "id": "1684179110403", "component": "debug", "tab": "1611921777196", - "name": "MDBToDido", + "name": "to_dido", "x": 402, "y": 300, "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "options": { - "type": "data", - "repository": false, - "enabled": true - }, - "color": "#967ADC", - "notes": "" - }, - { - "id": "1699963668903", - "component": "dido_controller", - "tab": "1615551125555", - "name": "DIDO_Controller", - "x": 406, - "y": 940, - "connections": { - "0": [ - { - "index": "0", - "id": "1617104731852" - } - ], - "1": [ - { - "index": "0", - "id": "1617104731852" - }, - { - "index": "0", - "id": "1617115013095" - } - ], - "2": [ - { - "index": "0", - "id": "1618393583970" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "options": { - "edge": "undefined" - }, - "color": "#2134B0", - "notes": "" - }, - { - "id": "1699964678894", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "modbus_to_dido", - "x": 83, - "y": 775, - "connections": { - "0": [ - { - "index": "0", - "id": "1699963668903" - }, - { - "index": "0", - "id": "1699964793925" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "modbus_to_dido", - "color": "gray" - }, - "options": { - "wirename": "modbus_to_dido" - }, - "color": "#303E4D", - "notes": "" - }, - { - "id": "1699964793925", - "component": "debug", - "tab": "1615551125555", - "name": "modbusToDido", - "x": 415, - "y": 765, - "connections": {}, "disabledio": { "input": [ 0 @@ -1942,37 +2064,17 @@ "notes": "" }, { - "id": "1699965957410", - "component": "modbus_reader", - "tab": "1611921777196", - "name": "Modbus reader", - "x": 102, - "y": 175, + "id": "1697806290915", + "component": "trigger", + "tab": "1612772287426", + "name": "TEST svietidlo CCT", + "x": 23, + "y": 253, "connections": { "0": [ { "index": "0", - "id": "1611951142547" - } - ], - "1": [ - { - "index": "0", - "id": "1621340721628" - }, - { - "index": "0", - "id": "1684179110403" - } - ], - "2": [ - { - "index": "0", - "id": "1621340721628" - }, - { - "index": "0", - "id": "1684179110403" + "id": "1612776786008" } ] }, @@ -1984,51 +2086,11 @@ "text": "", "color": "gray" }, - "options": {}, - "color": "#2134B0", - "notes": "" - }, - { - "id": "1700411878636", - "component": "thermometer", - "tab": "1611921777196", - "name": "Thermometer", - "x": 94.75, - "y": 414, - "connections": { - "0": [ - { - "index": "0", - "id": "1615802995322" - } - ], - "1": [ - { - "index": "0", - "id": "1615809595184" - }, - { - "index": "0", - "id": "1615809128443" - } - ], - "2": [ - { - "index": "0", - "id": "1621340721628" - } - ] + "options": { + "datatype": "object", + "data": "{ \"K94XLav1glVRnyQ6r01PQyAme3YJwBxM5oOzdP2j\": [ { \"ts\": 1697806318325, \"values\": { \"status\": \"OK\" } } ] }" }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "options": {}, - "color": "#5CB36D", + "color": "#F6BB42", "notes": "" } ], diff --git a/flow/dido_controller.js b/flow/di_do_controller.js similarity index 81% rename from flow/dido_controller.js rename to flow/di_do_controller.js index 8b5a7b7..11fd158 100644 --- a/flow/dido_controller.js +++ b/flow/di_do_controller.js @@ -1,9 +1,9 @@ -exports.id = 'dido_controller'; -exports.title = 'DIDO_Controller'; -exports.version = '2.0.0'; +exports.id = 'di_do_controller'; +exports.title = 'DI_DO_Controller'; +exports.version = '1.0.0'; exports.group = 'Worksys'; exports.color = '#2134B0'; -exports.input = 3; +exports.input = 1; exports.output = ["red", "white", "yellow"]; exports.click = false; exports.author = 'Daniel Segeš'; @@ -60,7 +60,7 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda //globals //FIRMWARE version -FLOW.OMS_edge_fw_version = "2023-10-18";//rok-mesiac-den +FLOW.OMS_edge_fw_version = "2022-05-12";//rok-mesiac-den FLOW.OMS_edgeName = ""; FLOW.OMS_maintenance_mode = false; @@ -178,7 +178,7 @@ exports.install = function(instance) { deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód deviceStatuses["door_condition"] = "closed";//Dverový kontakt - deviceStatuses["em"] = "OK";//elektromer rvo + deviceStatuses["rvo"] = {status: "OK"};//elektromer rvo deviceStatuses["temperature"] = "OK";//templomer deviceStatuses["battery"] = "OK";//Batéria deviceStatuses["power_supply"] = "OK";//Zdroj @@ -187,7 +187,6 @@ exports.install = function(instance) { deviceStatuses["state_of_breaker"] = {};//"Off";//Istič deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač - deviceStatuses["twilight_sensor"] = "OK"; //lux sensor /* dbRelays.on('change', function(doc, old) { @@ -303,27 +302,13 @@ exports.install = function(instance) { let time = 3*1000; setTimeout(function(){ - instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"}); + instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "buildTasks"}); sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", instanceSendTo.tb, instance ); monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version); }, time); } - - - // we ensure, all tasks will be rebuild every day at 11. To set correct switch off and on times - let sendRebuildTasksAt11 = null; - const checkIf11Oclock = () => - { - const d = new Date(); - const h = d.getHours(); - if(h === 11) - { - instance.send(instanceSendTo.cmd_manager, {sender:"dido_controller", cmd:"buildTasks"}); - } - } - sendRebuildTasksAt11 = setInterval(checkIf11Oclock, 3600000); function handleRsPort() @@ -463,6 +448,7 @@ exports.install = function(instance) { // dev: 'input', // mode: 'Simple' // }, + ws.onmessage = function(data) { data = JSON.parse(data.data); @@ -533,7 +519,6 @@ exports.install = function(instance) { instance.on("close", () => { if(rsPort) rsPort.close(); if(ws) ws.close(); - clearInterval(sendRebuildTasksAt11); }) loadAllDb(); @@ -732,7 +717,7 @@ exports.install = function(instance) { //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1}; ws.send(JSON.stringify(cmd)); - //switchLogic(pin, 1) + switchLogic(pin, 1) } } @@ -806,92 +791,71 @@ exports.install = function(instance) { else if(ws) { //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method - //monitor.info("turnOffLine pin (relay)", pin); + // monitor.info("turnOffLine pin (relay)", pin); let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0}; ws.send(JSON.stringify(cmd)); - //switchLogic(pin, 0) + switchLogic(pin, 0) } } - //data from modbus_reader or temperature sensor or twilight sensor or other modbus device - instance.on("0", flowdata => { - - if(!flowdata.data instanceof Object) return; - - // console.log('***********************', flowdata.data) - instance.send(instanceSendTo.debug, flowdata.data); - - // we handle nok status from modbus_reader component and thermometer - if(flowdata.data?.status) - { - const status = flowdata.data.status; - if(status == "NOK-twilight_sensor") - { - deviceStatuses["twilight_sensor"] = "NOK"; - } - else if(status == "NOK-em340" || status == "NOK-em111") - { - deviceStatuses["em"] = "NOK"; - } - else if(status == "NOK-thermometer") - { - deviceStatuses["temperature"] = "NOK"; - } - return; - } - - const values = flowdata.data.values; - if(values.hasOwnProperty("twilight_sensor")) - { - instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]}); - deviceStatuses["twilight_sensor"] = "OK" - } - else if(values.hasOwnProperty("temperature")) - { - deviceStatuses["temperature"] = "OK"; - } - // EM - else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current")) - { - deviceStatuses["em"] = "OK"; - } - - const updateStatus = checkFinalRVOStatus(); - if(updateStatus) values.status = "OK"; - - sendTelemetry(values, FLOW.OMS_rvo_tbname); - - }) - - // we expect array as flowdata.data - instance.on("1", flowdata => { + instance.on("data", (flowdata) => { console.log(flowdata.data); - if(!flowdata.data instanceof Object) return; + if(flowdata.data instanceof Object) + { - let obj = flowdata.data; - let line = obj.line; - let force = obj.force; - let info = obj.info; + if(flowdata.data.hasOwnProperty("sender")) + { + //console.log("sender", flowdata.data); - if(obj.command == "turnOn") turnOnLine(line, undefined, force, info); - else if(obj.command == "turnOff") turnOffLine(line, undefined, force, info); - else if(obj.command == "turnOnAlarm") turnOnAlarm(); - else if(obj.command == "turnOffAlarm") turnOffAlarm(); + if(flowdata.data.sender == "gettemperature") + { + deviceStatuses["temperature"] = flowdata.data.status; + } + else if(flowdata.data.sender == "modbus_citysys") + { + //elektromer rvo + if(flowdata.data.tbdata.hasOwnProperty(edgeName)) + { + //rvo + deviceStatuses["rvo"] = {status: flowdata.data.tbdata[edgeName][0]["values"]["status"], tbdata: flowdata.data.tbdata}; + } + else if(flowdata.data.tbdata.hasOwnProperty("twilight_sensor")) + { + switchLogic('twilight_sensor', flowdata.data.tbdata["twilight_sensor"]) + } + } + instance.send(instanceSendTo.debug, flowdata.data ); + return; + } + + let obj = flowdata.data; + + let line = obj.line; + let force = obj.force; + let info = obj.info; + + if(obj.command == "turnOn") turnOnLine(line, undefined, force, info); + else if(obj.command == "turnOff") turnOffLine(line, undefined, force, info); + else if(obj.command == "turnOnAlarm") turnOnAlarm(); + else if(obj.command == "turnOffAlarm") turnOffAlarm(); + + return; + } //! ake data prichadzaju z cmd_manager.js ??? //TODO transform to websocket - if (Array.isArray(obj)){ + if (Array.isArray(flowdata.data)){ - rsPort.write(Buffer.from(obj), function(err) { - switchLogic(obj); + rsPort.write(Buffer.from(flowdata.data), function(err) { + switchLogic(flowdata.data); - instance.send(instanceSendTo.debug, {"WRITE":obj} ); + instance.send(instanceSendTo.debug, {"WRITE":flowdata.data} ); }); } }) @@ -903,17 +867,12 @@ exports.install = function(instance) { let bytes = []; let bits = []; - //Hlavný istič - state_of_main_switch - if(deviceStatuses["state_of_main_switch"] == "On") - { - bits.push(0); - } - else if(deviceStatuses["state_of_main_switch"] == "Off") - { - bits.push(1); - } - //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY + //Hlavný istič - state_of_main_switch + if(deviceStatuses["state_of_main_switch"] == "On") bits.push(0); + else if(deviceStatuses["state_of_main_switch"] == "Off") bits.push(1); + + //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false if(!FLOW.OMS_maintenance_mode) { if(deviceStatuses["rotary_switch_state"] == "Manual") @@ -944,88 +903,41 @@ exports.install = function(instance) { { bits.push(0); } - else - { - bits.push(1); - } + else bits.push(1); //EM - if(deviceStatuses["em"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } + if(deviceStatuses["rvo"].status == "NOK") bits.push(1); + else bits.push(0); //Teplomer - if(deviceStatuses["temperature"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } + if(deviceStatuses["temperature"] == "NOK") bits.push(1); + else bits.push(0); //Batéria - if(deviceStatuses["battery"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } + if(deviceStatuses["battery"] == "NOK") bits.push(1); + else bits.push(0); //Zdroj - if(deviceStatuses["power_supply"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } + if(deviceStatuses["power_supply"] == "NOK") bits.push(1); + else bits.push(0); //MN - if(deviceStatuses["master_node"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } + if(deviceStatuses["master_node"] == "NOK") bits.push(1); + else bits.push(0); //výpadok napätia na fáze - if(deviceStatuses["no_voltage"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } - - if(deviceStatuses["twilight_sensor"] == "NOK") - { - bits.push(1); - } - else - { - bits.push(0); - } + if(deviceStatuses["no_voltage"] == "NOK") bits.push(1); + else bits.push(0); - // doplnime do 16 bitov (2 byty) - for(let i = bits.length; i < 16; i++) - { - bits.push(0); - } - - // console.log("calculateStateCode - deviceStatuses", deviceStatuses); - // console.log("calculateStateCode", bits); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + + //console.log("calculateStateCode - deviceStatuses", deviceStatuses); + //console.log("calculateStateCode", bits); let byte0 = bitwise.byte.write(bits.slice(0,8).reverse()); let byte1 = bitwise.byte.write(bits.slice(8).reverse()); @@ -1047,18 +959,10 @@ exports.install = function(instance) { let status = "OK"; - if(deviceStatuses["em"] == "NOK") + if(deviceStatuses["rvo"].status == "NOK") { - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: EM status is NOK"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: EM status is NOK"); - - status = "NOK"; - } - - if(deviceStatuses["twilight_sensor"] == "NOK") - { - let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: twilight_sensor is NOK"); - if(writeToFile) errLogger.error("checkFinalRVOStatus: twilight sensor is NOK"); + let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: rvo status is NOK"); + if(writeToFile) errLogger.error("checkFinalRVOStatus: rvo status is NOK", deviceStatuses["rvo"].tbdata); status = "NOK"; } @@ -1075,15 +979,10 @@ exports.install = function(instance) { if(status == "OK") { - let pinIndexes = [1, 4, 6]; - if(controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05']; - - //console.log('-------- previousValues', previousValues); - - for (const pinIndex of pinIndexes) { + for (const pinIndex of [1, 4, 6]) { if (previousValues[pinIndex] === 0) { - if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue; + if (pinIndex === 6 && FLOW.OMS_maintenance_mode) continue; let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0"); if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type); @@ -1095,7 +994,7 @@ exports.install = function(instance) { } } - // battery status. If value is 1 - battery is NOK + // battery status. If value is 1 - battery is not ok if (previousValues[5] === 1) { let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery"); @@ -1104,6 +1003,18 @@ exports.install = function(instance) { status = "NOK"; } + //ak mame telemetriu z elektromeru, posleme + if(deviceStatuses["rvo"].tbdata != undefined) + { + //deviceStatuses["rvo"] = {status: flowdata.data.tbdata[edgeName][0]["values"]["status"], tbdata: flowdata.data.tbdata}; + + deviceStatuses["rvo"].tbdata[edgeName][0]["values"]["status"] = status; + + + instance.send(instanceSendTo.tb, deviceStatuses["rvo"].tbdata); + delete deviceStatuses["rvo"].tbdata; + } + //console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding); if(!FLOW.OMS_masterNodeIsResponding) @@ -1130,7 +1041,17 @@ exports.install = function(instance) { if(status == "NOK") { - sendTelemetry({status: "NOK"}, FLOW.OMS_rvo_tbname); + + const dataToTb = { + [edgeName]: [ + { + "ts": Date.now(), + "values": {status: "NOK"} + } + ] + } + + instance.send(instanceSendTo.tb, dataToTb); return false; } @@ -1139,8 +1060,8 @@ exports.install = function(instance) { } - // we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]] - // we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1] + // we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) + // we pass two values in case of websocket ==> switchLogic("relay1_03",1) const switchLogic = (...args) => { let values = {status: "OK"}; @@ -1174,27 +1095,28 @@ exports.install = function(instance) { let tbname = obj.tbname; //default value - let value = "On"; - if(newPinValue === 0) value = "Off"; + let value = "Off"; + if(newPinValue === 1) value = "On"; //Hlavný istič - //! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri - //! takze ked pride z evoku signal pre 'input1_05', handlujeme ho ako 'door_condition' - if(type === "!!!state_of_main_switch") + if(type === "state_of_main_switch") { - if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) + + if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { + sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", instanceSendTo.tb, instance , "state_of_main_switch"); values["status"] = "NOK"; - - deviceStatuses["state_of_main_switch"] = "Off"; + value = "Off"; } - else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) + else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", instanceSendTo.tb, instance , "state_of_main_switch"); - - deviceStatuses["state_of_main_switch"] = "On"; + value = "On"; } + + + deviceStatuses["state_of_main_switch"] = value; } //Prevádzkový mód @@ -1226,9 +1148,9 @@ exports.install = function(instance) { } //console.log('***********************', pin2, pin3) - if (pin2 == 1 && pin3 == 0) value = "Manual"; + if (pin2 == 0 && pin3 == 1) value = "Manual"; if (pin2 == 0 && pin3 == 0) value = "Off"; - if (pin2 == 0 && pin3 == 1) value = "Automatic"; + if (pin2 == 1 && pin3 == 0) value = "Automatic"; deviceStatuses["rotary_switch_state"] = value; @@ -1236,14 +1158,14 @@ exports.install = function(instance) { //ak je spracovany, a automatic - tak ho zapnem //ak nie je spracovany, iba profil zapisem - instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value}); + instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "rotary_switch_state", value: value}); //console.log("rotary_switch_state pin", pin2, pin3, value); } //Zdroj - pin 4 else if (type === "power_supply") { - if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) + if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { //sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", instanceSendTo.tb, instance); sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", instanceSendTo.tb, instance, "power_supply"); @@ -1251,7 +1173,7 @@ exports.install = function(instance) { deviceStatuses["power_supply"] = "NOK"; } - else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) + else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", instanceSendTo.tb, instance); sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", instanceSendTo.tb, instance, "power_supply"); @@ -1262,7 +1184,7 @@ exports.install = function(instance) { //Batéria - pin 5 else if (type === "battery") { - if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) + if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) { //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", instanceSendTo.tb, instance); sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", instanceSendTo.tb, instance, "battery_level"); @@ -1270,7 +1192,7 @@ exports.install = function(instance) { deviceStatuses["battery"] = "NOK"; } - else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) + else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) { //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", instanceSendTo.tb, instance); sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", instanceSendTo.tb, instance, "battery_level"); @@ -1279,11 +1201,9 @@ exports.install = function(instance) { } } //Dverový kontakt - pin 6 - //! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch" - //! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition' - else if(type == "door_condition" || type === "state_of_main_switch") + else if(type == "door_condition") { - newPinValue === 0 ? value = "open" : value = "closed"; + newPinValue === 0 ? value = "closed" : value = "open"; if (newPinValue != previousValues[pinIndex]) { @@ -1304,9 +1224,8 @@ exports.install = function(instance) { //console.log(door_has_been_open_without_permision_alarm_is_on); - // zapneme sirenu - // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition") - if(type === "door_condition") turnOnAlarm(); + //zapneme sirenu + turnOnAlarm(); } if (value === "closed") @@ -1376,7 +1295,7 @@ exports.install = function(instance) { if(diff >= twilight_sensor_interval * 60 * 1000) { const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length; - instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: average}); + instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "lux_sensor", value: average}); twilight_sensor = []; @@ -1384,20 +1303,18 @@ exports.install = function(instance) { } //else console.log("lux_sensor", value, diff); } + else + { + instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "lux_sensor", value: value}); + } } else if(type == "state_of_contactor") { //sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", instanceSendTo.tb, instance ); + sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", instanceSendTo.tb, instance ); - if(!(deviceStatuses["state_of_contactor"][line] == value)) - { - sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", instanceSendTo.tb, instance ); - } - else - { - deviceStatuses["state_of_contactor"][line] = value; - } - + deviceStatuses["state_of_contactor"][line] = value; + //true, false if(value === "On") value = true; else if(value === "Off") value = false; @@ -1433,7 +1350,7 @@ exports.install = function(instance) { //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor setTimeout(function(){ - instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); + instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); }, time); reportLineStatus(line); @@ -1458,24 +1375,7 @@ exports.install = function(instance) { if(valueChanged) { - instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line}); - - //mame 3 istice. ked je viac ako 3 linie, dalsie sa zapajaju paralelne. to znamena na istici 1 je linia 1 a 4, na istici 2 je linia 2 a 5, na istici 3 je linia 3 a 6 (key je string ("4")) - //vyreportujeme a ohandlujeme liniu na tom istom istici ako paralelna linia - const lineOnSameBraker = line + 3 + ""; - - if(relaysData.hasOwnProperty(lineOnSameBraker)) { - instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "state_of_breaker", value: value, line: line + 3}); - - deviceStatuses["state_of_breaker"][line + 3] = value; - reportLineStatus(line + 3); - - values[type] = value; - tbname = relaysData[lineOnSameBraker].tbname; - sendTelemetry(values, tbname); - - delete values[type]; - } + instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "state_of_breaker", value: value, line: line}); } if(value == "Off") values["status"] = "NOK"; @@ -1483,27 +1383,25 @@ exports.install = function(instance) { deviceStatuses["state_of_breaker"][line] = value; reportLineStatus(line); + } values[type] = value; + + let result = checkFinalRVOStatus(); + if(!result && line == 0) + { + values["status"] = "NOK"; + } + + //-- //if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode(); if(pinsData.hasOwnProperty(pinIndex)) { let valueChanged = false; - if(newPinValue != previousValues[pinIndex]) - { - valueChanged = true; - //pin was changed - previousValues[pinIndex] = newPinValue; - } - - let result = checkFinalRVOStatus(); - if(!result && line == 0) - { - values["status"] = "NOK"; - } + if(newPinValue != previousValues[pinIndex]) valueChanged = true; if(type == "state_of_contactor") valueChanged = true; if(type == "rotary_switch_state") valueChanged = true; @@ -1514,7 +1412,7 @@ exports.install = function(instance) { if(FLOW.OMS_rvo_tbname == "") { - console.log("FLOW.OMS_rvo_tbname is EMPTY"); + console.log("FLOW.OMS_rvo_tbname is EMPTY"); } if(FLOW.OMS_rvo_tbname == tbname) @@ -1522,6 +1420,7 @@ exports.install = function(instance) { values["statecode"] = calculateStateCode(); //console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname); } + if(valueChanged) { @@ -1544,6 +1443,10 @@ exports.install = function(instance) { logger.debug("no pinIndex", pinsData[pinIndex], pinsData); } + //pin was changed + previousValues[pinIndex] = newPinValue; + + } @@ -1889,24 +1792,3 @@ exports.install = function(instance) { -// { -// "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV": [ -// { -// "ts": 1700409326353, -// "values": { -// "_event": { -// "type": "notice", -// "status": "new", -// "source": { -// "func": "rsPort.open()", -// "component": "1700343402190", -// "component_name": "DIDO_Controller", -// "edge": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV" -// }, -// "message": "al_shariff_10.0.0.38: FLOW has been started ", -// "message_data": "" -// } -// } -// } -// ] -// } \ No newline at end of file diff --git a/flow/gettemperature.js b/flow/gettemperature.js index 720592a..171cc96 100644 --- a/flow/gettemperature.js +++ b/flow/gettemperature.js @@ -1,18 +1,18 @@ -exports.id = 'thermometer'; +exports.id = 'gettemperature'; exports.title = 'Thermometer'; exports.group = 'Worksys'; exports.color = '#5CB36D'; -exports.version = '1.0.3'; +exports.version = '1.0.2'; 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`; +exports.readme = `# Getting temperature values from RVO`; const instanceSendTo = { debug: 0, tb: 1, - dido_controller: 2 + di_do_controller: 2 } //read temperature - frequency @@ -43,7 +43,7 @@ const monitor = log4js.getLogger("monitorLogs"); //monitor.info('info'); //errLogger.error("some error"); -const { promisifyBuilder } = require('./helper/db_helper'); +const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const dbSettings = TABLE("settings"); let temperatureAddress = ""; @@ -60,7 +60,7 @@ loadSettings(); exports.install = function(instance) { const { exec } = require('child_process'); - const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter'); + const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js'); let startRead; let dataToTb; @@ -76,9 +76,9 @@ exports.install = function(instance) { }) - const start = function() { + const start = function(){ - try { + try{ if(FLOW.OMS_controller_type === "unipi") { @@ -130,8 +130,8 @@ exports.install = function(instance) { monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready); - // instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ?? - instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"}); + instance.send(instanceSendTo.tb, dataToTb); + instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: status}); } else parseData(stdout); } @@ -162,9 +162,10 @@ exports.install = function(instance) { logger.debug("gettemperature", data); - if(!isNaN(data)) { + if (!isNaN(data)){ - if(counter > 290) + + //if ( counter > 290 ) { instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); @@ -173,22 +174,21 @@ exports.install = function(instance) { } logger.debug("gettemperature", data); - const values = { - "temperature": Number(data.toFixed(2)), - "status": "OK" - } dataToTb = { [edgeName]: [ { "ts": Date.now(), - "values":values + "values": { + "temperature": Number(data.toFixed(2)), + "status": "OK" + } } ] } instance.send(instanceSendTo.tb, dataToTb); - instance.send(instanceSendTo.dido_controller, values); + instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "OK"}); counter = 0; @@ -204,8 +204,9 @@ exports.install = function(instance) { sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer"); instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); - instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"}); + instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "NOK"}); } + } } @@ -215,4 +216,5 @@ exports.install = function(instance) { }, 3000); startRead = setInterval(start, timeoutMin * 1000 * 60); + }; \ No newline at end of file diff --git a/flow/modbus_citysys.js b/flow/modbus_citysys.js new file mode 100644 index 0000000..2dd9c06 --- /dev/null +++ b/flow/modbus_citysys.js @@ -0,0 +1,1135 @@ +exports.id = 'modbus_citysys'; +exports.title = 'Modbus_citysys'; +exports.version = '1.0.0'; +exports.group = 'Worksys'; +exports.color = '#2134B0'; +exports.input = 1; +exports.output = ["red", "white", "blue", "orange"]; +exports.click = false; +exports.author = 'Jakub Klena'; +exports.icon = 'bolt'; +exports.options = { edge: "undefined" }; + +exports.html = `
+
+
+
Edge TB Name
+
+
+
`; + +exports.readme = `# Energomonitor +## Outputs + + - *Red* - ERROR output (can connect to filewriter or something) + - *White* - STATUS output (answers to your commands, ERRORS and WARNINGS from your commands go both to this and to their own outputs, so they get logged) + - *Blue* - TB output (pure data for TB) +`; + + +const instanceSendTo = { + error: 0, + debug: 1, + tb: 2, + di_do_controller: 3 +} + +const DataToTbHandler = require('./helper/DataToTbHandler.js'); +const { sendNotification } = require('./helper/notification_reporter.js'); +const dbRelays = TABLE("relays"); +const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); +let tbname; + +async function loadSettings() +{ + //todo global FLOW.OMS_edgeName is making problem, so we load it here as well, it should not be + let responseRelays = await promisifyBuilder(dbRelays.find()); + FLOW.OMS_edgeName = responseRelays[0]["tbname"]; + tbname = FLOW.OMS_edgeName; +} + +loadSettings(); + +exports.install = function(instance) { + const SerialPort = require('serialport'); + const { exec } = require('child_process'); + const fs = require("fs"); + const filepath = F.path.root("saved_data/modbus_settings"); + const backup_filepath = F.path.root("saved_data/modbus_settings_backup"); + + const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); + const errorHandler = new ErrorToServiceHandler(); + + let receivedDataArray = []; + + + instance.CONFIG = { + "isRunning": false, + "debug": true, + "timeoutTime": 10000, + "msgWaitTime": 1000, + "port": "/dev/ttymxc0", + //"port_options": "stty -F /dev/ttymxc1 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke" + "port_options": "stty -F /dev/ttymxc0 9600 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke" + }; + + let PRIVATEVARS = { + "errBuffer": [], // Buffer for error messages + "tbBuffer": [], // Buffer for TB push messages + "device_index": 0, + "cmd_index": -1, + "devices": [ + /*{ + "name": "Elektrometer 1", + "tb_name": "EOzNMgZ9n43qPbjXmy7zwdA2DKdYvW5e6pxGRrVa", + "type": "EM111", + "address": 1, + "data":[], + "cmd":[], + "timeoutcount":0, + "status":"virtual" + },*/ + // { + // "name": "Elektrometer 2", + // "tb_name": "pJX1ObgmqGZ54DMyYL7aDdkEVdve38WKRzwjNrQ9", + // "type": "EM111", + // "address": 2, + // "data":[], + // "cmd":[], + // "timeoutcount":0, + // "status":"virtual" + // }, + // { + // "name": "Elektrometer 3", + // "tb_name": "XRvmwNz8QPblKp41GD7lKVkJrLVYoBO92dMegn6W", + // "type": "EM111", + // "address": 3, + // "data":[], + // "cmd":[], + // "timeoutcount":0, + // "status":"virtual" + // }, + // { + // "name": "Elektrometer 4", + // "tb_name": "oRO8rjaBDy21qPQJzW7oD9ApK3xmNleVZg9Ed4Gw", + // "type": "EM111", + // "address": 4, + // "data":[], + // "cmd":[], + // "timeoutcount":0, + // "status":"virtual" + // }, + { + "name": "Elektrometer 1", + "tb_name": "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", + "type": "EM340", + "address": 1, + "data":[], + "cmd":[], + "timeoutcount":0, + "status":"virtual" + } + ], + "cmd_tables": [ + { + "type":"EM340", + "cmd":[ + { + "name": "Voltage L1", + "tb_name": "a", + "register": 0, + "size": 2, + "multiplier": 0.1 + }, + { + "name": "Voltage L2", + "tb_name": "b", + "register": 2, + "size": 2, + "multiplier": 0.1 + }, + { + "name": "Voltage L3", + "tb_name": "c", + "register": 4, + "size": 2, + "multiplier": 0.1 + }, + { + "name": "Current L1", + "tb_name": "d", + "register": 12, + "size": 2, + "multiplier": 0.001 + }, + { + "name": "Current L2", + "tb_name": "e", + "register": 14, + "size": 2, + "multiplier": 0.001 + }, + { + "name": "Current L3", + "tb_name": "f", + "register": 16, + "size": 2, + "multiplier": 0.001 + } + + + // { + // "name": "Power factor", + // "tb_name": "power_factor", + // "register": 14, + // "size": 1, + // "multiplier": 0.001 + // }, + // { + // "name": "Frequency", + // "tb_name": "frequency", + // "register": 15, + // "size": 1, + // "multiplier": 0.1 + // }, + // { + // "name": "Energy", + // "tb_name": "consumption", + // "register": 16, + // "size": 2, + // "multiplier": 0.1 + // } + ] + } + ] + }; + + let ERRWEIGHT = { + EMERGENCY: "emergency", // System unusable + ALERT: "alert", // Action must be taken immidiately + CRITICAL: "critical", // Component unable to function + ERROR: "error", // Error, but component able to recover from it + WARNING: "warning", // Possibility of error, system running futher + NOTICE: "notice", // Significant message but not an error, things user might want to know about + INFO: "informational", // Info + DEBUG: "debug" // Debug - only if CONFIG.debug is enabled + }; + + instance.currentData = function(){ + let resp = []; + for (let f = 0; f < PRIVATEVARS.devices.length; f++){ + let dev = PRIVATEVARS.devices[f]; + for (let e = 0; e < dev.data.length; e++){ + let d = dev.data[e]; + resp.push({ + "name": dev.name+" - "+d.name, + "value": d.value + }); + } + } + return resp; + }; + + instance.configList = function(){ + let resp = []; + /*let data = PRIVATEVARS.feeds; + for (let a = 0; a < data.length; a++){ + for (let i = 0; i < instance.CONFIG.feeds.length; i++){ + let feed = instance.CONFIG.feeds[i]; + if (feed.name === data[a].id){ + for (let b = 0; b < data[a].streams.length; b++){ + for (let j = 0; j < feed.streams.length; j++){ + let stream = feed.streams[j]; + if (stream.name === data[a].streams[b].id){ + data[a].streams[b]["exists"] = true; + data[a].streams[b]["currently"] = stream; + } + } + } + } + } + } + resp.push({ + "name":"Device manager", + "icon":"tasks", + "_show":false, + "js_func":"energoDevManager", + "data": data + });*/ + + return resp; + } + + let timeoutInterval = null; + let msgWaitInterval = null; + let port = null; + let myEdge = "undefined"; + let starter = null; + instance.status("Loading...", "red"); + + + instance.availableCommands = [ + { + "name": "Status", + "cmd": "qStatus", + "icon": "stream", + "func": function(body){ + let a = true; + if (timeoutInterval === null){ + a = false; + } + let b = true; + if (msgWaitInterval === null){ + b = false; + } + let st = { + "isRunning":instance.CONFIG.isRunning, + "timeoutInterval":a, + "msgWaitInterval":b, + "CONFIG":instance.CONFIG + }; + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": st + }; + } + }, + { + "name": "Start Reading", + "cmd": "sStart", + "icon": "play", + "func": function(body){ + /*if (running === false){ + startCmdWaitInterval(); + running = true; + return "Reading started !"; + } else { + return "Reading already active !"; + }*/ + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": "WIP" + }; + } + }, + { + "name": "Stop Reading", + "cmd": "sStop", + "icon": "stop", + "func": function(body){ + /*if (running === true){ + stopCmdWaitInterval(); + stopTimeoutInterval(); + running = false; + return "Reading stopped !"; + } else { + return "Reading already inactive !"; + }*/ + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": "WIP" + }; + } + }, + { + "name": "Read current data", + "cmd": "qCurrentData", + "icon": "chart-bar", + "func": function(body){ + let resp = instance.currentData(); + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": resp + }; + } + }, + { + "name": "Save current config", + "cmd": "saveConfig", + "icon": "save", + "func": function(body){ + + instance.set("config", JSON.stringify(instance.CONFIG)); + instance.set("private", JSON.stringify(PRIVATEVARS)); + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": "done" + }; + } + }, + { + "name": "Toggle debug", + "cmd": "sDebug", + "icon": "comment-dots", + "func": function(body){ + + if (instance.CONFIG.debug){ + instance.CONFIG.debug = false; + instance.set("config", JSON.stringify(instance.CONFIG)); + + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": "debug OFF" + }; + } else { + instance.CONFIG.debug = true; + instance.set("config", JSON.stringify(instance.CONFIG)); + + return { + "type": "ok", + "timestamp": humanReadableTimeAndDate(), + "resp": "debug ON" + }; + } + + } + } + ]; + + + + function saveData(){ + if (checkFile(filepath)){ + let content = undefined; + try { + content = fs.readFileSync(filepath); + } catch (err){ + console.log("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message}); + + //sendError("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message}); + } + + if (content !== undefined){ + try { + fs.writeFileSync(backup_filepath, content, "utf8"); + } catch (err) { + //sendError("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to save backup of configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message}); + console.log("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to save backup of configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message}); + } + } + } + + let a = { + "config":instance.CONFIG, + "private":PRIVATEVARS + }; + + try { + fs.writeFileSync(filepath, JSON.stringify(a), "utf8"); + } catch (err) { + //sendError("saveData", myEdge, ERRWEIGHT.CRITICAL, "Unable to save configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message}); + console.log("saveData", myEdge, ERRWEIGHT.CRITICAL, "Unable to save configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message}); + } + } + + function loadData(){ + let content = undefined; + //console.log(filepath); + if (checkFile(filepath)){ + try { + content = fs.readFileSync(filepath); + } catch (err){ + //sendError("loadData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message}); + console.log("loadData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message}); + } + } else { + if (checkFile(backup_filepath)){ + try { + content = fs.readFileSync(backup_filepath); + } catch (err){ + //sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "Unable to read backup configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message}); + console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "Unable to read backup configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message}); + } + if (content !== undefined){ + //sendError("loadData", myEdge, ERRWEIGHT.WARNING, "No configuration, loading from backup !", {"name":instance.name, "id":instance.id}); + console.log("loadData", myEdge, ERRWEIGHT.WARNING, "No configuration, loading from backup !", {"name":instance.name, "id":instance.id}); + } + } else { + //sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "No configuration, not even backup !", {"name":instance.name, "id":instance.id}); + console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "No configuration, not even backup !", {"name":instance.name, "id":instance.id, "filepath": filepath}); + } + } + + + + if (content !== undefined){ + let a = JSON.parse(content); + instance.send(instanceSendTo.debug, a); + let c = a.config; + let p = a.private; + + + if (c === undefined){ + //sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "Configuration not found !", {"name":instance.name, "id":instance.id}); + console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "Configuration not found !", {"name":instance.name, "id":instance.id}); + instance.status("Error - no config", "red"); + } else if (p === undefined){ + //sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "Privatevars not found !", {"name":instance.name, "id":instance.id}); + console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "Privatevars not found !", {"name":instance.name, "id":instance.id}); + instance.status("Error - no private vars", "red"); + } else { + instance.CONFIG = c; + PRIVATEVARS = p; + + // Daj kazdemu device jeho tabulku prikazu + for (let i = 0; i < PRIVATEVARS.devices.length; i++){ + let device = PRIVATEVARS.devices[i]; + for (let j = 0; j < PRIVATEVARS.cmd_tables.length; j++){ + let table = PRIVATEVARS.cmd_tables[j]; + + if (device.type === table.type){ + PRIVATEVARS.devices[i].cmd = table.cmd; + } + } + } + + if (myEdge === "undefined"){ + instance.status("Unconfigured", "red"); + } else { + instance.status("Running", "green"); + startCmdWaitInterval(); + + instance.CONFIG.isRunning = true; + console.log("modbus loaded: ", PRIVATEVARS.devices); + } + } + } + } + + function checkFile(name){ + try { + fs.accessSync(name, fs.constants.F_OK | fs.constants.R_OK | fs.constants.W_OK); + return true; + } catch (err) { + return false; + } + } + + //Zapina slucku vycitavania dat + function readDeviceData(){ + stopCmdWaitInterval(); + + // let tbname = FLOW.OMS_edgeName; + + // Check port existance + if (port === null) + { + port = new SerialPort(instance.CONFIG.port); + + port.on('error', function(err) { + //logger.debug("rsPort opened error - failed", err.message); + //instance.send(instanceSendTo.debug, err.message); + + errorHandler.sendMessageToService( exports.title + " MODBUS RS485 open - failed: " + err.message); + }) + + port.on('open', function() { + + console.log("--->MODBUS RS485 READY - port opened"); + + // exec("sudo halfduplex /dev/ttymxc0", (error, stdout, stderr) => { + // instance.send(instanceSendTo.debug, {"err": error}); + + // if (error) { + // console.log("--->MODBUS RS485", error, stderr); + // errorHandler.sendMessageToService( exports.title + " sudo halfduplex /dev/ttymxc0 - failed: " + error); + // } + + // }); + + exec(instance.CONFIG.port_options, (error, stdout, stderr) => { + instance.send(instanceSendTo.debug, {"stdout":stdout,"stderr":stderr,"err":error}); + + if (error) { + console.log("--->MODBUS RS485", error, stderr); + errorHandler.sendMessageToService( exports.title + " " + instance.CONFIG.port_options + " - failed: " + error); + } + + }); + + }); + + port.on('data', receivedData); + + //sendError("readDeviceData", myEdge, ERRWEIGHT.DEBUG, "Serial port open!", {}); + //console.log("-->MODBUS readDeviceData", myEdge, ERRWEIGHT.DEBUG, "Serial port open!", {}); + + startCmdWaitInterval(); + return; // Cakame na port aby sa spravne otvoril a rozbehol + } + + + // Skontroluj existenciu device listu + if (PRIVATEVARS.devices.length > 0){ + // Ponastavuj indexy + PRIVATEVARS.cmd_index++; + if (PRIVATEVARS.cmd_index >= PRIVATEVARS.devices[PRIVATEVARS.device_index].cmd.length){ + // Kedže všetky príkazy pre daný device sú vybavené, je treba odoslat vyčítané data do TB + updateDataInTB(); + + PRIVATEVARS.cmd_index = 0; + PRIVATEVARS.device_index++; + + if (PRIVATEVARS.device_index >= PRIVATEVARS.devices.length){ + PRIVATEVARS.device_index = 0; + } + } + + let device = PRIVATEVARS.devices[PRIVATEVARS.device_index]; + + // Skontroluj existenciu príkazú pre daný device type + if (device.cmd.length < 1){ + //sendError("readDeviceData", tbname, ERRWEIGHT.ERROR, "No commands for this device type !", {"type": device.type}); + console.log("readDeviceData", tbname, ERRWEIGHT.ERROR, "No commands for this device type !", {"type": device.type}); + startCmdWaitInterval(); + return; + } + + // Odešli nasledujúci príkaz + sendCommand(); + + } else { + instance.CONFIG.isRunning = false; + //sendError("readDeviceData", myEdge, ERRWEIGHT.CRITICAL, "Modbus has no devices registered!", {}); + console.log("modbus_citys: readDeviceData", myEdge, ERRWEIGHT.CRITICAL, "Modbus has no devices registered!", {}); + } + } + + function readingTimeouted(){ + stopCmdWaitInterval(); + stopTimeoutInterval(); + + // let tbname = FLOW.OMS_edgeName; + + let device = PRIVATEVARS.devices[PRIVATEVARS.device_index]; + let com = device.cmd[PRIVATEVARS.cmd_index]; + //sendError("readingTimeouted", tbame, ERRWEIGHT.WARNING, "Reading timeouted !", {"device": device.address, "cmd": com.register}); + console.log("modbus_citys: readingTimeouted", tbname, ERRWEIGHT.WARNING, "Reading timeouted !", {"device": device.address, "cmd": com.register}); + + device.timeoutcount++; + //console.log("device.timeoutcount", device.timeoutcount); + if (device.timeoutcount === 16) + { + + //sendError("modbus_citys: readingTimeouted", tbname, ERRWEIGHT.CRITICAL, "Electrometer is not responding - reading timeouted", ""); + sendNotification("modbus_citys: readingTimeouted", tbname, "electrometer_is_not_responding", {}, "", instanceSendTo.tb, instance ); + + if (device.status === "OK"){ + device.status === "NOK"; + } + } + + startCmdWaitInterval(); + } + + function receivedData(data){ + + //let array = [...data]; + //console.log("received data", array); + + // let tbname = FLOW.OMS_edgeName; + + //!if received data are less than 9 bytes, we store it in array variable and return. than we concatenate second incoming + // data and then length of array is 9 + receivedDataArray = [...receivedDataArray, ...data]; + //if (array.length < 9) return; + let l = receivedDataArray.length; + //console.log("received",receivedDataArray, l) + + if ( l < 7 || l > 9 || l == 8 ) return; + + let device = PRIVATEVARS.devices[PRIVATEVARS.device_index]; + let com = device.cmd[PRIVATEVARS.cmd_index]; + + if (device.timeoutcount > 16) { + //sendError("Modbus_citysys: receivedData", tbname, ERRWEIGHT.NOTICE, "Electrometer is responding again", ""); + sendNotification("modbus_citys: receivedData", tbname, "electrometer_is_responding_again", {}, "", instanceSendTo.tb, instance ); + } + + device.timeoutcount = 0; + + if ((l == 7 && com.size != 1) || ( l == 9 && com.size != 2)) return; + + stopTimeoutInterval(); + + //sendError("receivedData", tbname, ERRWEIGHT.DEBUG, "Received data !", {"cmd": receivedDataArray}); + //console.log("receivedData", tbname, ERRWEIGHT.DEBUG, "Received data !", {"cmd": receivedDataArray}); + + // Skontroluj či sedí počet bytú v správe + //console.log("com size", com.size*2, "array2", receivedDataArray[2]); + if (receivedDataArray[2] !== (com.size*2)){ + //sendError("receivedData", tbname, ERRWEIGHT.ERROR, "Received data of incorrect size !", {"expected": (com.size*2), "received": receivedDataArray[2], "cmd": com.register, "whole_msg": receivedDataArray}); + console.log("modbus_citys: receivedData", tbname, ERRWEIGHT.ERROR, "Received data of incorrect size !", {"expected": (com.size*2), "received": receivedDataArray[2], "cmd": com.register, "whole_msg": receivedDataArray}); + startTimeoutInterval(); + } else { + // Konvertuj raw data na human readable + + let v = (receivedDataArray[3] << 8) | receivedDataArray[4]; + if (com.size == 2){ + v = v | (receivedDataArray[5] << 24) | (receivedDataArray[6] << 16); + } + v = Math.round((v * com.multiplier) * 100) / 100; + + + // Pokad device nemá ešte žádné hodnoty vyčítané, pushni túto hodnotu do pola + if (device.data.length < 1){ + device.data.push({ // Vždy ked správne zakomunikuje obnov status na OK + "changed": true, + "name": "status", + "value": "OK" + }); + device.data.push({ + "changed": true, + "name": com.tb_name, + "value": v + }); + } else { + // Kedže už neco v poli má, kukni sa či je tam aj táto hodnota + let found = false; + for (let i = 0; i < device.data.length; i++){ + let d = device.data[i]; + if (d.name == "status"){ // Ked natrefíš na status (vždy tam musí byt) prepíš ho na OK lebo zakomunikoval správne + device.data[i].changed = true; + device.data[i].value = "OK"; + } + + if (d.name == com.tb_name){ + found = true; + device.data[i].changed = true; + device.data[i].value = v; + } + } + + // Pole existuje, ale táto hodnota tam neni, pridaj ju + if (found === false){ + device.data.push({ + "changed": true, + "name": com.tb_name, + "value": v + }); + } + } + + //Správne sme prijali odpoveď, je čas na další msg + startCmdWaitInterval(); + } + //console.log('received data array', receivedDataArray); + receivedDataArray = []; + } + + function updateDataInTB(){ + let device = PRIVATEVARS.devices[PRIVATEVARS.device_index]; + + // console.log("---- MB device", device); + // console.log("---MB device data", device.data); + + let values = ""; + for (let i = 0; i < device.data.length; i++){ + let data = device.data[i]; + if (data.changed){ + if (values !== ""){ + values += ", "; + } + + if (data.name === "status"){ + values += "\""+data.name+"\":\""+data.value+"\""; + // This makes sure, that if this device doesn’t respond even once in next reading cycle, it will be marked as NOK + device.data[i].changed = true; + device.data[i].value = "NOK"; + } else { + values += "\""+data.name+"\":"+data.value; + device.data[i].changed = false; + } + } + + } + + //console.log("values modbus", values); + + + if (values !== ""){ + + // let tbname = FLOW.OMS_edgeName; + // if(tbname == "" || tbname === undefined ) + // { + // console.log("!!!!!!FLOW.OMS_edgeName is empty - 1"); + // return; + // } + + let tbmsg = "{\"" + tbname + "\":[{\"ts\":"+Date.now()+", \"values\":{"+values+"}}] }"; + tbmsg = JSON.parse(tbmsg); + + values = tbmsg[tbname][0]["values"]; + + // if we receive data from twilight sensor, we just send it to dido_controller + if(values.hasOwnProperty('twilight_sensor')) + { + delete values["status"]; + instance.send(instanceSendTo.di_do_controller, {sender: "modbus_citysys", tbdata: values}); + return; + } + + //console.log("modbus", Object.keys(values)); + + //sum Phase_1_power, Phase_2_power, Phase_3_power (if one of them is undefined, we handle it) + const numOr0 = n => isNaN(n) ? 0 : n; + let calculated_total_power = [values["Phase_1_power"], values["Phase_2_power"], values["Phase_3_power"]].reduce((a, b) => numOr0(a) + numOr0(b)); + values["total_power"] = parseFloat(calculated_total_power.toFixed(2)); + tbmsg[tbname][0]["values"] = values; + + Object.keys(values).map(singleValue => { + if (["Phase_1_voltage", "Phase_2_voltage", "Phase_3_voltage"].includes(singleValue)) + { + + let l = singleValue.split("_"); + let phase = parseInt(l[1]); + + if(FLOW.OMS_no_voltage == undefined) FLOW.OMS_no_voltage = new Set(); + + if(values[singleValue] == 0) + { + sendNotification("modbus_citys: updateDataInTB", tbname, "no_voltage_detected_on_phase", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase ); + + FLOW.OMS_no_voltage.add(phase); + } + else + { + FLOW.OMS_no_voltage.delete(phase); + sendNotification("modbus_citys: updateDataInTB", tbname, "voltage_on_phase_has_been_restored", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase); + } + + } + }) + + sendThingsBoard(tbmsg); + } + } + + + let electrometerNotResponding = 0; + function sendCommand(){ + let device = PRIVATEVARS.devices[PRIVATEVARS.device_index]; + let com = device.cmd[PRIVATEVARS.cmd_index]; + let array = [device.address, 3, ((com.register >> 8) & 0xFF), (com.register & 0xFF), ((com.size >> 8) & 0xFF), (com.size & 0xFF)]; + array = modbusCRC(array); + + //console.log('---device--', device); + //console.log('---device type--', device.type); + + //sendError("sendCommand", device.tb_name, ERRWEIGHT.DEBUG, "Sending command !", {"cmd": array}); + //console.log("sendCommand", device.tb_name, ERRWEIGHT.DEBUG, "Sending command !", {"cmd": array}); + + // let tbname = FLOW.OMS_edgeName; + // if(tbname == "" || tbname === undefined ) + // { + // console.log("!!!!!!FLOW.OMS_edgeName is empty - 2"); + // return; + // } + + startTimeoutInterval(); + port.write(Buffer.from(array), function(err) { + + //! poslany command + //console.log("poslany tento commant", array, err, device.type); + + if (err) { + stopTimeoutInterval(); + stopCmdWaitInterval(); + + + // elektromer neodpoveda viac ako 5 minut (15 commands za minutu sa posiela) + if (device.type === "EM111" || device.type === "EM340") + { + electrometerNotResponding++; + + if (electrometerNotResponding > 15 && electrometerNotResponding < 17) + { + + //sendError("Modbus_citys: sendCommand", tbname, ERRWEIGHT.CRITICAL, "Electrometer is not responding", {"err": err.message, "info": "No response more than 5 minutes"}); + sendNotification("modbus_citys: sendCommand", tbname, "electrometer_is_not_responding", {}, {"err": err.message, "info": "No response more than 5 minutes"}, instanceSendTo.tb, instance ); + + let tbmsg = { + [tbname]: [ + { + "ts": Date.now(), + "values": { + "status": "NOK" + } + } + ] + } + + sendThingsBoard(tbmsg) + } + } + + + return; + } + + if (device.type === "EM111") + { + if (electrometerNotResponding > 15) + { + //sendError("Modbus_citys: sendCommand", tbname, ERRWEIGHT.NOTICE, "Electrometer is responding again", ""); + sendNotification("modbus_citys: sendCommand", tbname, "electrometer_is_responding_again", {}, "", instanceSendTo.tb, instance ); + } + electrometerNotResponding = 0; + + } + }); + } + + function modbusCRC(array){ + let crc = 0xFFFF; + for (let i = 0; i < array.length; i++){ + let b = array[i]; + crc = crc ^ b; + + for (let j = 8; j>0; j--){ + if ((crc & 0x0001) != 0){ + crc = crc >> 1; + crc = crc ^ 0xA001; + } else { + crc = crc >> 1; + } + } + } + + array.push(crc & 0xFF); + array.push((crc >> 8) & 0xFF); + + return array; + } + + + instance.on('data', function(flowdata) { + + console.log("flowdata on data", flowdata); + sendStatus({"CONFIG": instance.CONFIG, "PRIVATEVARS":PRIVATEVARS}) + + }); + + instance.reconfigure = function() { + + //TODO remove ftom options + myEdge = instance.options.edge; + if (starter === null){ + starter = setInterval(function(){ + loadData(); + clearInterval(starter); + starter = null; + }, 5000); + } + }; + + instance.close = function() { + // close sockets and such + }; + + function sendError(func, device, weight, str, extra){ + if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){ + return; // Allow debug messages only if CONFIG.debug is active + } + + let content = { + "type": weight, + "status": "new", + "source": { + "func":func, + "component":instance.id, + "component_name":instance.name, + "edge":myEdge + }, + "message":str, + "message_data": extra + }; + let msg = {}; + msg[device] = [ + { + "ts": Date.now(), + "values": { + "_event":content + } + } + ]; + + // Msg can be outputted from components only after configuration + /*if (canSendErrData()){ + sendBufferedErrors(); + } else { + bufferError(msg); + }*/ + instance.send(instanceSendTo.tb, msg); // Even if error server is unavailable, send this message to output, for other possible component connections + + + + function sendBufferedErrors(){ + if (PRIVATEVARS.errBuffer === undefined){ + console.log("errBuffer undefined"); + console.log("private: ", PRIVATEVARS); + } + console.log("errBuffer size: ", PRIVATEVARS.errBuffer.length); + if (PRIVATEVARS.errBuffer.length > 0){ + for (let i = 0; i < PRIVATEVARS.errBuffer.length; i++){ + instance.send(instanceSendTo.error, PRIVATEVARS.errBuffer[i]); + } + PRIVATEVARS.errBuffer = []; //Clear the buffer + saveData(); + } + } + + function bufferError(msg){ + PRIVATEVARS.errBuffer.push(msg); + saveData(); + } + } + + function canSendErrData(){ + //if (FLOW.errServerAvailable) + return true; + //else + // return false; + } + + function sendStatus(str){ + instance.send(instanceSendTo.debug, str); + } + + function sendThingsBoard(obj){ + // Msg can be outputted from components only after configuration + /*if (canSendTbData()){ + sendBufferedTB(); + } else { + console.log("cant send data"); + bufferTB(str); + }*/ + //console.log("send thingsboard", str); + + //console.log("FLOW.OMS_edgeName", FLOW.OMS_edgeName, obj); + + if(obj.hasOwnProperty(FLOW.OMS_edgeName) && FLOW.OMS_edgeName != "") + { + //send it to di_do_controller + instance.send(instanceSendTo.di_do_controller, {sender: "modbus_citysys", tbdata: obj}); + } + // else + { + instance.send(instanceSendTo.tb, obj); // Even if TB server is unavailable, send this message to output, for other possible component connections + } + + //instance.send(2, str); // Even if TB server is unavailable, send this message to output, for other possible component connections + + + function sendBufferedTB(){ + if (PRIVATEVARS.tbBuffer.length > 0){ + console.log("sending buffered: ", PRIVATEVARS.tbBuffer.length ); + for (let i = 0; i < PRIVATEVARS.tbBuffer.length; i++){ + instance.send(instanceSendTo.tb, PRIVATEVARS.tbBuffer[i]); + } + PRIVATEVARS.tbBuffer = []; //Clear the buffer + saveData(); + } + } + + function bufferTB(str){ + PRIVATEVARS.tbBuffer.push(str); + saveData(); + } + } + + function canSendTbData(){ + //if (FLOW.tbAvailable) + return true; + //else + // return false; + } + + function startTimeoutInterval(){ + if (!timeoutInterval){ + timeoutInterval = setInterval(readingTimeouted, instance.CONFIG.timeoutTime); + } + } + + function stopTimeoutInterval(){ + if (timeoutInterval){ + clearInterval(timeoutInterval); + timeoutInterval = null; + } + } + + function startCmdWaitInterval(){ + if (!msgWaitInterval){ + msgWaitInterval = setInterval(readDeviceData, instance.CONFIG.msgWaitTime); + } + } + + function stopCmdWaitInterval(){ + if (msgWaitInterval){ + clearInterval(msgWaitInterval); + msgWaitInterval = null; + } + } + + instance.on('options', instance.reconfigure); + instance.reconfigure(); + + // LAST SECTION FOR COMMON FUNCTIONS + function humanReadableTimeAndDate(){ + let date_ob = new Date(); + + let date = ("0" + date_ob.getDate()).slice(-2); + let month = ("0" + (date_ob.getMonth() + 1)).slice(-2); + let year = date_ob.getFullYear(); + + let hours = ("0" + date_ob.getHours()).slice(-2); + let minutes = ("0" + date_ob.getMinutes()).slice(-2); + let seconds = ("0" + date_ob.getSeconds()).slice(-2); + + return date+"."+month+"."+year+" "+hours+":"+minutes+":"+seconds; + } + + if (starter === null){ + starter = setInterval(function(){ + loadData(); + clearInterval(starter); + starter = null; + }, 5000); + } + + //setTimeout(loadData, 5000); +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js deleted file mode 100644 index d21839d..0000000 --- a/flow/modbus_reader.js +++ /dev/null @@ -1,336 +0,0 @@ -exports.id = 'modbus_reader'; -exports.title = 'Modbus reader'; -exports.version = '2.0.0'; -exports.group = 'Worksys'; -exports.color = '#2134B0'; -exports.output = ["red", "white"]; -exports.click = false; -exports.author = 'Rastislav Kovac'; -exports.icon = 'bolt'; -exports.readme = ` - Modbus requests to modbus devices (electromer, twilight sensor, thermometer. - Component keeps running arround deviceConfig array in "timeoutInterval" intervals. Array items are objects with single modbus devices. - Everything is sent to dido_controller. If requests to device fail (all registers must fail to send NOK status) , we send "NOK-'device'" status to dido_controller. - This device needs to be configured in dido_controller!!! Double check if it is. In dido_controller we calculate final status and all values with status are pushed to tb. -`; - -const modbus = require('jsmodbus') -const SerialPort = require('serialport') - -const { timeoutInterval, deviceConfig } = require("../databases/modbus_config"); - -const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler'); -const errorHandler = new ErrorToServiceHandler(); - -const { sendNotification } = require('./helper/notification_reporter'); - -const instanceSendTo = { - debug: 0, - dido_controller: 1, -}; - -//to handle NOK and OK sendNotifications s -const numberOfNotResponding = {}; -let tbName = null; - - -exports.install = function(instance) { - - class SocketWithClients { - - constructor () { - this.stream = null; - this.socket = null; - this.clients = {}; - this.allValues = {}; - this.errors = 0; - this.index = 0; - this.timeoutInterval = 5000; - - // kedze potrebujeme ist stale dookola pre jednotlive zariadenia, potrebujeme ci uz index ako aj adresu zariadenia, a aj pocet registrov na vycitanie - this.deviceAddress = null; // adresa zariadenia (1 ma EM340 a 2 ma twilight_sensor) - this.indexInDeviceConfig = 0; // prvy item v deviceConfig - this.lengthOfActualDeviceStream = null; - this.device = null; - - this.startSocket(); - } - - startSocket = () => { - - let obj = this; - - this.socket = new SerialPort("/dev/ttymxc0", { - baudRate: 9600, - }) - - // we create a client for every deviceAddress ( = address) in list and push them into dictionary - for( let i = 0; i < deviceConfig.length; i++) - { - this.clients[deviceConfig[i].deviceAddress] = new modbus.client.RTU(this.socket, deviceConfig[i].deviceAddress); - } - - this.socket.on('error', function(e) { - console.log('socket connection error', e); - if(e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') { - console.log(exports.title + ' Waiting 10 seconds before trying to connect again'); - setTimeout(obj.startSocket, 10000); - } - }); - - this.socket.on('close', function() { - console.log('Socket connection closed ' + exports.title + ' Waiting 10 seconds before trying to connect again'); - setTimeout(obj.startSocket, 10000); - }); - - this.socket.on('open', function () { - console.log("socket connected"); - obj.getActualStreamAndDevice(); - obj.timeoutInterval = timeoutInterval; - }) - - }; - - getActualStreamAndDevice = () => { - const dev = deviceConfig[this.indexInDeviceConfig]; - this.index = 0; - this.errors = 0; - this.stream = dev.stream; - this.lengthOfActualDeviceStream = dev.stream.length; - this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number - this.device = dev.device; //em340, twilight_sensor - - if(this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval); - else setTimeout(this.readRegisters, 2000); - } - - readRegisters = () => { - - const str = this.stream[this.index]; - const register = str.register; - const size = str.size; - const tbAttribute = str.tbAttribute; - - let obj = this; - - this.clients[this.deviceAddress].readHoldingRegisters(register, size) - .then( function (resp) { - - resp = resp.response._body.valuesAsArray; //resp is array of length 1 or 2, f.e. [2360,0] - // console.log(deviceAddress, register, tbAttribute, resp); - - //device is responding again after NOK status - if(numberOfNotResponding.hasOwnProperty(obj.device)) - { - let message = ""; - if(obj.device == "em340") - { - message = "electrometer_ok"; - } - else if(obj.device == "twilight_sensor") - { - message = "twilight_sensor_ok"; - } - message && sendNotification("modbus_reader: readRegisters", tbName, message, {}, "", instanceSendTo.tb, instance); - delete numberOfNotResponding[obj.device]; - } - - obj.transformResponse(resp, register, obj.deviceAddress); - - obj.error = 0; - obj.index++; - - if(obj.index < obj.lengthOfActualDeviceStream) setTimeout(obj.readRegisters, 0); - else obj.setNewStream(); - - }).catch (function () { - - console.log("error pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute); - - obj.error++; - if(obj.error == obj.lengthOfActualDeviceStream) - { - instance.send(instanceSendTo.dido_controller, {status: "NOK-" + obj.device}); // NOK-em340, NOK-em111, NOK-twilight_sensor, NOK-thermometer - - //todo - neposlalo notification, ked sme vypojili twilight a neposle to do tb, ale do dido ?? - if(!numberOfNotResponding.hasOwnProperty(obj.device)) - { - let message = ""; - if(obj.device == "twilight_sensor") - { - message = "twilight_sensor_nok"; - } - else if(obj.device == "em340") - { - message = "electrometer_nok"; - } - message && sendNotification("modbus_reader: readingTimeouted", tbName, message, {}, "", instanceSendTo.tb, instance); - numberOfNotResponding[obj.device] = 1; - } - - obj.error = 0; - numberOfNotResponding[obj.device] += 1; - } - - console.error(require('util').inspect(arguments, { - depth: null - })) - - obj.index++; - if(obj.index < obj.lengthOfActualDeviceStream) setTimeout(obj.readRegisters, 0); - else obj.setNewStream(); - }) - - }; - - transformResponse = (response, register, deviceAddress) => { - - for (let i = 0; i < this.lengthOfActualDeviceStream; i++) { - - let a = this.stream[i]; - if (a.register === register) - { - let tbAttribute = a.tbAttribute; - let multiplier = a.multiplier; - - let value = this.calculateValue(response, multiplier); - // console.log(deviceAddress, register, tbName, tbAttribute, response, a.multiplier, value); - - // if(tbName == undefined) return; - - if(this.index + 1 + this.errors < this.lengthOfActualDeviceStream) - { - this.allValues[tbAttribute] = value; - return; - } - - const values = { - ...this.allValues, - [tbAttribute]: value, - }; - - this.checkNullVoltage(values); - - instance.send(instanceSendTo.dido_controller, {values: values}); - - this.allValues = {}; - break; - } - - } - - } - - setNewStream = () => - { - // console.log('------------',this.lengthOfActualDeviceStream, this.index); - // console.log('------------',this.indexInDeviceConfig, deviceConfig.length); - if(this.lengthOfActualDeviceStream == this.index) - { - if(this.indexInDeviceConfig + 1 == deviceConfig.length) - { - this.indexInDeviceConfig = 0; - } - else - { - this.indexInDeviceConfig += 1; - } - - this.getActualStreamAndDevice(); - } - } - - - // sendFinalObjects = (values) => - // { - - // const date = Date.now(); - // // values["status"] = "OK"; - - // const dataToTB = { - // [tbName]: [ - // { - // "ts": date, - // "values": values - // } - // ] - // }; - - // instance.send(instanceSendTo.tb, dataToTB); - - // const dataToDiDo = { - // values: values - // } - - // instance.send(instanceSendTo.dido_controller, dataToDiDo); - // } - - - calculateValue = (response, multiplier) => - { - let value = 0; - - let l = response.length; - if (l === 2) - { - value = (response[1]*(2**16) + response[0]); - - if(value >= (2**31)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x80000000), ak vieš robiť logický súčin - { - value = value - "0xFFFFFFFF" + 1; - } - } - else if (l === 1) - { - value = response[0]; - - if(value >= (2**15)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x8000), ak vieš robiť logický súčin - { - value = value - "0xFFFF" + 1; - } - } - - return Math.round(value * multiplier * 10) / 10; - } - - - checkNullVoltage = (values) => { - - if(!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return; - - Object.keys(values).map(singleValue => { - if (["Phase_1_voltage", "Phase_2_voltage", "Phase_3_voltage"].includes(singleValue)) - { - let l = singleValue.split("_"); - let phase = parseInt(l[1]); - - if(FLOW.OMS_no_voltage == undefined) FLOW.OMS_no_voltage = new Set(); - // console.log(values[singleValue], tbName); - - if(values[singleValue] == 0) - { - FLOW.OMS_no_voltage.add(phase); - sendNotification("modbus_citys: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase ); - // console.log('no voltage') - } - else - { - FLOW.OMS_no_voltage.delete(phase); - // console.log('voltage detected') - sendNotification("modbus_citys: checkNullVoltage", tbName, "voltage_on_phase_restored", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase); - } - } - }) - } - - // we use dataToTbHandler. Therefore we need to check, if objects we send to dido_controller are not empty - isObjectEmpty = (objectName) => { - return Object.keys(objectName).length === 0 && objectName.constructor === Object; - } - } - - setTimeout(() => { - const newSocket = new SocketWithClients(); - tbName = FLOW.OMS_rvo_tbname; - }, 25000); -} diff --git a/flow/thermometer.js b/flow/thermometer.js deleted file mode 100644 index 1a5714f..0000000 --- a/flow/thermometer.js +++ /dev/null @@ -1,220 +0,0 @@ -exports.id = 'thermometer'; -exports.title = 'Thermometer'; -exports.group = 'Worksys'; -exports.color = '#5CB36D'; -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 instanceSendTo = { - debug: 0, - tb: 1, - dido_controller: 2 -} - -//read temperature - frequency -let timeoutMin = 5;//minutes - -var path = require('path'); -var log4js = require("log4js"); - -log4js.configure({ - appenders: { - errLogs: { type: 'file', filename: path.join(__dirname + "/../", 'err.txt') }, - monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, - console: { type: 'console' } - }, - categories: { - errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, - monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, - //another: { appenders: ['console'], level: 'trace' }, - default: { appenders: ['console'], level: 'trace' } - } -}); - -const errLogger = log4js.getLogger("errLogs"); -const logger = log4js.getLogger(); -const monitor = log4js.getLogger("monitorLogs"); - -//logger.debug("text") -//monitor.info('info'); -//errLogger.error("some error"); - -const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper'); -const dbSettings = TABLE("settings"); -let temperatureAddress = ""; - -async function loadSettings() -{ - //todo global FLOW.OMS_edgeName is making problem, so we load it here as well, it should not be - let responseSettings = await promisifyBuilder(dbSettings.find()); - temperatureAddress = responseSettings[0]["temperature_adress"]; -} - -loadSettings(); - - -exports.install = function(instance) { - - const { exec } = require('child_process'); - const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter'); - - let startRead; - let dataToTb; - let counter = 0; - - let edgeName = ""; - - - logger.debug(exports.title, "installed"); - - instance.on("close", function(){ - clearInterval(startRead); - }) - - - const start = function() { - - try { - - if(FLOW.OMS_controller_type === "unipi") - { - clearInterval(startRead); - return; - } - - if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined"; - - logger.debug("FLOW.OMS_temperature_adress", FLOW.OMS_temperature_adress); - - exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => { - - edgeName = FLOW.OMS_edgeName; - - if(edgeName !== "") - { - if(error) - { - - if(FLOW.OMS_brokerready == undefined) - { - logger.debug("gettemparature - FLOW.OMS_brokerready is undefined"); - - setTimeout(function(){ - start(); - }, 3000); - - return; - } - - if(FLOW.OMS_brokerready) - { - //sendNotification("start", edgeName, ERRWEIGHT.WARNING, "Thermometer is not responding", {"Error": error}, instanceSendTo.tb, instance, "thermometer"); - sendNotification("start", edgeName, "thermometer_is_not_responding", {}, {"Error": error}, instanceSendTo.tb, instance, "thermometer"); - } - - let status = "NOK"; - dataToTb = { - [edgeName]: [ - { - "ts": Date.now(), - "values": { - "status": status - } - } - ] - } - - monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready); - - // instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ?? - instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"}); - } - else parseData(stdout); - } - else - { - monitor.info("gettemperature: edgeName is not defined", FLOW.OMS_edgeName); - - setTimeout(function(){ - start(); - }, 3000); - - return; - } - - - //instance.send({"Temp":stdout,"stderr":stderr,"err":error}); - }); - - } - catch(err) { - errLogger.error(exports.title, err); - } - } - - const parseData = function(data) { - - data = parseFloat(data); - - logger.debug("gettemperature", data); - - if(!isNaN(data)) { - - if(counter > 290) - { - instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); - - //sendNotification("parseData", edgeName, ERRWEIGHT.NOTICE, "Thermometer is working again", "", instanceSendTo.tb, instance, "thermometer"); - if(FLOW.OMS_brokerready) sendNotification("parseData", edgeName, "thermometer_is_responding_again", {}, "", instanceSendTo.tb, instance, "thermometer"); - } - - logger.debug("gettemperature", data); - const values = { - "temperature": Number(data.toFixed(2)), - "status": "OK" - } - - dataToTb = { - [edgeName]: [ - { - "ts": Date.now(), - "values":values - } - ] - } - - instance.send(instanceSendTo.tb, dataToTb); - instance.send(instanceSendTo.dido_controller, values); - - 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", edgeName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", instanceSendTo.tb, instance, "thermometer"); - sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer"); - - instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); - instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"}); - } - - } - - } - - setTimeout(function(){ - start(); - }, 15000); - - startRead = setInterval(start, timeoutMin * 1000 * 60); - -}; \ No newline at end of file diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index 794fedc..54c6cbc 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -187,7 +187,7 @@ exports.install = function(instance) { let mqtt_clientid = responseSettings[0]["mqtt_clientid"]; let mqtt_username = responseSettings[0]["mqtt_username"]; let mqtt_port = responseSettings[0]["mqtt_port"]; - + console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]); opts = { @@ -204,7 +204,7 @@ exports.install = function(instance) { } connectToTbServer(); - } + } function connectToTbServer() { @@ -217,16 +217,16 @@ exports.install = function(instance) { instance.status("Connected", "green"); monitor.info("MQTT broker connected"); - brokerready = true; + brokerready = true; FLOW.OMS_brokerready = brokerready; - wsmqtt_status = 'connected'; + wsmqtt_status = 'connected'; }); broker.on('reconnect', function() { instance.status("Reconnecting", "yellow"); brokerready = false; - FLOW.OMS_brokerready = brokerready; + FLOW.OMS_brokerready = brokerready; }); broker.on('message', function(topic, message) { @@ -245,6 +245,7 @@ exports.install = function(instance) { } instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message }); + }); broker.on('close', function(err) { diff --git a/package.json b/package.json index e524d7c..2230755 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,10 @@ "dependencies": { "bitwise": "^2.1.0", "easy-crc": "0.0.2", - "jsmodbus": "^4.0.6", "log4js": "^6.3.0", "mqtt": "^4.2.6", - "nodemailer": "^6.9.7", "serialport": "^9.2.8", - "total.js": "^3.4.13" + "total.js": "^3.4.5" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/saved_data/modbus_settings b/saved_data/modbus_settings new file mode 100644 index 0000000..4ed37d8 --- /dev/null +++ b/saved_data/modbus_settings @@ -0,0 +1,174 @@ +{ + "config":{ + "isRunning":false, + "debug":true, + "timeoutTime":4000, + "msgWaitTime":17000, + "port":"/dev/ttymxc0", + "port_options":"stty -F /dev/ttymxc0 9600 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke" + }, + "private":{ + "errBuffer":[ + + ], + "tbBuffer":[ + + ], + "device_index":0, + "cmd_index":0, + "devices":[ + { + "name":"Elektrometer 1", + "tb_name":"", + "type":"EM340", + "address":1, + "data":[ + + ], + "cmd":[ + + ], + "timeoutcount":0, + "status":"virtual" + }, + { + "name":"Twilight sensor", + "tb_name":"", + "type":"twilight", + "address":2, + "data":[ + + ], + "cmd":[ + + ], + "timeoutcount":0, + "status":"virtual" + } + ], + "cmd_tables":[ + { + "type":"EM340", + "cmd":[ + { + "name":"Voltage L1", + "tb_name":"Phase_1_voltage", + "register":0, + "size":2, + "multiplier":0.1 + }, + { + "name":"Voltage L2", + "tb_name":"Phase_2_voltage", + "register":2, + "size":2, + "multiplier":0.1 + }, + { + "name":"Voltage L3", + "tb_name":"Phase_3_voltage", + "register":4, + "size":2, + "multiplier":0.1 + }, + { + "name":"Current L1", + "tb_name":"Phase_1_current", + "register":12, + "size":2, + "multiplier":0.001 + }, + { + "name":"Current L2", + "tb_name":"Phase_2_current", + "register":14, + "size":2, + "multiplier":0.001 + }, + { + "name":"Current L3", + "tb_name":"Phase_3_current", + "register":16, + "size":2, + "multiplier":0.001 + }, + { + "name":"Power L1", + "tb_name":"Phase_1_power", + "register":18, + "size":2, + "multiplier":0.1 + }, + { + "name":"Power L2", + "tb_name":"Phase_2_power", + "register":20, + "size":2, + "multiplier":0.1 + }, + { + "name":"Power L3", + "tb_name":"Phase_3_power", + "register":22, + "size":2, + "multiplier":0.1 + }, + { + "name":"Power tot", + "tb_name":"total_power", + "register":40, + "size":2, + "multiplier":0.1 + }, + { + "name":"Energy in", + "tb_name":"total_energy", + "register":52, + "size":2, + "multiplier":0.1 + }, + { + "name":"PowF L1", + "tb_name":"Phase_1_pow_factor", + "register":46, + "size":1, + "multiplier":0.001 + }, + { + "name":"PowF L2", + "tb_name":"Phase_2_pow_factor", + "register":47, + "size":1, + "multiplier":0.001 + }, + { + "name":"PowF L3", + "tb_name":"Phase_3_pow_factor", + "register":48, + "size":1, + "multiplier":0.001 + }, + { + "name":"PowF", + "tb_name":"power_factor", + "register":49, + "size":1, + "multiplier":0.001 + } + ] + }, + { + "type":"twilight", + "cmd":[ + { + "name":"Twilight", + "tb_name":"twilight_sensor", + "register":60, + "size":2, + "multiplier":1 + } + ] + } + ] + } +}