exports.id = 'cmd_manager'; exports.title = 'CMD Manager'; exports.group = 'Worksys'; exports.color = '#5D9CEC'; exports.version = '0.0.4'; exports.output = ['red', 'blue', 'yellow', 'blue', 'white']; //blue - send message to relays exports.input = true; exports.author = 'Daniel Segeš'; exports.icon = 'cloud-upload'; //exports.npm = ['serialport' , 'child_process']; exports.html = `
RPC - run RPC calls

@(User)
@(Password)
@(My edge)
`; exports.readme = `Manager for CMD calls`; const SerialPort = require('serialport'); const { exec } = require('child_process'); const { crc8, crc16, crc32 } = require('easy-crc'); const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js'); const { bytesToInt, longToByteArray, addZeroBefore, isEmptyObject, convertUTCDateToLocalDate } = require('./helper/utils'); const bitwise = require('bitwise'); var SunCalc = require('./helper/suncalc.js'); const DataToTbHandler = require('./helper/DataToTbHandler.js'); const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js'); const { promisifyBuilder } = require('./helper/db_helper.js'); const { sendNotification, initNotifications, ERRWEIGHT } = require('./helper/notification_reporter.js'); //https://github.com/log4js-node/log4js-node/blob/master/examples/example.js //file: { type: 'file', filename: path.join(__dirname, 'log/file.log') } var path = require('path'); var log4js = require("log4js"); const process = require('process'); log4js.configure({ appenders: { errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') }, monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') }, console: { type: 'console' } }, categories: { errLogs: { appenders: ['console', 'errLogs'], level: 'error' }, monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' }, //another: { appenders: ['console'], level: 'trace' }, default: { appenders: ['console'], level: 'trace' } } }); const errLogger = log4js.getLogger("errLogs"); const logger = log4js.getLogger(); const monitor = log4js.getLogger("monitorLogs"); //USAGE //logger.debug("text") //monitor.info('info'); //errLogger.error("some error"); //load from settings let latitude = 48.70826502;//48.682255758; let longitude = 17.28455203;//17.278910807; const gmtOffset = 0; //ak nie je nastaveny //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ //https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates //set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime //priorities for registers let priorities = []; let minutes = 1; priorities["0"] = minutes; priorities["1"] = minutes; minutes = 5; priorities["74"] = minutes; priorities["75"] = minutes; priorities["76"] = minutes; priorities["77"] = minutes; priorities["78"] = minutes; priorities["79"] = minutes; priorities["84"] = minutes; minutes = 10; priorities["87"] = minutes; priorities["6"] = minutes; priorities["7"] = minutes; priorities["80"] = minutes; priorities["8"] = minutes; priorities["3"] = minutes; priorities["89"] = minutes; //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89]; //1 - dimming const dbNodes = TABLE("nodes"); const dbRelays = TABLE("relays"); const dbSettings = TABLE("settings"); const errorHandler = new ErrorToServiceHandler(); let rotary_switch_state = "Off"; let lux_sensor; let state_of_breaker = {};//key is line, value is On/Off let disconnectedReport = {};//key is tbname, value true/false let relaysData = {};//key is line, value is data from db let nodesData = {};//key is node, value data from db //helper container for counting resolved group of commands (commands related to set profile) let cmdCounter = {};//key is node, value is counter let cmdNOKNodeCounter = {};//key is node, value is counter function cmdCounterResolve(address) { if(cmdCounter.hasOwnProperty(address)) { cmdCounter[address] = cmdCounter[address] - 1; let result = cmdCounter[address]; if(result == 0) delete cmdCounter[address]; return result; } return -1; } function getParams(priority) { let params = {}; //core rpc values params.address = 0;//if(recipient === 0) address = 0; params.byte1 = 0;//msb, podla dokumentacie data3 params.byte2 = 0;//podla dokumentacie data2 params.byte3 = 0;//podla dokumentacie data1 params.byte4 = 0;//lsb, podla dokumentacie data0 params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast params.register = -1;//register number params.rw = 0;//0: read, 1: write //other values //params.type = "cmd"; "relay" "cmd-terminal" //params.tbname = tbname; params.priority = priorityTypes.node_cmd;//default priority params.timestamp = 0;//execution time if(priority != undefined ) { params.timestamp = priority; params.priority = priority; } params.addMinutesToTimestamp = 0;//repeat if > 0, //params.isDusk = false; //params.isDawn = false; //params.info = ""; return params; } async function loadSettings() { let responseSettings = await promisifyBuilder(dbSettings.find()); latitude = responseSettings[0]["latitude"]; longitude = responseSettings[0]["longitude"]; //globals FLOW.OMS_language = responseSettings[0]["lang"]; FLOW.OMS_rvo_name = responseSettings[0]["rvo_name"]; FLOW.OMS_projects_id = responseSettings[0]["projects_id"]; //FLOW.OMS_rvo_tbname = responseSettings[0]["tbname"]; FLOW.OMS_temperature_adress = responseSettings[0]["temperature_adress"]; FLOW.OMS_controller_type = responseSettings[0]["controller_type"]; FLOW.OMS_serial_port = responseSettings[0]["serial_port"]; //logger.log("", "settings", responseSettings[0], "-------------------------------------"); logger.debug('settings', responseSettings[0]); //FLOW.OMS_tem //rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number initNotifications(); } //nastav profil nodu function processNodeProfile(node) { if(rotary_switch_state != "Automatic") { logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); return; } let nodeObj = nodesData[node]; let line = nodeObj.line; if(relaysData[line].contactor == 0) { logger.debug("line line is off", line, node); return; } if(nodeObj.processed == 1) { logger.debug("node was already processed", node); return; } let profile = nodeObj.profile; logger.debug("processNodeProfile: start - set profile for ", node, profile); let nodeProfile; try{ nodeProfile = JSON.parse( profile ); if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined"); } catch (error) {} //test reset profilu //nodeProfile = undefined; logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); //return; //let timestamp = priorityTypes.node_cmd; //let now = new Date(); //now.setSeconds(now.getSeconds() + 10); //let timestamp = now.getTime(); let timestamp = priorityTypes.node_cmd; //nodeProfile = undefined; removeTask({type: "set_node_profile", address: node}); cmdNOKNodeCounter[node] = 0; //co ked sa prave spracovava? //if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false; if(nodeProfile === undefined) { //vypneme profil nodu, posleme cmd //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia logger.debug("turn off profile"); let params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; params.byte1 = 0; params.byte2 = 0; params.byte3 = 0; params.byte4 = 32; params.recipient = 1; params.register = 8; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = 'turn off/reset node profile'; cmdCounter[node] = 1; tasks.push(params); //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", instanceSendTo.tb, instance ); } else { let tasksProfile = []; //cmdCounter[node] = tasksProfile.length; //tasks.push(tasksProfile); //let timestamp = priorityTypes.node_cmd; //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; params.byte1 = 0; params.byte2 = 0; params.byte3 = 0; params.byte4 = 32; params.recipient = 1; params.register = 8; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = 'turn off node profile'; tasksProfile.push(params); timestamp++; logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); //TS1 Time point a TS1 Time Point Levels let register = 9; for(let i = 0; i < nodeProfile.intervals.length; i++) { let obj = nodeProfile.intervals[i]; //let timePoint = obj.time_point; let dim_value = obj.value; //Reg 9 až Reg 40 /* Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. Časový bod má formát: Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované Register úrovne má rovnaký formát ako dimming register (Reg 1). */ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //params.byte1 = 0;//msb, podla dokumentacie data3 //params.byte2 = 0;//podla dokumentacie data2 //params.byte3 = 0;//podla dokumentacie data1 //params.byte4 = 0;//lsb, podla dokumentacie data0 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! let start_time = obj.start_time; let t = start_time.split(":"); //if(timePoint != undefined) t = timePoint.split(":"); //else t = [0,0]; logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; params.byte1 = parseInt(t[0]);//hh params.byte2 = parseInt(t[1]);//mm params.byte3 = 0;//ss params.byte4 = 0;// params.recipient = 1; params.register = register; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = 'TS1 Time point ' + (i + 1); tasksProfile.push(params); register++; timestamp++; params = getParams(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.recipient = 1; params.register = register; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = 'TS1 Time point Levels ' + (i + 1); tasksProfile.push(params); register++; timestamp++; } //Threshold lux level for DUSK/DAWN //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //params.byte1 = 0;//msb, podla dokumentacie data3 //params.byte2 = 0;//podla dokumentacie data2 //params.byte3 = 0;//podla dokumentacie data1 //params.byte4 = 0;//lsb, podla dokumentacie data0 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //Time schedule settings na koniec //if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor) { logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); let params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; params.register = 96; params.recipient = 1; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = "Threshold lux level for DUSK/DAWN"; if(nodeProfile.dusk_lux_sensor) { let v = nodeProfile.dusk_lux_sensor_value; let ba = longToByteArray(v); params.byte1 = ba[1];//msb params.byte2 = ba[0]; } if(nodeProfile.dawn_lux_sensor) { let v = nodeProfile.dawn_lux_sensor_value; let ba = longToByteArray(v); params.byte3 = ba[1];//msb params.byte4 = ba[0]; } tasksProfile.push(params); timestamp++; } //DUSK/DAWN max. adjust period { logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); let params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; params.register = 97; params.recipient = 1; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = "DUSK/DAWN max. adjust period"; if(nodeProfile.astro_clock) { let v = nodeProfile.dusk_lux_sensor_time_window; let ba = longToByteArray(v); params.byte1 = ba[1];//msb params.byte2 = ba[0]; } if(nodeProfile.astro_clock) { let v = nodeProfile.dawn_lux_sensor_time_window; let ba = longToByteArray(v); params.byte3 = ba[1];//msb params.byte4 = ba[0]; } tasksProfile.push(params); timestamp++; } //Static offset { //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: //Bity 0 – 6: hodnota v minútach //Bit 7: znamienko (1 – mínus) logger.debug("processNodeProfile: Static offset", node); let params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; params.register = 98; params.recipient = 1; params.rw = 1;//write params.timestamp = timestamp; params.addMinutesToTimestamp = 0; params.info = "Static offset"; if(nodeProfile.astro_clock) { let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); if(dusk_astro_clock_offset < 0) { params.byte3 = (dusk_astro_clock_offset * -1) + 128; } else { params.byte3 = dusk_astro_clock_offset; } if(dawn_astro_clock_offset < 0) { params.byte4 = (dawn_astro_clock_offset * -1) + 128; } else { params.byte4 = dawn_astro_clock_offset; } } tasksProfile.push(params); timestamp++; } logger.debug("Time schedule settings - turn on", node); params = getParams(priorityTypes.node_cmd); params.type = "set_node_profile"; params.address = node; params.register = 8; params.recipient = 1; params.rw = 1;//write //Time schedule settings let bits = []; //Byte 0 (LSB): //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). bits.push(1); //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) bits.push(0); bits.push(0); bits.push(0); if(nodeProfile.astro_clock == true) { //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý bits.push(1); } else bits.push(0); //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) bits.push(0); //Bity 6-7 - zatiaľ nepoužité bits.push(0); bits.push(0); params.byte4 = bitwise.byte.write(bits.reverse()); //Byte 2 – nastavenie pre lux senzor: bits = []; //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia if(nodeProfile.dusk_lux_sensor == true)//sumrak { bits.push(1); } else bits.push(0); //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia if(profile.dawn_lux_sensor == true)//usvit { bits.push(1); } else bits.push(0); //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. bits.push(0);//zatial neimplementovane //Bit 3 – 7 - nepoužité bits.push(0); bits.push(0); bits.push(0); bits.push(0); bits.push(0); params.byte2 = bitwise.byte.write(bits.reverse()); params.timestamp = timestamp; params.info = "Time schedule settings - turn on"; tasksProfile.push(params); //zaver cmdCounter[node] = tasksProfile.length; //tasks.push(tasksProfile); tasks = tasks.concat(tasksProfile); } logger.debug("finished set profile for ", node); } const instanceSendTo = { debug: 0, tb: 1, http_response: 2, di_do_controller: 3, infoSender: 4 } const priorityTypes = { terminal: 0, fw_detection: 1,//reserved only for FW detection - FLOW.OMS_masterNodeIsResponding high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) relay_profile: 3, node_broadcast: 4, node_profile: 5, node_cmd: 6 } let interval = null;//timeout for procesing tasks let refFlowdata = null;//holds reference to httprequest flowdata let refFlowdataObj = {}; function cleanUpRefFlowdataObj() { let now = new Date(); let timestamp = now.getTime(); //clear old refFlowdata references let keys = Object.keys(refFlowdataObj); for(let i = 0; i < keys.length; i++) { let timestampKey = keys[i]; if((timestamp - timestampKey) > 60*1000 ) { console.log("cleanUpRefFlowdataObj delete", timestampKey); delete refFlowdataObj[ timestampKey ]; } } } let tasks = [];//list of command calls to process function removeTask(obj) { let keys = Object.keys(obj); tasks = tasks.filter((task) => { let counter = 0; for(let i = 0; i < keys.length; i++) { let key = keys[i]; if(task.hasOwnProperty(key) && obj.hasOwnProperty(key)) { if(task[key] == obj[key]) counter++; } } if(counter == keys.length) return false; return true; }); } //TODO - to remove? const shortIterval = 10; const longInterval = 100; loadSettings(); exports.install = function(instance) { process.on('uncaughtException', function (err) { //TODO send to service errLogger.error('uncaughtException:', err.message) errLogger.error(err.stack); errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); //process.exit(1); }) //te();//force error const tbHandler = new DataToTbHandler(instanceSendTo.tb); tbHandler.setSender(exports.title); //FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name //const errorHandler = new ErrorToServiceHandler(instance, instanceSendTo.infoSender); errorHandler.setProjectsId(FLOW.OMS_projects_id); //const errorHandler = new ErrorToServiceHandler(instance); //errorHandler.sendMessageToService("ahoj", 0); async function loadRelaysData(line) { logger.debug("loadRelaysData", line); //ak zapiname liniu, mali by sme skontrolovat kde processed je false //nodes.table: node:number|tbname:string|line:number|profile:string|processed:boolean //vyselektujem vsetky nodes a spracujem profil return new Promise((resolve, reject) => { dbRelays.find().make(function(builder) { builder.callback(function(err, response) { if(err != null) reject(err); let relaysDataTmp = {}; for(let i = 0; i < response.length; i++) { let record = response[i]; let line = record["line"]; relaysDataTmp[ record["line"] ] = record; //porovname predchadzajuce hodnoty //ak record.contactor == 1, a aktualna hodnota record.contactor == 0 //to znamena, ze sa zmenil stav - linia bola vypnuta let prevData = relaysData[ record["line"] ]; //ugly but do not remove!!! relaysData[ record["line"] ] = record; let state = "";//on, off or empty (no change) if(prevData != undefined) { /* if(prevData.contactor == 1 && record.contactor == 0) { state = "off"; reportOfflineNodeStatus(line); } if(prevData.contactor == 0 && record.contactor == 1) { state = "on"; reportOnlineNodeStatus(line); } */ } else { //start flowu state = "start"; } if(line != undefined) { //ak sa jedna o update profilu linie - pozor di_co_controller posiela command pre loadRelaysData if(line != record["line"] ) continue; } //je zapnuta linia? contactor = 1 a processed = false, spracujeme profil if(record.contactor == 1) { //nespracovany profil, zapisem do nodu //rotary_switch_state = Automatic - profilu pre nody sa vykonavaju //ak je spracovany, a automatic - tak ho zapnem if(rotary_switch_state == "Automatic") { //prejs nodes - nacitame vsetky nody z pre danu liniu for (let k in nodesData) { //node:number|tbname:string|line:number|profile:string|processed:boolean //potrebujem nody k danej linii if(record.line == nodesData[k].line) { let node = nodesData[k].node; let processed = nodesData[k].processed; if(!processed) { processNodeProfile(node); } else{ //logger.debug( `node ${node} profile for line ${nodesData[k].line} was already processed`); } } } } else { logger.debug("unable to process profile - rotary_switch_state is", rotary_switch_state); } } } relaysData = {...relaysDataTmp}; resolve("OK"); }); }); //resolve(stdout); //reject(error); }) } function reportOnlineNodeStatus(line, newRotarySwitchState) { //broadcast cas, o 1-2 sek neskor - status, brightness //Po zapnutí línie broadcastovo aktualizovať predtým čas. logger.debug("--->reportOnlineNodeStatus for line", line); //return; //run broadcast //Actual time (3x reportujeme -> prvy krat hned, potom po 20 sekundach pre istotu) addMinutesToTimestamp = 0; let params = {}; let recipient = 2;//2 broadcast, address = 0 let address = 0;//0 if(recipient === 2) { address = 0xffffffff;//Broadcast } var d = new Date(); let hours = d.getHours(); let minutes = d.getMinutes(); let seconds = d.getSeconds(); params.address = address;//broadcast params.byte1 = hours;//h params.byte2 = minutes;//m params.byte3 = seconds;//s params.byte4 = 0; params.recipient = recipient; params.register = 87;//Actual time params.rw = 1;//write let timestampStart = priorityTypes.node_broadcast; //other values params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; tasks.push(params); params.timestamp = d.getTime() + 13000; tasks.push(params); params.timestamp = d.getTime() + 14000; tasks.push(params); if(newRotarySwitchState == 'Automatic') { // we execute 3x the same command (1st after 30seconds, 2nd 31seconsd, 3rd 32seconds) // cmd = WRITE: 255, 255, 255, 255, 255, 0, 8, 0, 0, 0, 17, 214, 34 // 4bytes address, 1byte recipient, 2 register, 4bytes data, 2bytes crc const timestamp = Date.now(); let params = getParams(priorityTypes.high_priority); params.type = "cmd"; // params.tbname = ''; params.address = 0xffffffff; //broadcast params.register = 8; //register na casove profily pre nody params.recipient = 2; //broadcast params.rw = 1; //write params.byte4 = 17; //decimal 17 params.timestamp = timestamp + 13000; params.info = 'znovuzapnutie profilov na svietidlach'; tasks.push(params); params.timestamp = timestamp + 14000; tasks.push(params); params.timestamp = timestamp + 15000; tasks.push(params); } if(newRotarySwitchState == 'Manual') { // we execute 3x the same command (1st after 30seconds, 2nd 31seconsd, 3rd 32seconds) // cmd = WRITE: 255, 255, 255, 255, 255, 0, 8, 0, 0, 0, 0, 218, 226 // 4bytes address, 1byte recipient, 2 register, 4bytes data, 2bytes crc const timestamp = Date.now(); let params = getParams(priorityTypes.high_priority); params.type = "cmd"; // params.tbname = ''; params.address = 0xffffffff; //broadcast params.register = 8; //register na casove profily pre nody params.recipient = 2; //broadcast params.rw = 1; //write params.timestamp = timestamp + 13000; params.info = 'vypnutie profilov na svietidlach'; tasks.push(params); params.timestamp = timestamp + 14000; tasks.push(params); params.timestamp = timestamp + 15000; tasks.push(params); // after this 3 cmd, new cmds are executed: // cmd = WRITE: 255, 255, 255, 255, 255, 0, 1, 0, 0, 0, 228, 144, 62 let newCmd = {...params}; newCmd.register = 1; newCmd.byte4 = 228; // all nodes to 100% newCmd.timestamp = timestamp + 17000; tasks.push(newCmd); newCmd.timestamp = timestamp + 18000; tasks.push(newCmd); newCmd.timestamp = timestamp + 19000; tasks.push(newCmd); } let sec = 20; setTimeout(function(){ //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel for (let k in nodesData) { //potrebujem nody k danej linii if(line == nodesData[k].line || line == undefined) { let tbname = nodesData[k].tbname; let node = nodesData[k].node; //prud, vykon - current, input power pre liniu pre vsetky nody //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 / brightness (after set dimming from platform)'; //params.debug = true; tasks.push(params); } //Prúd { let params = getParams(priorityTypes.high_priority); params.type = "cmd"; params.tbname = tbname; params.address = node; params.register = 75;//prud params.recipient = 1;//slave params.rw = 0;//read params.timestamp = priorityTypes.high_priority; params.info = 'read current (after set dimming from platform)'; //params.debug = true; tasks.push(params); } //výkon { let params = getParams(priorityTypes.high_priority); params.type = "cmd"; params.tbname = tbname; params.address = node; params.register = 76;//výkon params.recipient = 1;//slave params.rw = 0;//read params.timestamp = priorityTypes.high_priority; params.info = 'read power (after set dimming from platform)'; //params.debug = true; tasks.push(params); } } } },sec*1000); } function reportOfflineNodeStatus(line) { logger.debug("--->reportOfflineNodeStatus for line", line); values = {}; values["dimming"] = 0;//brightness values["power"] = 0;//výkon values["current"] = 0;//prúd values["status"] = "OFFLINE";//prúd for (let k in nodesData) { //potrebujem nody k danej linii if(line == nodesData[k].line || line == undefined) { let tbname = nodesData[k].tbname; //logger.debug("node:", tbname); let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); } } //report OFFLINE for line //relaysData[line].tbname; //values = {}; //values["status"] = "OFFLINE";//prúd } let now = new Date(); console.log("CMD Manager installed", now.toLocaleString("sk-SK")); //To disable NTP time synchronization, type the following command at the terminal. exec(`timedatectl set-ntp false`, (err, stdout, stderr) => { if (err || stderr) { console.error(err); console.log(stderr); monitor.info("failed timedatectl set-ntp", err, stderr); } else { exec(`timedatectl set-timezone "Europe/Bratislava"`, (err, stdout, stderr) => { if (err || stderr) { console.error(err); console.log(stderr); monitor.info("failed timedatectl set-timezone", err, stderr); } else { console.log("timedatectl set-timezone Europe/Bratislava"); } }); } }); function turnOnLine(line, info) { let obj = { line: line, command: "turnOn", info: info }; logger.debug("linia", line, obj); if(rotary_switch_state == 'Automatic') reportOnlineNodeStatus(undefined, 'Automatic'); instance.send(instanceSendTo.di_do_controller, obj); } function turnOffLine(line, info) { let obj = { line: line, command: "turnOff", info: info }; logger.debug("linia", line, obj); instance.send(instanceSendTo.di_do_controller, obj); } function detectIfResponseIsValid(bytes) { //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK let type = "RESPONSE"; if(bytes[4] == 0) type = "RESPONSE"; else if(bytes[4] == 1) type = "ERROR"; else if(bytes[4] == 2) type = "EVENT"; else type = "UNKNOWN"; let crc = crc16('ARC', bytes.slice(0, 9)); let c1 = (crc >> 8) & 0xFF; let c2 = crc & 0xFF; let message = "OK"; let error = ""; if(c1 != bytes[9]) { //CRC_ERROR message = "NOK"; error = "CRC_ERROR c1"; instance.send(instanceSendTo.debug, "CRC_ERROR c1"); } if(c2 != bytes[10]) { //CRC_ERROR message = "NOK"; error = "CRC_ERROR c2"; instance.send(instanceSendTo.debug, "CRC_ERROR c2"); } //crc error if(type != "RESPONSE") { instance.send(instanceSendTo.debug, bytes); instance.send(instanceSendTo.debug, "RESPONSE " + type + " - " + bytes[4]); //logger.debug(instanceSendTo.debug, "RESPONSE " + type + " - " + bytes[4], bytes); error = "type is: " + type; message = "NOK"; } return {message: message, type: type, error: error}; } function buildTasks(params) { //report FLOW.OMS_edge_fw_version as fw_version //report date as startdate monitor.info("buildTasks - params", params); //return; //https://service-prod01.worksys.io/gettime let processLine;//defined line let init = false; let processLineProfiles = true; let processBroadcast = true; let processNodes = true; if(params == undefined) { init = true; tasks = []; logger.debug("-->buildTasks clear tasks"); } else { processLineProfiles = false; processBroadcast = false; processNodes = false; processLineProfiles = params.processLineProfiles; processLine = params.line; } //load profiles pre linie //relaysData[ record["line"] ] let now = new Date(); if(processLineProfiles) { //process line profiles let keys = Object.keys(relaysData); for(let i = 0; i < keys.length; i++) { let line = keys[i];//line is turned off by default let profilestr = relaysData[line].profile; //Reset linii let resetLine = false; if(FLOW.OMS_rvo_name == "Kovalov RVO 2" && line != '0' && init == true) resetLine = true; if(resetLine) { /* Takže v Koválove sú nastavené offesty pre dusk a dawn nasledovne: DUSK: offset +20 minút – teda napr. namiesto 17:00 bude 17:20 a reštart by sa robil v čase 17:19, teda o minútu skôr. Tak aby keď budeš robiť zapnutie o 17:20 tak na RVO1 sa svietidlá zapnú v rovnakom čase. Teda: vypnutie v čase DUSK_TIME + 19 minút, zapnutie v čase DUSK_TIME + 20 minút DAWN: offset -30 minút – teda napr. namiesto 7:00 bude 6:30 a reštart by sa robil v čase 6:30, tak aby sa svietidlá zhasli rovnako s RVO1. Zapnutie by bolo 6:31. Teda: vypnutie v čase DAWN_TIME -30 minút, zapnutie v čase DAWN_TIME -29 minút Vždy po reštarte asi 30 sekúnd po zapnutí treba poslať aktuálny čas na nody. */ //function calculateDuskDown(date, line, duskOffset = 0, dawnOffset = 0) let duskOffset = 20; let dawnOffset = -30; let sunCalcResult = calculateDuskDown(new Date(), undefined, duskOffset, dawnOffset); console.log(sunCalcResult); //if(isDusk) time_points[t].value = 1;//sumrak - zapneme svetlo //if(isDawn) time_points[t].value = 0;//vychod - vypneme svetlo //DUSK - sumrak { //vypneme liniu a o minitu zapneme { let value = 0;//vypneme liniu let isDusk = true; let isDawn = false; let dusk_time = sunCalcResult.dusk_time; if(dusk_time < now.getTime()) dusk_time = dusk_time + 24*60*60*1000;//1den let params = getParams(priorityTypes.relay_profile); params.type = "relay"; params.line = line; params.value = value; params.tbname = relaysData[line].tbname; params.timestamp = dusk_time; params.duskOffset = duskOffset; params.useProfile = false; //once a day params.addMinutesToTimestamp = 24*60; //this will be recalculated params.isDusk = isDusk; params.isDawn = isDawn; if(params.value == 0) params.info = "reset - KOVALOV - force turn off line: " + line; else if(params.value == 1) params.info = "reset - KOVALOV - force turn on line: " + line; params.debug = true; //turn on/off line tasks.push(params); console.log(params); } //a o minutu zapneme { let value = 1;//zapneme liniu let isDusk = true; let isDawn = false; let dusk_time = sunCalcResult.dusk_time + 60*1000;//o minutu neskor po vypnuti zapneme if(dusk_time < now.getTime()) dusk_time = dusk_time + 24*60*60*1000;//1den let params = getParams(priorityTypes.relay_profile); params.type = "relay"; params.line = line; params.value = value; params.tbname = relaysData[line].tbname; params.timestamp = dusk_time; params.duskOffset = duskOffset + 1; params.useProfile = false; //once a day params.addMinutesToTimestamp = 24*60; //this will be recalculated params.isDusk = isDusk; params.isDawn = isDawn; if(params.value == 0) params.info = "reset - KOVALOV - force turn off line: " + line; else if(params.value == 1) params.info = "reset - KOVALOV - force turn on line: " + line; params.debug = true; //turn on/off line tasks.push(params); console.log(params); } } //DAWN - vychod { //vypneme liniu a o minitu zapneme { let value = 0;//vypneme liniu let isDusk = false; let isDawn = true; let dawn_time = sunCalcResult.dawn_time; if(dawn_time < now.getTime()) dawn_time = dawn_time + 24*60*60*1000;//1den let params = getParams(priorityTypes.relay_profile); params.type = "relay"; params.line = line; params.value = value; params.tbname = relaysData[line].tbname; params.timestamp = dawn_time; params.dawnOffset = dawnOffset; params.useProfile = false; //once a day params.addMinutesToTimestamp = 24*60; //this will be recalculated params.isDusk = isDusk; params.isDawn = isDawn; if(params.value == 0) params.info = "reset - KOVALOV - force turn off line: " + line; else if(params.value == 1) params.info = "reset - KOVALOV - force turn on line: " + line; params.debug = true; //turn on/off line tasks.push(params); console.log(params); } //a o minitu zapneme { let value = 1;//vypneme liniu let isDusk = false; let isDawn = true; let dawn_time = sunCalcResult.dawn_time + 1000*60;//o minutu neskor po vypnuti zapneme if(dawn_time < now.getTime()) dawn_time = dawn_time + 24*60*60*1000;//1den let params = getParams(priorityTypes.relay_profile); params.type = "relay"; params.line = line; params.value = value; params.tbname = relaysData[line].tbname; params.timestamp = dawn_time; params.dawnOffset = dawnOffset + 1; params.useProfile = false; //once a day params.addMinutesToTimestamp = 24*60; //this will be recalculated params.isDusk = isDusk; params.isDawn = isDawn; if(params.value == 0) params.info = "reset - KOVALOV - force turn off line: " + line; else if(params.value == 1) params.info = "reset - KOVALOV - force turn on line: " + line; params.debug = true; //turn on/off line tasks.push(params); console.log(params); } } //console.log("-------------------------Kovalov RVO 2----"); } if(processLine != undefined) { if(processLine != line) continue; } try{ if(profilestr === "") throw ("profile is not defined"); let profile = JSON.parse(profilestr); if(Object.keys(profile).length === 0) throw ("profile is not defined"); monitor.info("buildTasks: profile for line", line); monitor.info("profile:", profile); let time_points = profile.time_points; if(time_points == undefined) time_points = profile.intervals; monitor.info("buildTasks: time_points", time_points); let currentValue = 0; if(time_points.length > 0) currentValue = time_points[ time_points.length - 1].value; //create task for tun on + turn off, calculate dusk/down if(profile.astro_clock == true) { //let now = new Date().toLocaleString("en-US", {timeZone: "Europe/Bratislava"}); let sunCalcResult = calculateDuskDown(new Date(), line); 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} ); if(profile.dusk_lux_sensor == false) time_points.push( {"start_time": sunCalcResult["dusk"], "value": 0, "isDusk": true} ); //aby nam to neostalo svietit if(profile.dawn_lux_sensor == true) { //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window let [ahours, aminutes, aseconds] = sunCalcResult["dawn"].split(':'); let ad = new Date(); ad.setHours( parseInt(ahours) ); ad.setMinutes( parseInt(aminutes) + profile.dawn_lux_sensor_time_window ); ad.setSeconds(0); let strDate = ad.getHours() + ":" + ad.getMinutes(); time_points.push( {"value": 0, "start_time": strDate} ); } if(profile.dusk_lux_sensor == true) { //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window let [ahours, aminutes, aseconds] = sunCalcResult["dusk"].split(':'); let ad = new Date(); ad.setHours( parseInt(ahours) ); ad.setMinutes( parseInt(aminutes) + profile.dawn_lux_sensor_time_window ); ad.setSeconds(0); let strDate = ad.getHours() + ":" + ad.getMinutes(); time_points.push( {"value": 1, "start_time": strDate} ); } } //sort time_points time_points.sort(function (a, b) { let [ahours, aminutes, aseconds] = a.start_time.split(':'); let [bhours, bminutes, bseconds] = b.start_time.split(':'); let ad = new Date(); ad.setHours( parseInt(ahours) ); ad.setMinutes( parseInt(aminutes) ); ad.setSeconds(0); let bd = new Date(); bd.setHours( parseInt(bhours) ); bd.setMinutes( parseInt(bminutes) ); ad.setSeconds(0); return ad.getTime() - bd.getTime(); }); monitor.info("-->comming events turn on/off lines:"); for(let t = 0; t < time_points.length; t++) { let start_time = new Date(); let isDusk = false; let isDawn = false; if(time_points[t].hasOwnProperty("isDusk")) isDusk = time_points[t].isDusk; if(time_points[t].hasOwnProperty("isDawn")) isDawn = time_points[t].isDawn; if(isDusk) time_points[t].value = 1;//sumrak - zapneme svetlo if(isDawn) time_points[t].value = 0;//vychod - vypneme svetlo if(time_points[t].hasOwnProperty("start_time")) { let [hours, minutes, seconds] = time_points[t].start_time.split(':'); start_time.setHours( parseInt(hours) ); start_time.setMinutes( parseInt(minutes) ); start_time.setSeconds(0); } //task is the past if(now.getTime() > start_time.getTime()) { currentValue = time_points[t].value; //je v minulosti, pridame 24h start_time.setDate(start_time.getDate() + 1); } let params = getParams(priorityTypes.relay_profile); params.type = "relay"; params.line = line; params.value = time_points[t].value; params.tbname = relaysData[line].tbname; params.timestamp = start_time.getTime(); params.addMinutesToTimestamp = 0; //once a day if(!isDusk && !isDawn) params.addMinutesToTimestamp = 24*60; //inak sa cas vypocita dynamicky //this will be recalculated params.isDusk = isDusk; params.isDawn = isDawn; //if(profile.astro_clock == true && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) if(params.value == 0) { params.info = "turn off line: " + line; if(isDusk) params.info = "dusk: turn off line: " + line; if(isDawn) params.info = "dawn: turn off line: " + line; } else if(params.value == 1) { params.info = "turn on line: " + line; if(isDusk) params.info = "dusk: turn on line: " + line; if(isDawn) params.info = "dawn: turn on line: " + line; } params.debug = true; //turn on/off line tasks.push(params); monitor.info(params.info, start_time); } monitor.info("-->time_points final", line, time_points); //ensure to turn on/off according to calculated value let params = getParams(priorityTypes.terminal); params.type = "relay"; params.line = parseInt(line); params.tbname = relaysData[line].tbname; params.value = currentValue; params.isDusk = false; params.isDawn = false; params.timestamp = priorityTypes.terminal; params.addMinutesToTimestamp = 0; params.debug = true; //logger.debug(now.toLocaleString("sk-SK")); monitor.info("-->currentValue for relay", line, currentValue); //turn on/off line if(params.value == 0) params.info = "turn off line on startup: " + line; else if(params.value == 1) params.info = "turn on line on startup: " + line; tasks.push(params); } catch (error) { if(profilestr !=="" ) { //errLogger.error(profilestr, error); errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); } } } //logger.debug("tasks:"); //logger.debug(tasks); } //PROCESS DEFAULT BROADCASTS //RPC pre nody / broadcast //Time of dusk, Time of dawn //Actual Time if(processBroadcast) { let addMinutesToTimestamp = 5; { //run broadcast Time of dusk addMinutesToTimestamp = 60*5; let params = getParams(priorityTypes.node_broadcast); let recipient = 2;//2 broadcast, address = 0 let address = 0;//0 if(recipient === 2) { address = 0xffffffff;//Broadcast } let sunCalcResult = calculateDuskDown(); let dusk_hours = sunCalcResult["dusk_hours"]; let dusk_minutes = sunCalcResult["dusk_minutes"]; params.address = address;//broadcast params.byte1 = dusk_hours;//h params.byte2 = dusk_minutes;//m params.byte3 = 0;//s params.byte4 = 0; params.recipient = recipient; params.register = 6;//Time of dusk - Reg 6 params.rw = 1;//write let timestampStart = priorityTypes.node_broadcast; //other values params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast Time of dusk"; tasks.push(params); } { //run broadcast Time of dawn addMinutesToTimestamp = 60*5; let params = getParams(priorityTypes.node_broadcast); let recipient = 2;//2 broadcast, address = 0 let address = 0;//0 if(recipient === 2) { address = 0xffffffff;//Broadcast } let sunCalcResult = calculateDuskDown(); let dawn_hours = sunCalcResult["dawn_hours"]; let dawn_minutes = sunCalcResult["dawn_minutes"]; params.address = address;//broadcast params.byte1 = dawn_hours;//h params.byte2 = dawn_minutes;//m params.byte3 = 0;//s params.byte4 = 0; params.recipient = recipient; params.register = 7;//Time of dawn - Reg 6 params.rw = 1;//write let timestampStart = priorityTypes.node_broadcast; //other values params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast Time of dawn"; tasks.push(params); } { //run broadcast //Actual time addMinutesToTimestamp = 5; let params = getParams(priorityTypes.node_broadcast); let recipient = 2;//2 broadcast, address = 0 let address = 0;//0 if(recipient === 2) { address = 0xffffffff;//Broadcast } var d = new Date(); let hours = d.getHours(); let minutes = d.getMinutes(); let seconds = d.getSeconds(); params.address = address;//broadcast params.byte1 = hours;//h params.byte2 = minutes;//m params.byte3 = seconds;//s params.byte4 = 0; params.recipient = recipient; params.register = 87;//Actual time params.rw = 1;//write let timestampStart = priorityTypes.node_broadcast; //other values params.type = "cmd"; //params.tbname = tbname; params.timestamp = timestampStart; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual time"; tasks.push(params); } { //run broadcast Actual Lux level from cabinet //Do tohto registra posiela riadiaca jednotka hodnotu intenzity osvetlenia ktorú meria jej senzor pre potreby riadenia časov súmraku resp. úsvitu podľa intenzity osvetlenia. //Byty 0 (LSB) a 1 obsahujú 16 bitový integer s luxami. let params = getParams(priorityTypes.node_broadcast); addMinutesToTimestamp = 15; let recipient = 2;//2 broadcast, address = 0 let address = 0;//0 if(recipient === 2) { address = 0xffffffff;//Broadcast } //TODO //16 bitový integer s luxami params.byte3 = lux_sensor; params.byte4 = lux_sensor; params.timestamp = priorityTypes.node_broadcast; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "run broadcast: Actual Lux level from cabinet"; params.register = 95;//Actual Lux level from cabinet params.rw = 1;//write } } //process nodes & tasks //reportovanie pre platformu if(processNodes) { for (let k in nodesData) { let address = parseInt(k); let tbname = nodesData[k].tbname; let register = 0; //logger.debug("generated cmd - buildTasks for node:", address); //listOfCommands - READ for(let i = 0; i < listOfCommands.length; i++) { register = listOfCommands[i]; let params = getParams(priorityTypes.node_cmd); //core rpc values params.address = address; params.byte1 = 0; params.byte2 = 0; params.byte3 = 0; params.byte4 = 0; params.recipient = 1; params.register = register; params.rw = 0; let addMinutesToTimestamp = priorities[register]; let timestampStart = priorityTypes.node_cmd; //run imediatelly in function runTasks if(addMinutesToTimestamp > 1) { timestampStart = timestampStart + addMinutesToTimestamp * 60000; } //other values params.type = "cmd"; params.tbname = tbname; params.timestamp = timestampStart; params.addMinutesToTimestamp = addMinutesToTimestamp; params.info = "generated cmd - buildTasks (node)"; //monitor last node && last command /* if(register == listOfCommands[ listOfCommands.length - 1 ]) { //if(k == 632) params.debug = true; if(k == 698) params.debug = true; } */ tasks.push(params); } } } //niektore ulohy sa vygeneruju iba 1x pri starte!!! if(!init) return; //Priebežne (raz za cca 5 minút) je potrebné vyčítať z Master nodu verziu jeho FW. //Jedná sa o register 10. Rovnaká interpretácia ako pri FW verzii nodu. //Adresa mastera je 0. V prípade že kedykoľvek nastane situácia že Master Node neodpovedá (napríklad pri vyčítaní telemetrie z nodu nevráti žiadne dáta), //tak treba vyreportovať string "NOK". { let params = getParams(priorityTypes.fw_detection); params.type = "cmd"; params.register = 4; params.address = 0; let timestampStart = priorityTypes.fw_detection; params.timestamp = timestampStart; params.addMinutesToTimestamp = 5; params.tbname = FLOW.OMS_edgeName; params.info = "Master node FW verzia"; //params.debug = true; //this will set FLOW.OMS_masterNodeIsResponding tasks.push(params); } //kazdu hodinu skontrolovat nastavenie profilov { //get exact datetime from services //https://service-prod01.worksys.io/gettime let params = getParams(priorityTypes.fw_detection); params.type = "process_profiles"; let timestampStart = priorityTypes.relay_profile; params.timestamp = timestampStart; params.addMinutesToTimestamp = 60;//60 = every hour params.info = "detekcia nespracovaných profilov linie a nodov"; //params.debug = true; tasks.push(params); } { //get exact datetime from services //https://service-prod01.worksys.io/gettime //https://service-prod01.worksys.io/#main //https://sa-prod01.worksys.io/ //https://code-prod01.worksys.io/ let params = getParams(priorityTypes.fw_detection); params.type = "ntp-gettime"; let timestampStart = priorityTypes.fw_detection; params.timestamp = timestampStart; params.addMinutesToTimestamp = 60;//every hour params.info = "https://service-prod01.worksys.io/gettime"; //params.debug = true; //this will set FLOW.OMS_masterNodeIsResponding tasks.push(params); } { //edge_date_time let params = getParams(priorityTypes.node_cmd); params.type = "edge_date_time"; let timestampStart = priorityTypes.node_cmd; params.timestamp = timestampStart; params.addMinutesToTimestamp = 1; params.tbname = FLOW.OMS_edgeName; params.info = "reportovanie aktuálneho času na LM - EDGE-Date Time"; //logger.debug("BUILD Master node FW verzia"); tasks.push(params); } { //edge_date_time let params = getParams(priorityTypes.node_cmd); params.type = "number_of_luminaires"; let timestampStart = priorityTypes.node_cmd + 1; params.timestamp = timestampStart; params.addMinutesToTimestamp = 1; params.tbname = FLOW.OMS_edgeName; params.info = "reportovanie number_of_luminaires"; tasks.push(params); } monitor.info("tasks created:", tasks.length); } function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) { //let dusk_hours = sunCalcResult["dusk_hours"]; //let dusk_minutes = sunCalcResult["dusk_minutes"]; let duskTimeStamp; let downTimeStamp; //prejedme si line s profilom, kde mame "astro_clock": true /* "dawn_lux_sensor": true, "dusk_lux_sensor": true, "dawn_lux_sensor_value": 5, "dusk_lux_sensor_value": 5, "dawn_astro_clock_offset": 0, "dusk_astro_clock_offset": 10, "dawn_lux_sensor_time_window": 30, "dusk_lux_sensor_time_window": 30, "dawn_astro_clock_time_window": 60, "dusk_astro_clock_time_window": 60 */ //ak sme pred/po vychode a lux value <= lux_sensor_value, liniu zapneme //ak sme pred/po zapade a lux_value <= lux_sensor_value, liniu zapneme let now = new Date(); let currentTimestamp = now.getTime(); let keys = Object.keys(relaysData); for(let i = 0; i < keys.length; i++) { let line = keys[i];//line is turned off by default let profilestr = relaysData[line].profile; try{ let profile = JSON.parse(profilestr); if(Object.keys(profile).length === 0) throw ("profile is not defined"); if(profile.astro_clock == true) { let sunCalcResult = calculateDuskDown(date, line); //dawn: usvit/vychod - lux je nad hranicou - vypnem //dusk: zapad pod hranicou - zapnem //"dawn_lux_sensor_time_window": 30, //"dusk_lux_sensor_time_window": 30, //vychod if(profile.dawn_lux_sensor == true) { 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) { //dawn: usvit/vychod - lux je nad hranicou - vypnem if(lux_sensor_value > profile.dawn_lux_sensor_value) { //vypnem turnOffLine(line, "profile: dawn - turnOff line according to lux sensor"); } else { //zapnem turnOnLine(line, "profile: dawn - turnOn line according to lux sensor"); } } //ak sme po vychode if(currentTimestamp > lux_sensor_time_window2) { //vypneme //urobime jednorazovy prikaz } } //zapad if(profile.dusk_lux_sensor == true) { 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) { //dusk: zapad pod hranicou - zapnem if(lux_sensor_value < profile.dusk_lux_sensor_value) { //zapnem turnOnLine(line, "profile: dusk - turnOff line according to lux sensor"); } else { //vypnem turnOffLine(line, "profile: dusk - turnOff line according to lux sensor"); } } } } } catch (error) { //if(profilestr !=="" ) logger.debug(profilestr, error); } } } let sunCalcResult = calculateDuskDown(); let reportDuskDawn = { dusk_time: sunCalcResult.dusk_time, dawn_time: sunCalcResult.dawn_time, dusk_time_reported: undefined, dawn_time_reported: undefined }; async function upateNodeStatus(node, status) { //MASTER if(node == 0) return; let nodeObj = nodesData[node]; if(nodeObj == undefined) return; if(status) { cmdNOKNodeCounter[node] = 0; } else cmdNOKNodeCounter[node]++; if(nodeObj.status !== status) { await dbNodes.modify({ status: status }).where("node", node).make(function(builder) { builder.callback(function(err, response) { if(err == null) nodesData[node].status = status; }); }); } } async function runTasks() { clearInterval(interval); let currentTimestamp = Date.now(); //report dusk, dawn--------------------------------- if(reportDuskDawn.dusk_time < currentTimestamp) { //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund if( (currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { //reportovali sme? if(reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { sendNotification("CMD Manager: calculated Time of dusk", FLOW.OMS_edgeName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", instanceSendTo.tb, instance); reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; } } var nextDay = new Date(); nextDay.setDate(nextDay.getDate() + 1); sunCalcResult = calculateDuskDown(nextDay); reportDuskDawn.dusk_time = sunCalcResult.dusk_time; } if(reportDuskDawn.dawn_time < currentTimestamp) { //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund if( (currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { //reportovali sme? if(reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { sendNotification("CMD Manager: calculated Time of dawn", FLOW.OMS_edgeName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", instanceSendTo.tb, instance); reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; } } var nextDay = new Date(); nextDay.setDate(nextDay.getDate() + 1); sunCalcResult = calculateDuskDown(nextDay); reportDuskDawn.dawn_time = sunCalcResult.dawn_time; } //-------------------------------------------------------- //sort tasks //tasks.sort((a,b) => a.timestamp - b.timestamp ); tasks.sort(function (a, b) { if(a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) { return a.priority - b.priority; } return a.timestamp - b.timestamp; }); if(tasks.length == 0 ) { instance.send(instanceSendTo.debug, "no tasks created"); interval = setInterval(runTasks, longInterval); return; } if(!rsPort.isOpen) { instance.send(instanceSendTo.debug, "!rsPort.isOpen"); //await rsPort.open(); //continue } let currentTask = tasks[0]; if(currentTask.debug) { //logger.debug("--->task to process", currentTask); } if(currentTask.timestamp <= currentTimestamp) { let params = {...tasks[0]}; if(FLOW.OMS_maintenance_mode) { //allow terminal commands if(params.type == "cmd-terminal"); else { interval = setInterval(runTasks, longInterval); return; } } let type = params.type; let tbname = params.tbname; let nodeKey = params.address; let useProfile = params.useProfile; if(useProfile === undefined) useProfile = true; let duskOffset = params.duskOffset; let dawnOffset = params.dawnOffset; let line = null; //rpc related if(nodesData[nodeKey] !== undefined) line = nodesData[nodeKey].line; if(params.line !== undefined) line = params.line; let repeatTask = false; if(params.addMinutesToTimestamp > 0) repeatTask = true; if(params.isDawn || params.isDusk) repeatTask = true; if(repeatTask) { if(type == "cmd") { //set next start time automatically tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; } } else { //terminal data... tasks.shift(); } //custom tasks if(type == "number_of_luminaires") { tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; //treba reportovat node status { //number_of_luminaires //number_of_ok_luminaires //number_of_nok_luminaires let keys = Object.keys(nodesData); let number_of_luminaires = keys.length; let number_of_ok_luminaires = 0; let number_of_nok_luminaires = 0; for(let i = 0; i < keys.length; i++) { let key = keys[i]; let nodeObj = nodesData[key]; if(nodeObj.tbname == undefined) continue; if(nodeObj.status) number_of_ok_luminaires++; else number_of_nok_luminaires++; } let values = { number_of_luminaires: number_of_luminaires, number_of_ok_luminaires: number_of_ok_luminaires, number_of_nok_luminaires: number_of_nok_luminaires }; let dataToTb = { [FLOW.OMS_edgeName]: [ { "ts": Date.now(), "values": values } ] } //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); interval = setInterval(runTasks, shortIterval); return; } } if(type == "ntp-gettime") { RESTBuilder.make(function(builder) { if(!builder) return; builder.method('GET'); //FLOW.OMS_edge_fw_version builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); builder.callback(function(err, response, output) { if (err) { console.log(err); return; } instance.send(instanceSendTo.debug, "RESTBuilder response"); const res = output.response; try{ const obj = JSON.parse(res); let d = new Date(obj.date); const now = new Date(); //offset in minutes - convertUTCDateToLocalDate let diffInMinutes = now.getTimezoneOffset(); //d.setMinutes( d.getMinutes() + diffInMinutes ); //let converted = convertUTCDateToLocalDate(d); console.log("---->TimezoneOffset", diffInMinutes); if(d instanceof Date) { console.log("current js date:", d, d.getHours()); let year = d.getFullYear(); let month = addZeroBefore(d.getMonth() + 1); let day = addZeroBefore(d.getDate()); //-2 hodiny!!!! let hours = addZeroBefore( d.getHours() ); let minutes = addZeroBefore(d.getMinutes() ); let seconds = addZeroBefore(d.getSeconds()); let timestamp = `${year}${month}${day} ${hours}:${minutes}:${seconds}`; let dstr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; logger.debug("--->RESTBuilder response", res, timestamp, dstr); //TODO - poslat notifikaciu a nastav hw cas //timedatectl set-timezone "Europe/Bratislava" //hwclock --set --date="2021-08-24 15:02:00" --localtime //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ //timedatectl set-time "2022-04-27 09:13:00" { let year = d.getUTCFullYear(); let month = addZeroBefore(d.getUTCMonth() + 1); let day = addZeroBefore(d.getUTCDate()); let hours = addZeroBefore( d.getUTCHours() ); let minutes = addZeroBefore(d.getUTCMinutes() ); let seconds = addZeroBefore(d.getUTCSeconds()); let UTCstr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; exec(`timedatectl set-time "${UTCstr}"`, (err, stdout, stderr) => { if (err || stderr) { console.error(err); console.log(stderr); console.log(UTCstr); monitor.info("failed timedatectl set-time", err, stderr); } else { console.log(`UTC: timedatectl set-time "${UTCstr}"`); } }); } //RTC time - hardware time - ak je RTC in local TZ: yes - nastavime UTC preratany podla timezone exec(`hwclock --set --date="${dstr}" --localtime`, (err, stdout, stderr) => { if (err || stderr) { console.error(err); console.log(stderr); monitor.info("failed to set date", dstr, res, err, stderr); } else { console.log(stdout); console.log(`Successfully set the system's datetime - ${dstr}`); const now = new Date(); console.log(now); } }); //detect Read-only file system ///dev/mmcblk0p2 on / type ext3 (rw,noatime,nodiratime,errors=remount-ro,commit=100,data=ordered) - (if ro = Read-only file system) //egrep " ro,|,ro " /proc/mounts //mount exec(`egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { if (err || stderr) { console.error(err); console.log(stderr); } else { //console.log("Read-only", stdout); let lines = stdout + ""; lines = lines.split("\n"); let readOnlyDetected = ""; for(let i = 0; i < lines.length; i++) { if(lines[i].startsWith("/dev/mmcblk0p2")) { readOnlyDetected = lines[i]; } } if(readOnlyDetected !== "") { errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); } } }); /* //povodna verzia exec(`sudo /bin/date --set="${timestamp}"`, (err, stdout, stderr) => { if (err || stderr) { console.error(err); console.log(stderr); monitor.info("failed to set date", d, res, err, stderr); } else { console.log(stdout); console.log(`Successfully set the system's datetime to ${stdout}`); const now = new Date(); console.log(now); } }); */ } } catch (error) { logger.debug("--->ntp-gettime", error, res); } }); }); tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; interval = setInterval(runTasks, shortIterval); return; } //kontrola nespracovanych profilov nodov if(type == "process_profiles") { tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; //select nespracovane nody //node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean //buildTasks({processLineProfiles: true, line: line}); /* let keys = Object.keys(nodesData); for(let i = 0; i < keys.length; i++) { let node = keys[i]; let line = node.line; if(node.processed) continue; if(relaysData[line] != undefined) { let relayStatus = relaysData[line].contactor; if(relayStatus == 1) { //linia je zapnuta //await loadRelaysData(flowdata.data.line); } } } */ //vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov loadRelaysData(); interval = setInterval(runTasks, shortIterval); return; } if(type == "edge_date_time") { //var d = new Date(); //let hours = addZeroBefore(d.getHours()); //let minutes = addZeroBefore(d.getMinutes()); //let seconds = addZeroBefore(d.getSeconds()); //let values = {"edge_date_time": `${hours}:${minutes}:${seconds}`}; let values = {"edge_date_time": Date.now()}; let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); interval = setInterval(runTasks, shortIterval); return; } //relay if(type == "relay") { //ak je dusk, alebo dawn, vypocitame si dynamicky nove values if(params.isDawn || params.isDusk) { let date = new Date(); date.setDate(date.getDate() + 1);//next day let sunCalcResult; if(useProfile) sunCalcResult = calculateDuskDown(date, params.line); else { //do not use profile, line is there for undefined sunCalcResult = calculateDuskDown(date, undefined, duskOffset, dawnOffset); } if(params.isDawn) { tasks[0].timestamp = sunCalcResult.dawn_time; } if(params.isDusk) { tasks[0].timestamp = sunCalcResult.dusk_time; } } else { if(tasks[0].addMinutesToTimestamp == 0);// tasks.shift(); else tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; } let info; if(useProfile) info = "aplikovaný bod profilu"; else info = params.info; let message = ""; if(params.value == 1) { turnOnLine(params.line, info); message = "on"; } else if(params.value == 0) { turnOffLine(params.line, info); message = "off"; } //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.INFO, "aplikovaný bod profilu línie " + params.line + " - stav: " + message, "", instanceSendTo.tb, instance, null ); if(useProfile) sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", instanceSendTo.tb, instance ); interval = setInterval(runTasks, shortIterval); return; } //zhodeny hlavny istic let disconnected = false; //if(rotary_switch_state == "Off") disconnected = true; //state_of_breaker[line] - alebo istic linie if(state_of_breaker.hasOwnProperty(line)) { //if(state_of_breaker[line] == "Off") disconnected = true; } //toto sa reportuje po prijati dat z di_do_controlera if(disconnected) { let values = {"status": "OFFLINE"}; logger.debug("disconnected", values); logger.debug("rotary_switch_state", rotary_switch_state); logger.debug("state_of_breaker", state_of_breaker[line]); let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } //report only once! if(!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false; if(!disconnectedReport[tbname]) { //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); } interval = setInterval(runTasks, shortIterval); return; } disconnectedReport[tbname] = false; //high_priority if(!FLOW.OMS_masterNodeIsResponding) { //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version errorHandler.sendMessageToService("Master node is not responding"); let stop = true; if(params.type == "cmd-terminal") stop = false; //fw version - register == 4 if(params.type == "cmd" && params.register == 4 && params.address == 0) stop = false; if(stop) { interval = setInterval(runTasks, longInterval); return; } } let relayStatus = 1; if(relaysData[line] != undefined) { relayStatus = relaysData[line].contactor; } if(line == 0) relayStatus = 0; if(params.type == "cmd-terminal") relayStatus = 1; //check if rotary_switch_state == "Off" if(relayStatus == 0) { //console.log("------------------------------------relayStatus", relayStatus, line); let values = {"status": "OFFLINE"}; let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); interval = setInterval(runTasks, shortIterval); return; } if(!rsPort.isOpen) { interval = setInterval(runTasks, longInterval); return; } //RE-CALCULATE VALUES //set actual time for broadcast if(params.register == 87 && params.recipient === 2) { var d = new Date(); let hours = d.getHours(); let minutes = d.getMinutes(); let seconds = d.getSeconds(); params.byte1 = hours;//h params.byte2 = minutes;//m params.byte3 = seconds;//s params.byte4 = 0; } //set dusk/down for broadcast //Time of dusk if(params.register == 6 && params.recipient === 2) { let sunCalcResult = calculateDuskDown(); let dusk_hours = sunCalcResult["dusk_hours"]; let dusk_minutes = sunCalcResult["dusk_minutes"]; params.byte1 = dusk_hours;//h params.byte2 = dusk_minutes;//m params.byte3 = 0;//s params.byte4 = 0; //TODO astrohodiny let dusk = "Time of dusk: " + sunCalcResult["dusk"]; //sendNotification("CMD Manager: calculated Time of dusk", relaysData[0].tbname, ERRWEIGHT.INFO, dusk, "", instanceSendTo.tb, instance, null ); } //Time of dawn if(params.register == 7 && params.recipient === 2) { let sunCalcResult = calculateDuskDown(); let dawn_hours = sunCalcResult["dawn_hours"]; let dawn_minutes = sunCalcResult["dawn_minutes"]; params.byte1 = dawn_hours;//h params.byte2 = dawn_minutes;//m params.byte3 = 0;//s params.byte4 = 0; //TODO astrohodiny let dawn = "Time of dawn: " + sunCalcResult["dawn"]; //sendNotification("CMD Manager: calculated Time of dusk", relaysData[0].tbname, ERRWEIGHT.INFO, dawn, "", instanceSendTo.tb, instance, null ); } //----------------------- let register = params.register; instance.send(instanceSendTo.debug, "address: " + params.address + " register:" + params.register + "type: " + params.type); var startTime, endTime; startTime = new Date(); let resp = com_generic(params.address, params.recipient, params.rw, params.register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); let readBytes = 11; //if broadcast WRITE - do not read //if(params.recipient == 2) readBytes = 0; //WRITE + BROADCAST = readBytes = 0; if(params.rw == 1 && params.recipient == 2) readBytes = 0; if(params.hasOwnProperty("debug")) { //console.log("--->readBytes", readBytes, params); } await writeData(rsPort, resp, readBytes).then(function (data) { endTime = new Date(); var timeDiff = endTime - startTime; //--1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data //let bytes = data.slice(0); let bytes = data; let dataBytes = data.slice(5,9); let result = detectIfResponseIsValid(bytes); let message = result.message; let type = result.type; let error = result.error; //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK if(params.debug != "generated cmd") { //debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug, params); } if(params.hasOwnProperty("debug")) { if(params.debug) { console.log("detected response:", result); logger.debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug, params, result); } } //debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug); //debug("writeData done", type, "duration", timeDiff, "type", params.debug, result); let tbname = params.tbname; let saveToTb = true; if(tbname == null || tbname == undefined || tbname == "") saveToTb = false; //-- //CMD FINISHED if(message == "OK") { upateNodeStatus(params.address, true); //write if(params.type == "set_node_profile") { let result = cmdCounterResolve(params.address); if(result == 0) { dbNodes.modify({ processed: true }).where("node", params.address).make(function(builder) { builder.callback(function(err, response) { sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: params.address}, "", instanceSendTo.tb, instance ); logger.debug( "--> profil úspešne odoslaný na node č. " + params.address); nodesData[params.address].processed = true; }); }); } } //parse read response let values = {}; if(params.rw == 0) { values = processResponse(register, dataBytes);//read } if(params.rw == 1) { //write command //set command dimming if(params.register == 1) values = {"comm_status": message}; } if(params.register == 0) values["status"] = message; //fw version - register == 4 if(params.register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version; if(params.address == 0) { //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", instanceSendTo.tb, instance, "rvo_status" ); //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, "master_node_is_responding_again", {}, "", instanceSendTo.tb, instance, "rvo_status" ); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_responding_again", {}, "", instanceSendTo.tb, instance, "rvo_status" ); FLOW.OMS_masterNodeIsResponding = true; } //odoslanie príkazu z terminálu - dáta if(params.type == "cmd-terminal") { //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.DEBUG, "odoslanie príkazu z terminálu", params, instanceSendTo.tb, instance, null ); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, instanceSendTo.tb, instance ); } if(params.debug) { logger.debug("saveToTb", saveToTb, tbname, values); } if(saveToTb) { let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); } else { if(params.type == "cmd-terminal") { if(params.refFlowdataKey != undefined) { logger.debug("cmd-terminal SUCCESS"); logger.debug(currentTask); //make http response let responseObj = {}; responseObj["type"] = "SUCESS"; responseObj["bytes"] = data; //params.refFlowdata.data = responseObj; //instance.send(instanceSendTo.http_response, params.refFlowdata); let refFlowdata = refFlowdataObj[ params.refFlowdataKey ]; refFlowdata.data = responseObj; instance.send(instanceSendTo.http_response, refFlowdata); } else { console.log("params.refFlowdataKey is undefined", params); } } } } else { upateNodeStatus(params.address, false); if(params.refFlowdataKey != undefined) { logger.debug("cmd-terminal FAILED"); logger.debug(currentTask); //make http response let responseObj = {}; responseObj["type"] = "ERROR"; responseObj["bytes"] = data; //params.refFlowdata.data = responseObj; //instance.send(instanceSendTo.http_response, params.refFlowdata); let refFlowdata = refFlowdataObj[ params.refFlowdataKey ]; if(refFlowdata !== undefined) { refFlowdata.data = responseObj; instance.send(instanceSendTo.http_response, refFlowdata); } } /* if(params.type == "cmd-terminal") { if(params.refFlowdata != undefined) { logger.debug("cmd-terminal FAILED"); logger.debug(currentTask); //make http response let responseObj = {}; responseObj["type"] = "ERROR"; responseObj["bytes"] = data; params.refFlowdata.data = responseObj; instance.send(instanceSendTo.http_response, params.refFlowdata); } } */ if(params.address == 0) { //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", instanceSendTo.tb, instance, "rvo_status"); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", instanceSendTo.tb, instance, "rvo_status"); logger.debug("master_node_is_not_responding", params); FLOW.OMS_masterNodeIsResponding = false; } if(params.type == "set_node_profile") { delete cmdCounter[params.address]; let tbname = nodesData[ params.address ].tbname; logger.debug( "profil nebol úspešne odoslaný na node č. ", params, result, resp); //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "profil nebol úspešne odoslaný na node č. " + params.address, "", instanceSendTo.tb, instance, null ); sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", instanceSendTo.tb, instance ); } //is it node? if(nodesData.hasOwnProperty(params.address)) { if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false; } //Master node version //if(params.register == 4 && saveToTb) if(saveToTb) { let values = { "status": "NOK" }; let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); } //instance.send(instanceSendTo.debug, result); if(params.hasOwnProperty("debug")) { if(params.debug) { logger.debug("writeData err: ", error, result, params); } } //logger.debug(error, result, params); } }).catch(function (reason) { console.log("writeData catch exception", reason); logger.debug(currentTask); if(params.refFlowdataKey != undefined) { logger.debug("catch: cmd-terminal FAILED"); logger.debug(currentTask); //make http response let responseObj = {}; responseObj["type"] = "ERROR";// responseObj["message"] = "ERROR WRITE FAILED: " + reason;// //params.refFlowdata.data = responseObj; //instance.send(instanceSendTo.http_response, params.refFlowdata); let refFlowdata = refFlowdataObj[ params.refFlowdataKey ]; if(refFlowdata !== undefined) { refFlowdata.data = responseObj; instance.send(instanceSendTo.http_response, refFlowdata); } } /* if(params.type == "cmd-terminal") { if(params.refFlowdata != undefined) { logger.debug("cmd-terminal FAILED"); logger.debug(currentTask); //make http response let responseObj = {}; responseObj["type"] = "ERROR WRITE FAILED: " + reason; //responseObj["bytes"] = data; params.refFlowdata.data = responseObj; instance.send(instanceSendTo.http_response, params.refFlowdata); //refFlowdata = undefined; } } */ if(params.hasOwnProperty("debug")) { if(params.debug) { logger.debug("-->WRITE FAILED: " + reason, params.debug, params); } } upateNodeStatus(params.address, false); let tbname = params.tbname; let saveToTb = true; if(tbname == null || tbname == undefined || tbname == "") saveToTb = false; if(params.address == 0) { //sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", instanceSendTo.tb, instance, "rvo_status"); sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", instanceSendTo.tb, instance, "rvo_status"); logger.debug("master_node_is_not_responding", params); FLOW.OMS_masterNodeIsResponding = false; } if(params.type == "set_node_profile") { delete cmdCounter[params.address]; let tbname = nodesData[ params.address ].tbname; logger.debug( "profil nebol úspešne odoslaný na node č. ", params, resp); //sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "odosielanie profilu na node č. " + params.address + " zlyhalo", "", instanceSendTo.tb, instance, null ); sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", instanceSendTo.tb, instance ); } //is it node? if(nodesData.hasOwnProperty(params.address)) { if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false; } //Master node version if(params.register == 4 && saveToTb) { let values = { "status": "NOK", "master_node_version": "NOK" }; let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } //instance.send(instanceSendTo.tb, dataToTb); tbHandler.sendToTb(dataToTb, instance); FLOW.OMS_masterNodeIsResponding = false; } //treba? /* else if(saveToTb) { let values = { "comm_status": "no_comm" }; let dataToTb = { [tbname]: [ { "ts": Date.now(), "values": values } ] } instance.send(instanceSendTo.tb, dataToTb); } */ instance.send(instanceSendTo.debug, reason); }); } else { if(currentTask.debug) { //currentTask.timestamp <= currentTimestamp logger.debug("currentTask is not processed - task is in the future", currentTask); } interval = setInterval(runTasks, longInterval); return; } //console.log("----->runTasks - setInterval", new Date()); interval = setInterval(runTasks, shortIterval); } //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI if(FLOW.OMS_serial_port == "") FLOW.OMS_serial_port = "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 }); //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit //rsPort.setMaxListeners(0); rsPort.on('open', async function() { logger.debug("CMD manager - rsPort opened sucess"); 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) { instance.send(instanceSendTo.debug, "RPC runSyncExec - Promise Resolved:" + status); logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); //APP START let dataToInfoSender = {id: FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name}; dataToInfoSender.fw_version = FLOW.OMS_edge_fw_version; dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' '); dataToInfoSender.__force__ = true; instance.send(instanceSendTo.infoSender, dataToInfoSender); logger.debug(0, "---------------------------->START message send to service", dataToInfoSender); //---- nodesData = {}; dbNodes.find().make(function(builder) { builder.callback(function(err, response) { for(let i = 0; i < response.length; i++) { let node = response[i]; let key = node["node"]; nodesData[ key ] = node; } //buildTasks(); //interval = setInterval(runTasks, longInterval); }); }); }).catch(function (reason) { instance.send(instanceSendTo.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); }); }); rsPort.on('error', function(err) { //TODO report to service!!! //errLogger.error(exports.title, "unable to open port", FLOW.OMS_serial_port, err.message); errorHandler.sendMessageToService([exports.title, "unable to open port", FLOW.OMS_serial_port, err.message], 0); instance.send(instanceSendTo.debug, err.message); }); rsPort.on("close", () => { rsPort.close(); }); //loadRelaysData(); rsPort.open(); instance.on("close", () => { clearInterval(interval); rsPort.close(); }); //onData instance.on("data", async function(flowdata) { //instance.on("data", (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 = []; if(tasks.length == 0) { buildTasks(); if(rsPort.isOpen) { interval = setInterval(runTasks, 100); } else { instance.send(instanceSendTo.debug, "port is not opened!!!"); } } } else { //terminal data - object //logger.debug("flowdata", flowdata.data); if(typeof flowdata.data === 'object') { //logger.debug("dido", flowdata.data); if(flowdata.data.hasOwnProperty("sender")) { //data from di_do_controller if(flowdata.data.sender == "di_do_controller") { if(flowdata.data.hasOwnProperty("cmd")) { let cmd = flowdata.data.cmd; if(cmd == "buildTasks") { clearInterval(interval); logger.debug("-->CMD MANAGER - BUILD TASKS"); buildTasks(); //logger.debug("tasks:"); //logger.debug(tasks); logger.debug("-->CMD MANAGER - RUN TASKS"); interval = setInterval(runTasks, 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); } } } else if(cmd == "rotary_switch_state") { //state was changed if(rotary_switch_state != flowdata.data.value) { if(flowdata.data.value == "Off") { //vyreportovat vsetky svietdla reportOfflineNodeStatus(); } else reportOnlineNodeStatus(undefined, flowdata.data.value); } 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); } else reportOnlineNodeStatus(line); } } } else{ logger.debug("undefined cmd", cmd); } } } return; } //data from worksys if(flowdata.data.hasOwnProperty("topic")) { let data = flowdata.data.content.data; let command = data.params.command; let method = data.method; let profile = data.params.payload; if(profile == undefined) profile = ""; let entity = data.params.entities[0]; let entity_type = entity.entity_type; let tbname = entity.tb_name; instance.send(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") { //let command = data.params.command; let value = data.params.payload.value; if(command == "dimming") { let nodeWasFound = false; let keys = Object.keys(nodesData); //logger.debug("-----", keys); for(let i = 0; i < keys.length; i++) { let node = keys[i]; //logger.debug( node, nodesData[node], tbname); if(tbname == nodesData[node].tbname.trim()) { let params = getParams(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); }); }); } } } 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") { //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") { 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); } return; } //terminal if(!rsPort.isOpen) await rsPort.open(); let params = flowdata.data.body; if(params == undefined) { //logger.debug("CMD manager flowdata.data.body is undefined"); return; } params.priority = 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); } } }) } ///helper functions function calculateDuskDown(date, line, duskOffset = 0, dawnOffset = 0) { if(date === undefined) date = new Date(); //if(duskOffset === undefined) duskOffset = 0; //if(dawnOffset === undefined) dawnOffset = 0; //let line = keys[i]; let profilestr = ""; if(relaysData[line] != undefined) profilestr = relaysData[line].profile; let result = {}; var times = SunCalc.getTimes(date, latitude, longitude); let dawn = new Date(times.sunrise);//usvit let dusk = new Date(times.sunset);//sumrak //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 let dusk_astro_clock_offset = duskOffset;//minutes let dawn_astro_clock_offset = dawnOffset;//minutes try{ let profile = JSON.parse(profilestr); if(Object.keys(profile).length === 0) throw ("profile is not defined"); //Jednoduchý režim if(profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) { } //Režim astrohodín if(profile.astro_clock == true) { //if(profile.dusk_lux_sensor == false) { if(profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt( profile.dusk_astro_clock_offset ); } //if(profile.dawn_lux_sensor == false) { if(profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt( profile.dawn_astro_clock_offset ); } } //dusk - súmrak //down, sunrise - svitanie } catch (error) { if(profilestr != "") { logger.debug(profilestr); logger.debug(error); } } result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset*60000); dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset*60000); result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); result.dusk_hours = dusk.getHours(); result.dusk_minutes = dusk.getMinutes(); result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); result.dawn_hours = dawn.getHours(); result.dawn_minutes = dawn.getMinutes(); result.dusk_time = dusk.getTime(); result.dawn_time = dawn.getTime(); result.dusk_astro_clock_offset = dusk_astro_clock_offset; result.dawn_astro_clock_offset = dawn_astro_clock_offset; return result; } function processResponse(register, bytes) { let values = {}; let byte3 = bytes[0]; let byte2 = bytes[1]; let byte1 = bytes[2]; let byte0 = bytes[3]; //status if(register == 0) { let statecode = bytesToInt(bytes); values = {"statecode": statecode}; return values; } //Dimming, CCT if(register == 1) { let brightness = 0; let dimming = byte0; if(dimming > 128) { //dimming = -128; brightness = dimming - 128; } //cct //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 let cct; if(byte3 == 1) cct = byte2*256 + byte1; else cct = bytesToInt(bytes.slice(0, 3)); //cct podla auditu values["dimming"] = brightness; return values; } // if(register == 4) { values["master_node_version"] = bytes[1] + "." + bytes[2]; //logger.debug("FW Version", register, bytes); } //Napätie if(register == 74) { let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); values["voltage"] = Number(voltage); } //Prúd if(register == 75) { let current = bytesToInt(bytes); values["current"] = current; } //výkon if(register == 76) { let power = (bytesToInt(bytes) * 0.1).toFixed(2); values["power"] = Number(power); } //účinník if(register == 77) { let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2); values["power_factor"] = Number(power_factor); } //frekvencia if(register == 78) { let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); values["frequency"] = Number(frequency); } //energia if(register == 79) { let energy = bytesToInt(bytes); //Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal. values["energy"] = energy / 1000; } //doba života if(register == 80) { let lifetime = ( bytesToInt(bytes) / 60).toFixed(2); values["lifetime"] = Number(lifetime); } //nastavenie profilu if(register == 8) { let time_schedule_settings = bytesToInt(bytes); values["time_schedule_settings"] = time_schedule_settings; } //skupinová adresa 1 if(register == 3) { let gr_add_1 = bytesToInt(byte0); values["gr_add_1"] = gr_add_1; let gr_add_2 = bytesToInt(byte1); values["gr_add_2"] = gr_add_2; let gr_add_3 = bytesToInt(byte2); values["gr_add_3"] = gr_add_3; let gr_add_4 = bytesToInt(byte3); values["gr_add_4"] = gr_add_4; } //naklon if(register == 84) { let temp; if(byte3 >= 128) { temp = (byte3 - 128) * (-1); } else { temp = byte3; } let inclination_x; if(byte2 >= 128) { inclination_x = (byte2 - 128) * (-1); } else { inclination_x = byte2; } let inclination_y; if(byte1 >= 128) { inclination_y = (byte1 - 128) * (-1); } else { inclination_y = byte1; } let inclination_z; if(byte0 >= 128) { inclination_z = (byte0 - 128) * (-1); } else { inclination_z = byte0; } values["temperature"] = temp; //náklon x values["inclination_x"] = inclination_x; //náklon y values["inclination_y"] = inclination_y; //náklon z values["inclination_z"] = inclination_z; } let h = byte3; let m = byte2; let s = byte1; let timestamp; if(register == 87 || register == 6 || register == 7 ) { //if(byte3 < 10) h = "0" + byte3; //if(byte2 < 10) m = "0" + byte2; //if(byte1 < 10) s = "0" + byte1; var d = new Date(); d.setHours(h); d.setMinutes(m); d.setSeconds(s); timestamp = d.getTime(); } //aktuálny čas if(register == 87) { //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. //values["actual_time"] = h + ":" + m + ":" + s; values["actual_time"] = timestamp; } //čas súmraku if(register == 6) { //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. //values["dusk_time"] = h + ":" + m + ":" + s; values["dusk_time"] = timestamp; } //čas úsvitu if(register == 7) { //Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek. //values["dawn_time"] = h + ":" + m + ":" + s; values["dawn_time"] = timestamp; } //FW verzia if(register == 89) { //formát: "Byte3: Byte2.Byte1 (Byte0)" values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; } return values; } //byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { let resp = []; let cmd = register; if (typeof adresa === 'string') adresa = parseInt(adresa); if (typeof byte1 === 'string') byte1 = parseInt(byte1); if (typeof byte2 === 'string') byte2 = parseInt(byte2); if (typeof byte3 === 'string') byte3 = parseInt(byte3); if (typeof byte4 === 'string') byte4 = parseInt(byte4); if (rw === 0) { cmd = cmd + 0x8000; } //master if(rec === 0) adresa = 0; if(rec === 2) { adresa = 0xffffffff;//Broadcast } //recipient if (rec === 3) { resp.push(0xFF); resp.push(0xFF); resp.push(0xFF); resp.push(0xFF); resp.push( adresa & 0xFF );//band } else { resp.push( (adresa >> 24) & 0xFF);//rshift resp.push( (adresa >> 16) & 0xFF); resp.push( (adresa >> 8) & 0xFF); resp.push( adresa & 0xFF ); if (rec === 2) { resp.push(0xFF); } else resp.push(0); } resp.push( (cmd >> 8) & 0xFF);//rshift resp.push( cmd & 0xFF );//band resp.push( byte1 & 0xFF );//band resp.push( byte2 & 0xFF );//band resp.push( byte3 & 0xFF );//band resp.push( byte4 & 0xFF );//band //let data = '12345'; let crc = crc16('ARC', resp); let c1 = (crc >> 8) & 0xFF; let c2 = crc & 0xFF; resp.push(c1); resp.push(c2); //logger.debug("checksum", crc); //logger.debug("resp", resp); return resp; }