diff --git a/flow/cloudmqttconnect.js b/flow/cloudmqttconnect.js index 1fc0e09..d619784 100644 --- a/flow/cloudmqttconnect.js +++ b/flow/cloudmqttconnect.js @@ -9,10 +9,10 @@ exports.output = 2; exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" }; exports.html = `
-
-
-
Hostname or IP address (if not empty - setting will override db setting)
-
+
+
+
Hostname or IP address (if not empty - setting will override db setting)
+
Port
@@ -27,14 +27,16 @@ exports.html = `
`; + + const { promisifyBuilder } = require('./helper/db_helper'); const fs = require('fs'); const mqtt = require('mqtt'); const nosql = NOSQL('tbdatacloud'); const SEND_TO = { - debug: 0, - rpcCall: 1, + debug: 0, + rpcCall: 1, } //CONFIG @@ -54,295 +56,319 @@ let lastRestoreTime = 0; // if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable let sendClientError = true; + exports.install = function(instance) { - var client; - var opts; - var clientReady = false; - - let o = null; //options - - function main() { - loadSettings(); - } - - //set opts according to db settings - function loadSettings() { - - o = instance.options; - if (!o.topic) o.topic = FLOW.GLOBALS.settings.cloud_topic; - - opts = { - host: o.host, - port: o.port, - clientId: o.clientid, - username: o.username, - rejectUnauthorized: false, - resubscribe: false - }; - - console.log("wsmqttpublich -> loadSettings from instance.options", o); - - connectToTbServer(); - } - - function connectToTbServer() { - var url = "mqtt://" + opts.host + ":" + opts.port; - console.log("MQTT URL: ", url); - - client = mqtt.connect(url, opts); - - client.on('connect', function() { - client.subscribe(`${o.topic}_backward`, (err) => { - if (!err) { - console.log("MQTT subscribed"); - } - }); - instance.status("Connected", "green"); - clientReady = true; - sendClientError = true; - }); - - client.on('reconnect', function() { - instance.status("Reconnecting", "yellow"); - clientReady = false; - }); - - client.on('message', function(topic, message) { - // message is type of buffer - message = message.toString(); - if (message[0] === '{') { - - - try { - message = JSON.parse(message); - if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { - client.publish(`${o.topic}_forward`, `{"device": "${message.device}", "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); - instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); - } - } catch (e) { instance.debug('MQTT: Error parsing data', message) } - - instance.send(SEND_TO.rpcCall, { "topic": o.topic, "content": message }); - } - }); - - client.on('close', function() { - clientReady = false; - - instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); - }); + var client; + var opts; + var clientReady = false; + + let o = null; //options + + function main() + { + loadSettings(); + } + + //set opts according to db settings + function loadSettings() + { + + o = instance.options; + if(!o.topic) o.topic = FLOW.GLOBALS.settings.cloud_topic; + + opts = { + host: o.host, + port: o.port, + clientId: o.clientid, + username: o.username, + rejectUnauthorized: false, + resubscribe: false + }; + + console.log("wsmqttpublich -> loadSettings from instance.options",o); + + connectToTbServer(); + } + + function connectToTbServer() + { + var url = "mqtt://" + opts.host + ":" + opts.port; + console.log("MQTT URL: ", url); + + client = mqtt.connect(url, opts); + + client.on('connect', function() { + client.subscribe(`${o.topic}_backward`, (err) => { + if (!err) { + console.log("MQTT subscribed"); + } + }); + instance.status("Connected", "green"); + clientReady = true; + sendClientError = true; + }); + + client.on('reconnect', function() { + instance.status("Reconnecting", "yellow"); + clientReady = false; + }); + + client.on('message', function(topic, message) { + // message is type of buffer + message = message.toString(); + if (message[0] === '{') { + TRY(function() { + + message = JSON.parse(message); + if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { + client.publish(`${o.topic}_forward`, `{"device": "${message.device}", "id": ${message.data.id}, "data": {"success": true}}`, {qos:1}); + instance.send(SEND_TO.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}}); + } + + }, () => instance.debug('MQTT: Error parsing data', message)); + } + + instance.send(SEND_TO.rpcCall, {"topic":o.topic, "content":message }); + }); + + client.on('close', function() { + clientReady = false; + + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, {"message":"Client CLOSE signal received !"}); + }); + + client.on('error', function(err) { + instance.status("Err: "+ err.code, "red"); + instance.send(SEND_TO.debug, {"message":"Client ERROR signal received !", "error":err, "opt":opts }); + if(sendClientError) { + console.log('MQTT client error', err); + sendClientError = false; + } + clientReady = false; + }); + + } + + + instance.on('0', function(data) { + + if(clientReady) + { + //do we have some data in backup file? if any, process data from database + if(saveTelemetryOnError) + { + //read telemetry data and send back to server + if(!processingData) processDataFromDatabase(); + } + + let stringifiedJson = JSON.stringify(data.data) + client.publish(`${o.topic}_forward`, stringifiedJson, {qos: 1}); + } + else + { + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, {"message":"Client unavailable. Data not sent !", "data": data.data }); + + if(saveTelemetryOnError) + { + //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql + makeBackupFromDbFile(); + + //write to tb + data.data.id = UID(); + nosql.insert(data.data); + } + } + }); + + instance.on("1", _ => { + main(); + }) + + instance.close = function(done) { + if(clientReady){ + client.end(); + } + }; + + + function getDbBackupFileCounter(type) + { + var files = fs.readdirSync(__dirname + "/../databases"); + + let counter = 0; + for(var i = 0; i < files.length; i++) + { + + if(files[i] == "tbdatacloud.nosql") continue; + + if(files[i].endsWith(".nosql")) + { + + let pos = files[i].indexOf("."); + if(pos > -1) + { + + let fileCounter = counter; + let firstDigit = files[i].slice(0, pos); + + fileCounter = parseInt(firstDigit); + if(isNaN(fileCounter)) fileCounter = 0; + //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); + + if(type == "max") + { + if(fileCounter > counter) + { + counter = fileCounter; + } + } + else if(type == "min") + { + if(counter == 0) counter = fileCounter; + + if(fileCounter < counter) + { + counter = fileCounter; + } + } + } + } + + } + + if(type == "max") counter++; + + return counter; + } + + const makeBackupFromDbFile = async () => { + + if(!saveTelemetryOnError) return; + + //to avoid large file: tbdata.nosql + + //init value is 0! + if(insertNoSqlCounter > 0) + { + --insertNoSqlCounter; + return; + } + + insertNoSqlCounter = 100; + + let source = __dirname + "/../databases/tbdatacloud.nosql"; + + var stats = fs.statSync(source); + var fileSizeInBytes = stats.size; + + if(fileSizeInBytes > noSqlFileSizeLimit) + { + + let counter = 1; + counter = getDbBackupFileCounter("max"); + + let destination = __dirname + "/../databases/" + counter + "." + "tbdatacloud.nosql"; + + //make backup file + fs.copyFileSync(source, destination); + //fs.renameSync(p, p + "." + counter); + + //clear tbdata.nosql + fs.writeFileSync(source, ""); + fs.truncateSync(source, 0); + + } + } + + const processDataFromDatabase = async () => { + + if(restore_from_backup <= 0) return; + + //calculate diff + const now = new Date(); + let currentTime = now.getTime(); + let diff = currentTime - lastRestoreTime; + + if( (diff / 1000) < restore_backup_wait) + { + //console.log("*********restore_backup_wait", diff, restore_backup_wait); + return; + } + + processingData = true; + + //get filename to process + let counter = getDbBackupFileCounter("min"); + + //we have some backup files + let dataBase = 'tbdata'; + + var nosql; + if(counter == 0) dataBase = 'tbdatacloud'; + else dataBase = counter + "." + 'tbdatacloud'; + + nosql = NOSQL(dataBase); + + //select all data - use limit restore_from_backup + let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); + + for(let i = 0; i < records.length; i++) + { + if(clientReady) { + + let item = records[i]; + let id = item.id; + + if(id !== undefined) + { + //console.log("------------processDataFromDatabase - remove", id, dataBase, i); + + try { - client.on('error', function(err) { - instance.status("Err: " + err.code, "red"); - instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); - if (sendClientError) { - console.log('MQTT client error', err); - sendClientError = false; - } - clientReady = false; - }); + let message = JSON.parse(JSON.stringify(item)); + delete message.id; + client.publish(`${o.topic}_forward`, JSON.stringify(message), {qos:1}); + + //remove from database + await promisifyBuilder(nosql.remove().where("id", id)); - } + } catch(error) { + //process error + console.log("processDataFromDatabase", error); + } + } + + } + else + { + processingData = false; + return; + } + } - instance.on('0', function(data) { + if(records.length > 0) + { + //clean backup file + if(counter > 0) nosql.clean(); + } - if (clientReady) { - //do we have some data in backup file? if any, process data from database - if (saveTelemetryOnError) { - //read telemetry data and send back to server - if (!processingData) processDataFromDatabase(); - } - - let stringifiedJson = JSON.stringify(data.data) - client.publish(`${o.topic}_forward`, stringifiedJson, { qos: 1 }); - } - else { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); - instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); - - if (saveTelemetryOnError) { - //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql - makeBackupFromDbFile(); - - //write to tb - data.data.id = UID(); - nosql.insert(data.data); - } - } - }); - - instance.on("1", _ => { - main(); - }) - - instance.close = function(done) { - if (clientReady) { - client.end(); - } - }; - - - function getDbBackupFileCounter(type) { - var files = fs.readdirSync(__dirname + "/../databases"); - - let counter = 0; - for (var i = 0; i < files.length; i++) { - - if (files[i] == "tbdatacloud.nosql") continue; - - if (files[i].endsWith(".nosql")) { - - let pos = files[i].indexOf("."); - if (pos > -1) { - - let fileCounter = counter; - let firstDigit = files[i].slice(0, pos); - - fileCounter = parseInt(firstDigit); - if (isNaN(fileCounter)) fileCounter = 0; - //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); - - if (type == "max") { - if (fileCounter > counter) { - counter = fileCounter; - } - } - else if (type == "min") { - if (counter == 0) counter = fileCounter; - - if (fileCounter < counter) { - counter = fileCounter; - } - } - } - } - - } - - if (type == "max") counter++; - - return counter; - } - - const makeBackupFromDbFile = async () => { - - if (!saveTelemetryOnError) return; - - //to avoid large file: tbdata.nosql - - //init value is 0! - if (insertNoSqlCounter > 0) { - --insertNoSqlCounter; - return; - } - - insertNoSqlCounter = 100; - - let source = __dirname + "/../databases/tbdatacloud.nosql"; - - var stats = fs.statSync(source); - var fileSizeInBytes = stats.size; - - if (fileSizeInBytes > noSqlFileSizeLimit) { - - let counter = 1; - counter = getDbBackupFileCounter("max"); - - let destination = __dirname + "/../databases/" + counter + "." + "tbdatacloud.nosql"; - - //make backup file - fs.copyFileSync(source, destination); - //fs.renameSync(p, p + "." + counter); - - //clear tbdata.nosql - fs.writeFileSync(source, ""); - fs.truncateSync(source, 0); - - } - } - - const processDataFromDatabase = async () => { - - if (restore_from_backup <= 0) return; - - //calculate diff - const now = new Date(); - let currentTime = now.getTime(); - let diff = currentTime - lastRestoreTime; - - if ((diff / 1000) < restore_backup_wait) { - //console.log("*********restore_backup_wait", diff, restore_backup_wait); - return; - } - - processingData = true; - - //get filename to process - let counter = getDbBackupFileCounter("min"); - - //we have some backup files - let dataBase = 'tbdata'; - - var nosql; - if (counter == 0) dataBase = 'tbdatacloud'; - else dataBase = counter + "." + 'tbdatacloud'; - - nosql = NOSQL(dataBase); - - //select all data - use limit restore_from_backup - let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); - - for (let i = 0; i < records.length; i++) { - if (clientReady) { - - let item = records[i]; - let id = item.id; - - if (id !== undefined) { - //console.log("------------processDataFromDatabase - remove", id, dataBase, i); - - try { - - let message = JSON.parse(JSON.stringify(item)); - delete message.id; - client.publish(`${o.topic}_forward`, JSON.stringify(message), { qos: 1 }); - - //remove from database - await promisifyBuilder(nosql.remove().where("id", id)); - - } catch (error) { - //process error - console.log("processDataFromDatabase", error); - } - - } - - } - else { - processingData = false; - return; - } - } - - if (records.length > 0) { - //clean backup file - if (counter > 0) nosql.clean(); - } - - //no data in db, remove - if (records.length == 0) { - if (counter > 0) nosql.drop(); - } - - const d = new Date(); - lastRestoreTime = d.getTime(); - - processingData = false; - - } - - instance.on('options', main); + //no data in db, remove + if(records.length == 0) + { + if(counter > 0) nosql.drop(); + } + + const d = new Date(); + lastRestoreTime = d.getTime(); + + processingData = false; + + } + + instance.on('options', main); }; diff --git a/flow/cmd_manager.js b/flow/cmd_manager.js index d5f0842..07e12c6 100644 --- a/flow/cmd_manager.js +++ b/flow/cmd_manager.js @@ -30,2765 +30,2769 @@ exports.readme = `Manager for CMD calls`; exports.install = function(instance) { - const { SerialPort } = require('serialport'); - const { exec } = require('child_process'); - const { crc16 } = require('easy-crc'); - const { runSyncExec, writeData } = require('./helper/serialport_helper'); - const { bytesToInt, longToByteArray, addZeroBefore } = require('./helper/utils'); - const bitwise = require('bitwise'); - - var SunCalc = require('./helper/suncalc'); - const DataToTbHandler = require('./helper/DataToTbHandler'); - const errorHandler = require('./helper/ErrorToServiceHandler'); - const { sendNotification } = require('./helper/notification_reporter'); - const process = require('process'); - const { errLogger, logger, monitor } = require('./helper/logger'); - - //for accelerometer purposes - const { naklony } = require("../databases/accelerometer_db"); - - const dbNodes = TABLE("nodes"); - const dbRelays = TABLE("relays"); - - let GLOBALS; - let SETTINGS; - let rsPort; - let tbHandler; - - // runTasks intervals - const SHORT_INTERVAL = 30; - const LONG_INTERVAL = 300; - - //send data to following instances: - const SEND_TO = { - debug: 0, - tb: 1, - http_response: 2, - dido_controller: 3, - infoSender: 4 - } - - const PRIORITY_TYPES = { - terminal: 0, - fw_detection: 1,//reserved only for FW detection - SETTINGS.masterNodeIsResponding - high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) - relay_profile: 3, - node_broadcast: 4, - node_profile: 5, - node_cmd: 6 - } - - const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes - - //list of command calls to process. Processing in runTasks function - let tasks = []; - - let interval = null;//timeout for procesing tasks - let customTasksInterval = null; // interval for reportEdgeDateTimeAndNumberOfLuminaires(); - let setCorrectTime = null; // interval for setting a correct edgeTime - let sendNodeReadout = null; // interval for sending agregate data from node - - let refFlowdataObj = {}; - - //load from settings - let latitude = 48.70826502;//48.682255758; - let longitude = 17.28455203;//17.278910807; - - const gmtOffset = 0; - - //ak nie je nastaveny - //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ - //https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates - - //priorities for registers - let priorities = []; - - let minutes = 1; - priorities["1"] = minutes; - priorities["76"] = minutes; - - // minutes = 5; - priorities["75"] = minutes;//current - priorities["79"] = minutes;//energy - priorities["87"] = minutes;//aktualny cas - //priorities["84"] = minutes; - - // minutes = 10; - priorities["74"] = minutes; - priorities["77"] = minutes; - priorities["78"] = minutes; - - // minutes = 60; - priorities["0"] = minutes; - priorities["6"] = minutes; - priorities["7"] = minutes; - priorities["8"] = minutes; - - // minutes = 60 * 24; - priorities["89"] = minutes; - priorities["80"] = minutes; - - //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) - let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; - - let rotary_switch_state; - let lux_sensor; - let state_of_breaker = {};//key is line, value is On/Off - let disconnectedReport = {};//key is tbname, value true/false - - let relaysData; - let nodesData; - - let sunCalcResult; - let reportDuskDawn; - - //helper container for counting resolved group of commands (commands related to set profile) - let cmdCounter = {};//key is node, value is counter - - //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice - const nodeProfileSendFail = new Set(); - - //we expect to get current temperature in Senica from senica-prod01 - let temperatureInSenica = null; - let accelerometerInterval = null; - - //END OF VARIABLE SETTINGS - //-------------------------------- - - - function main() { - GLOBALS = FLOW.GLOBALS; - SETTINGS = FLOW.GLOBALS.settings; - relaysData = GLOBALS.relaysData; - nodesData = GLOBALS.nodesData; - latitude = GLOBALS.settings.latitude; - longitude = GLOBALS.settings.longitude; - - tbHandler = new DataToTbHandler(SEND_TO.tb); - tbHandler.setSender(exports.title); - - let now = new Date(); - console.log("CMD Manager installed", now.toLocaleString("sk-SK")); - - sunCalcResult = calculateDuskDawn(); - - reportDuskDawn = { - dusk_time: sunCalcResult.dusk_time, - dawn_time: sunCalcResult.dawn_time, - dusk_time_reported: undefined, - dawn_time_reported: undefined - }; + const SerialPort = require('serialport'); + const { exec } = require('child_process'); + const { crc16 } = require('easy-crc'); + const { runSyncExec, writeData } = require('./helper/serialport_helper'); + const { bytesToInt, longToByteArray, addZeroBefore } = require('./helper/utils'); + const bitwise = require('bitwise'); + + var SunCalc = require('./helper/suncalc'); + const DataToTbHandler = require('./helper/DataToTbHandler'); + const errorHandler = require('./helper/ErrorToServiceHandler'); + const { sendNotification } = require('./helper/notification_reporter'); + const process = require('process'); + const { errLogger, logger, monitor } = require('./helper/logger'); + + //for accelerometer purposes + const { naklony } = require("../databases/accelerometer_db"); + + const dbNodes = TABLE("nodes"); + const dbRelays = TABLE("relays"); + + let GLOBALS; + let SETTINGS; + let rsPort; + let tbHandler; + + // runTasks intervals + const SHORT_INTERVAL = 30; + const LONG_INTERVAL = 300; + + //send data to following instances: + const SEND_TO = { + debug: 0, + tb: 1, + http_response: 2, + dido_controller: 3, + infoSender: 4 + } + + const PRIORITY_TYPES = { + terminal: 0, + fw_detection: 1,//reserved only for FW detection - SETTINGS.masterNodeIsResponding + high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform) + relay_profile: 3, + node_broadcast: 4, + node_profile: 5, + node_cmd: 6 + } + + const TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION = 600000; // 10 minutes + + //list of command calls to process. Processing in runTasks function + let tasks = []; + + let interval = null;//timeout for procesing tasks + let customTasksInterval = null; // interval for reportEdgeDateTimeAndNumberOfLuminaires(); + let setCorrectTime = null; // interval for setting a correct edgeTime + let sendNodeReadout = null; // interval for sending agregate data from node + + let refFlowdataObj = {}; + + //load from settings + let latitude = 48.70826502;//48.682255758; + let longitude = 17.28455203;//17.278910807; + + const gmtOffset = 0; + + //ak nie je nastaveny + //https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/ + //https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates + + //priorities for registers + let priorities = []; + + let minutes = 1; + priorities["1"] = minutes; // dimming + priorities["76"] = minutes; // power + + minutes = 5; + priorities["75"] = minutes; // current + priorities["79"] = minutes; // energy + priorities["87"] = minutes; // aktualny cas + //priorities["84"] = minutes; + + minutes = 10; + priorities["74"] = minutes; // voltage + priorities["77"] = minutes; // power factor + priorities["78"] = minutes; // frequency + + minutes = 60; + priorities["0"] = minutes; // statecode + priorities["6"] = minutes; // dusk + priorities["7"] = minutes; // dawn + priorities["8"] = minutes; // profile + + minutes = 60 * 24; + priorities["89"] = minutes; // verzia fw + priorities["80"] = minutes; // lifetime + + //prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming) + let listOfCommands = [0, 1, 6, 7, 8, 74, 75, 76, 77, 78, 79, 80, 87, 89]; + + let rotary_switch_state; + let lux_sensor; + let state_of_breaker = {};//key is line, value is On/Off + let disconnectedReport = {};//key is tbname, value true/false + + let relaysData; + let nodesData; + + let sunCalcResult; + let reportDuskDawn; + + //helper container for counting resolved group of commands (commands related to set profile) + let cmdCounter = {};//key is node, value is counter + + //if sending of profile to node fails, we send notification and push node into set, so we do not send notification twice + const nodeProfileSendFail = new Set(); + + //we expect to get current temperature in Senica from senica-prod01 + let temperatureInSenica = null; + let accelerometerInterval = null; + + //END OF VARIABLE SETTINGS + //-------------------------------- + + + function main() { + GLOBALS = FLOW.GLOBALS; + SETTINGS = FLOW.GLOBALS.settings; + relaysData = GLOBALS.relaysData; + nodesData = GLOBALS.nodesData; + latitude = GLOBALS.settings.latitude; + longitude = GLOBALS.settings.longitude; + + tbHandler = new DataToTbHandler(SEND_TO.tb); + tbHandler.setSender(exports.title); + + let now = new Date(); + console.log("Cmd-mngr installed", now.toLocaleString("sk-SK")); + + sunCalcResult = calculateDuskDawn(); + + reportDuskDawn = { + dusk_time: sunCalcResult.dusk_time, + dawn_time: sunCalcResult.dawn_time, + dusk_time_reported: undefined, + dawn_time_reported: undefined + }; - handleRsPort(); + handleRsPort(); - customTasksInterval = setInterval(function() { - reportEdgeDateTimeAndNumberOfLuminaires(); - }, 120000); - reportEdgeDateTimeAndNumberOfLuminaires(); + customTasksInterval = setInterval(function() { + reportEdgeDateTimeAndNumberOfLuminaires(); + }, 120000); + reportEdgeDateTimeAndNumberOfLuminaires(); - setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour - setCorrectPlcTimeOnceADay(); + setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour + setCorrectPlcTimeOnceADay(); - sendNodeReadout = setInterval(sendNodesData, 150000); - accelerometerInterval = setInterval(accelerometerData, 60000 * 30); //30 min - } - - - function cmdCounterResolve(address) { - if (cmdCounter.hasOwnProperty(address)) { - cmdCounter[address] = cmdCounter[address] - 1; - - let result = cmdCounter[address]; - if (result == 0) delete cmdCounter[address]; - return result; - } - return -1; - } - - - function getParams(priority) { - let params = {}; - - //core rpc values - params.address = 0;//if(recipient === 0) address = 0; - params.byte1 = 0;//msb, podla dokumentacie data3 - params.byte2 = 0;//podla dokumentacie data2 - params.byte3 = 0;//podla dokumentacie data1 - params.byte4 = 0;//lsb, podla dokumentacie data0 - params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast - params.register = -1;//register number - params.rw = 0;//0: read, 1: write - - //other values - //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" - //params.tbname = tbname; - params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority - params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed - if (priority != undefined) { - params.timestamp = priority; - params.priority = priority; - } + sendNodeReadout = setInterval(sendNodesData, 150000); + accelerometerInterval = setInterval(accelerometerData, 60000 * 30); //30 min + } + + + function cmdCounterResolve(address) { + if (cmdCounter.hasOwnProperty(address)) { + cmdCounter[address] = cmdCounter[address] - 1; + + let result = cmdCounter[address]; + if (result == 0) delete cmdCounter[address]; + return result; + } + return -1; + } + + + function getParams(priority) { + let params = {}; + + //core rpc values + params.address = 0;//if(recipient === 0) address = 0; + params.byte1 = 0;//msb, podla dokumentacie data3 + params.byte2 = 0;//podla dokumentacie data2 + params.byte3 = 0;//podla dokumentacie data1 + params.byte4 = 0;//lsb, podla dokumentacie data0 + params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast + params.register = -1;//register number + params.rw = 0;//0: read, 1: write + + //other values + //params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" + //params.tbname = tbname; + params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority + params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed + if (priority != undefined) { + params.timestamp = priority; + params.priority = priority; + } - params.addMinutesToTimestamp = 0;//repeat task if value is > 0 - //if node regular readout does not respond, we repeat request - params.repeatCounter = 0; - //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" - //params.info = ""; - //params.debug = true; // will console.log params in writeData response - - return params; - } - - - //nastav profil nodu - function processNodeProfile(node) { - if (rotary_switch_state != "Automatic") { - logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); - return; - } - - let nodeObj = nodesData[node]; - let line = nodeObj.line; - - if (relaysData[line].contactor == 0) { - logger.debug("line line is off", line, node); - return; - } - - if (nodeObj.processed == 1) { - //logger.debug("node was already processed", node); - return; - } - - let nodeProfile = nodeObj.profile; - logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); - if (nodeProfile) { - - try { - nodeProfile = JSON.parse(nodeProfile); - } catch (error) { - logger.debug("Cmd_manager - Error parsing node profile", error); - } - - } - - logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); - - let timestamp = PRIORITY_TYPES.node_cmd; - - removeTask({ type: "set_node_profile", address: node }); - - if (nodeProfile === "") { - //vypneme profil nodu, posleme cmd - //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. - //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia - - logger.debug("turn off profile"); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = 'turn off/reset node profile'; - - cmdCounter[node] = 1; - - tasks.push(params); - } - else { - let tasksProfile = []; - - //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte4 = 96; - params.recipient = 1; - params.register = 8; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = 'turn off node profile'; - - tasksProfile.push(params); - - timestamp++; - - logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); - - //TS1 Time point a TS1 Time Point Levels - let register = 9; - for (let i = 0; i < nodeProfile.intervals.length; i++) { - let obj = nodeProfile.intervals[i]; - //let timePoint = obj.time_point; - let dim_value = obj.value; - - - //Reg 9 až Reg 40 - - /* - Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. - Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). - Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. - Časový bod má formát: - Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované - Register úrovne má rovnaký formát ako dimming register (Reg 1). - */ - - let start_time = obj.start_time; - let t = start_time.split(":"); - //if(timePoint != undefined) t = timePoint.split(":"); - //else t = [0,0]; - - logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte1 = parseInt(t[0]);//hh - params.byte2 = parseInt(t[1]);//mm - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.byte4 = parseInt(dim_value) + 128;// - params.recipient = 1; - params.register = register; - params.rw = 1;//write - params.timestamp = timestamp; - params.addMinutesToTimestamp = 0; - params.info = 'TS1 Time point Levels ' + (i + 1); - - tasksProfile.push(params); - - register++; - timestamp++; - } - - //Threshold lux level for DUSK/DAWN - { - - logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); - - let params = getParams(); - params.type = "set_node_profile"; - params.address = node; - params.register = 96; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = "Threshold lux level for DUSK/DAWN"; - - if (nodeProfile.dusk_lux_sensor) { - let v = nodeProfile.dusk_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if (nodeProfile.dawn_lux_sensor) { - let v = nodeProfile.dawn_lux_sensor_value; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //DUSK/DAWN max. adjust period - { - - logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); - - let params = getParams(); - params.type = "set_node_profile"; - params.address = node; - params.register = 97; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = "DUSK/DAWN max. adjust period"; - - if (nodeProfile.astro_clock) { - let v = nodeProfile.dusk_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte1 = ba[1];//msb - params.byte2 = ba[0]; - } - - if (nodeProfile.astro_clock) { - let v = nodeProfile.dawn_lux_sensor_time_window; - let ba = longToByteArray(v); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - } - - tasksProfile.push(params); - timestamp++; - - } - - //Static offset - { - - //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: - //Bity 0 – 6: hodnota v minútach - //Bit 7: znamienko (1 – mínus) - - logger.debug("processNodeProfile: Static offset", node); - - let params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 98; - params.recipient = 1; - params.rw = 1;//write - params.timestamp = timestamp; - params.info = "Static offset"; - - if (nodeProfile.astro_clock) { - let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); - let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); - - if (dusk_astro_clock_offset < 0) { - params.byte3 = (dusk_astro_clock_offset * -1) + 128; - } - else { - params.byte3 = dusk_astro_clock_offset; - } - - if (dawn_astro_clock_offset < 0) { - params.byte4 = (dawn_astro_clock_offset * -1) + 128; - } - else { - params.byte4 = dawn_astro_clock_offset; - } - } - - tasksProfile.push(params); - timestamp++; - } - - logger.debug("Time schedule settings - turn on", node); - - params = getParams(PRIORITY_TYPES.node_cmd); - params.type = "set_node_profile"; - params.address = node; - params.register = 8; - params.recipient = 1; - params.rw = 1;//write - - //Time schedule settings - let bits = []; - - //Byte 0 (LSB): - //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). - bits.push(1); - //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) - bits.push(0); - bits.push(0); - bits.push(0); - if (nodeProfile.astro_clock == true) { - //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý - bits.push(1); - } - else bits.push(0); - - //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) - bits.push(0); - - //Bity 6-7 - zatiaľ nepoužité - bits.push(0); - bits.push(0); - - params.byte4 = bitwise.byte.write(bits.reverse()); - - //Byte 2 – nastavenie pre lux senzor: - bits = []; - - //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if (nodeProfile.dusk_lux_sensor == true)//sumrak - { - bits.push(1); - } - else bits.push(0); - - //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia - if (nodeProfile.dawn_lux_sensor == true)//usvit - { - bits.push(1); - } - else bits.push(0); - - //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. - bits.push(0);//zatial neimplementovane - - //Bit 3 – 7 - nepoužité - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - bits.push(0); - - params.byte2 = bitwise.byte.write(bits.reverse()); - params.timestamp = timestamp; - params.info = "Time schedule settings - turn on"; - - tasksProfile.push(params); - - //zaver - cmdCounter[node] = tasksProfile.length; - - //tasks.push(tasksProfile); - tasks = tasks.concat(tasksProfile); - - } - - logger.debug("finished set profile for ", node); - - console.log("proces profile finished *********************") - } - - - function cleanUpRefFlowdataObj() { - let now = new Date(); - let timestamp = now.getTime(); + params.addMinutesToTimestamp = 0;//repeat task if value is > 0 + // if node regular readout does not respond, we repeat request + params.repeatCounter = 0; + //params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint" + //params.info = ""; + //params.debug = true; // will console.log params in writeData response + + return params; + } + + + //nastav profil nodu + function processNodeProfile(node) { + if (rotary_switch_state != "Automatic") { + logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic"); + return; + } + + let nodeObj = nodesData[node]; + let line = nodeObj.line; + + if (relaysData[line].contactor == 0) { + logger.debug("line line is off", line, node); + return; + } + + if (nodeObj.processed == 1) { + //logger.debug("node was already processed", node); + return; + } + + let nodeProfile = nodeObj.profile; + logger.debug("processNodeProfile: start - set profile for ", node, nodeProfile); + if (nodeProfile) { + + try { + nodeProfile = JSON.parse(nodeProfile); + } catch (error) { + logger.debug("Cmd-mngr: Error parsing node profile", error); + } + + } + + logger.debug("processNodeProfile", node, line, nodeObj, nodeProfile); + + let timestamp = PRIORITY_TYPES.node_cmd; + + removeTask({ type: "set_node_profile", address: node }); + + if (nodeProfile === "") { + //vypneme profil nodu, posleme cmd + //Pokiaľ je hodnota rovná 1 – Profil sa zapne, ostatné bity sa nezmenia. + //Pokiaľ sa hodnota rovná 2 – profil sa vypne, ostatné bity sa nezmenia + + logger.debug("turn off profile"); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = 'turn off/reset node profile'; + + cmdCounter[node] = 1; + + tasks.push(params); + } + else { + let tasksProfile = []; + + //vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings – reset profilu + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte4 = 96; + params.recipient = 1; + params.register = 8; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = 'turn off node profile'; + + tasksProfile.push(params); + + timestamp++; + + logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node); + + //TS1 Time point a TS1 Time Point Levels + let register = 9; + for (let i = 0; i < nodeProfile.intervals.length; i++) { + let obj = nodeProfile.intervals[i]; + //let timePoint = obj.time_point; + let dim_value = obj.value; + + + //Reg 9 až Reg 40 + + /* + Samotný profil sa zapisuje do max. 16 párov – časový bod a úroveň. + Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8). + Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00. + Časový bod má formát: + Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 – rezervované + Register úrovne má rovnaký formát ako dimming register (Reg 1). + */ + + let start_time = obj.start_time; + let t = start_time.split(":"); + //if(timePoint != undefined) t = timePoint.split(":"); + //else t = [0,0]; + + logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte1 = parseInt(t[0]);//hh + params.byte2 = parseInt(t[1]);//mm + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.byte4 = parseInt(dim_value) + 128;// + params.recipient = 1; + params.register = register; + params.rw = 1;//write + params.timestamp = timestamp; + params.addMinutesToTimestamp = 0; + params.info = 'TS1 Time point Levels ' + (i + 1); + + tasksProfile.push(params); + + register++; + timestamp++; + } + + //Threshold lux level for DUSK/DAWN + { + + logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node); + + let params = getParams(); + params.type = "set_node_profile"; + params.address = node; + params.register = 96; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = "Threshold lux level for DUSK/DAWN"; + + if (nodeProfile.dusk_lux_sensor) { + let v = nodeProfile.dusk_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if (nodeProfile.dawn_lux_sensor) { + let v = nodeProfile.dawn_lux_sensor_value; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //DUSK/DAWN max. adjust period + { + + logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node); + + let params = getParams(); + params.type = "set_node_profile"; + params.address = node; + params.register = 97; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = "DUSK/DAWN max. adjust period"; + + if (nodeProfile.astro_clock) { + let v = nodeProfile.dusk_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte1 = ba[1];//msb + params.byte2 = ba[0]; + } + + if (nodeProfile.astro_clock) { + let v = nodeProfile.dawn_lux_sensor_time_window; + let ba = longToByteArray(v); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + } + + tasksProfile.push(params); + timestamp++; + + } + + //Static offset + { + + //Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát: + //Bity 0 – 6: hodnota v minútach + //Bit 7: znamienko (1 – mínus) + + logger.debug("processNodeProfile: Static offset", node); + + let params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 98; + params.recipient = 1; + params.rw = 1;//write + params.timestamp = timestamp; + params.info = "Static offset"; + + if (nodeProfile.astro_clock) { + let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset); + let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset); + + if (dusk_astro_clock_offset < 0) { + params.byte3 = (dusk_astro_clock_offset * -1) + 128; + } + else { + params.byte3 = dusk_astro_clock_offset; + } + + if (dawn_astro_clock_offset < 0) { + params.byte4 = (dawn_astro_clock_offset * -1) + 128; + } + else { + params.byte4 = dawn_astro_clock_offset; + } + } + + tasksProfile.push(params); + timestamp++; + } + + logger.debug("Time schedule settings - turn on", node); + + params = getParams(PRIORITY_TYPES.node_cmd); + params.type = "set_node_profile"; + params.address = node; + params.register = 8; + params.recipient = 1; + params.rw = 1;//write + + //Time schedule settings + let bits = []; + + //Byte 0 (LSB): + //Bit 0 (LSB) – zapnutie/vypnutie profilov ako takých (1 – zapnuté). + bits.push(1); + //Bit 1 – 3 - zatiaľ nepoužité (zapisovať 0) + bits.push(0); + bits.push(0); + bits.push(0); + if (nodeProfile.astro_clock == true) { + //Bit 4 – ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý + bits.push(1); + } + else bits.push(0); + + //Bit 5 – zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu) + bits.push(0); + + //Bity 6-7 - zatiaľ nepoužité + bits.push(0); + bits.push(0); + + params.byte4 = bitwise.byte.write(bits.reverse()); + + //Byte 2 – nastavenie pre lux senzor: + bits = []; + + //Bit 0 (LSB) – riadenie súmraku podľa lux senzoru (1 – zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if (nodeProfile.dusk_lux_sensor == true)//sumrak + { + bits.push(1); + } + else bits.push(0); + + //Bit 1 - riadenie úsvitu podľa lux senzoru (1 – zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia + if (nodeProfile.dawn_lux_sensor == true)//usvit + { + bits.push(1); + } + else bits.push(0); + + //Bit 2 – zdroj pre hodnotu luxov – 0 – RVO posiela hodnoty zo svojho luxmetra, 1 – node má pripojený svoj vlastný lux meter. + bits.push(0);//zatial neimplementovane + + //Bit 3 – 7 - nepoužité + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + bits.push(0); + + params.byte2 = bitwise.byte.write(bits.reverse()); + params.timestamp = timestamp; + params.info = "Time schedule settings - turn on"; + + tasksProfile.push(params); + + //zaver + cmdCounter[node] = tasksProfile.length; + + //tasks.push(tasksProfile); + tasks = tasks.concat(tasksProfile); + + } + + logger.debug("finished set profile for ", node); + + console.log("proces profile finished *********************") + } + + + function cleanUpRefFlowdataObj() { + let now = new Date(); + let timestamp = now.getTime(); - //clear old refFlowdata references - let keys = Object.keys(refFlowdataObj); - for (let i = 0; i < keys.length; i++) { - let timestampKey = keys[i]; + //clear old refFlowdata references + let keys = Object.keys(refFlowdataObj); + for (let i = 0; i < keys.length; i++) { + let timestampKey = keys[i]; - if ((timestamp - timestampKey) > 60 * 1000) { - console.log("cleanUpRefFlowdataObj delete", timestampKey); - delete refFlowdataObj[timestampKey]; - } - } - } + if ((timestamp - timestampKey) > 60 * 1000) { + console.log("cleanUpRefFlowdataObj delete", timestampKey); + delete refFlowdataObj[timestampKey]; + } + } + } - function removeTask(obj) { - let keys = Object.keys(obj); - tasks = tasks.filter((task) => { + function removeTask(obj) { + let keys = Object.keys(obj); + tasks = tasks.filter((task) => { - let counter = 0; - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - if (task.hasOwnProperty(key) && obj.hasOwnProperty(key)) { - if (task[key] == obj[key]) counter++; - } - } + let counter = 0; + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + if (task.hasOwnProperty(key) && obj.hasOwnProperty(key)) { + if (task[key] == obj[key]) counter++; + } + } - if (counter == keys.length) return false; - return true; - }); - } + if (counter == keys.length) return false; + return true; + }); + } - process.on('uncaughtException', function(err) { - //TODO send to service + process.on('uncaughtException', function(err) { + //TODO send to service - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); + errLogger.error('uncaughtException:', err.message) + errLogger.error(err.stack); - errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); - //process.exit(1); - }) + errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error"); + //process.exit(1); + }) - //te();//force error + //te();//force error - function processAllNodeProfilesOnLine(line) { - for (let k in nodesData) { - if (line == nodesData[k].line) { - let node = nodesData[k].node; - let processed = nodesData[k].processed; - - if (!processed) processNodeProfile(node); - //else logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); - } - } - } - - - function loadRelaysData(line) { - for (const [key, value] of Object.entries(relaysData)) { - if (key == "0") continue; - if (line != undefined) { - //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData - if (line != value.line) continue; - } - - if (value.contactor == 1) processAllNodeProfilesOnLine(value.line); - } - } - - - function reportOnlineNodeStatus(line) { - //Po zapnutí línie broadcastovo aktualizovať predtým čas a o 3 sek neskor - status, brightness - - logger.debug("Cmd-mngr: ----->reportOnlineNodeStatus for line", line); + function processAllNodeProfilesOnLine(line) { + for (let k in nodesData) { + if (line == nodesData[k].line) { + let node = nodesData[k].node; + let processed = nodesData[k].processed; + + if (!processed) processNodeProfile(node); + //else logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`); + } + } + } + + + function loadRelaysData(line) { + for (const [key, value] of Object.entries(relaysData)) { + if (key == "0") continue; + if (line != undefined) { + //ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData + if (line != value.line) continue; + } + + if (value.contactor == 1) processAllNodeProfilesOnLine(value.line); + } + } + + + function reportOnlineNodeStatus(line) { + //Po zapnutí línie broadcastovo aktualizovať predtým čas a o 3 sek neskor - status, brightness + + logger.debug("Cmd-mngr: ----->reportOnlineNodeStatus for line", line); - const d = new Date(); + const d = new Date(); - // broadcast actual time - let params = getParams(); - params.address = 0xffffffff;//Broadcast - params.byte1 = d.getHours(); - params.byte2 = d.getMinutes(); - params.recipient = 2;//2 broadcast, address = 0 - params.register = 87;//Actual time - params.rw = 1;//write - params.type = "node-onetime-write"; - params.timestamp = d.getTime() + 30000; - params.info = "run broadcast: Actual time"; - //params.debug = true; + // broadcast actual time + let params = getParams(); + params.address = 0xffffffff;//Broadcast + params.byte1 = d.getHours(); + params.byte2 = d.getMinutes(); + params.recipient = 2;//2 broadcast, address = 0 + params.register = 87;//Actual time + params.rw = 1;//write + params.type = "node-onetime-write"; + params.timestamp = d.getTime() + 30000; + params.info = "run broadcast: Actual time"; + //params.debug = true; - tasks.push(params); + tasks.push(params); - let sec = 3; - setTimeout(function() { - //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel + let sec = 3; + setTimeout(function() { + //Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel - let time = Date.now(); - - for (let k in nodesData) { + let time = Date.now(); + + for (let k in nodesData) { - //potrebujem nody k danej linii - if (line == nodesData[k].line || line == undefined) { + //potrebujem nody k danej linii + if (line == nodesData[k].line || line == undefined) { - let tbname = nodesData[k].tbname; - let node = nodesData[k].node; - let status = "NOK"; + let tbname = nodesData[k].tbname; + let node = nodesData[k].node; + let status = "NOK"; - // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. - if (nodesData[k].node_status_before_offline === true || nodesData[k].status === true) { - status = "OK"; - nodesData[k].time_of_last_communication = time; - } + // if status of node was "OK" before switching it off, we set the node's time_of_last_communication on time, it was switched on again and send OK status to tb. + if (nodesData[k].node_status_before_offline === true || nodesData[k].status === true) { + status = "OK"; + nodesData[k].time_of_last_communication = time; + } - nodesData[k].readout.status = status; + nodesData[k].readout.status = status; - updateNodeStatus(k, status === "OK" ? true : false); - if (nodesData[k].hasOwnProperty("node_status_before_offline")) delete nodesData[k].node_status_before_offline; - sendTelemetry({ status: status }, tbname, time); + updateNodeStatus(k, status === "OK" ? true : false); + if (nodesData[k].hasOwnProperty("node_status_before_offline")) delete nodesData[k].node_status_before_offline; + sendTelemetry({ status: status }, tbname, time); - //vyreportovanie dimming, current, input power pre liniu pre vsetky nody - //Prud - { - let params = getParams(); + //vyreportovanie dimming, current, input power pre liniu pre vsetky nody + //Prud + { + let params = getParams(); - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 75;//prud - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = time + 4000; - params.info = 'read current'; - //params.debug = true; - tasks.push(params); - } + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 75;//prud + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 4000; + params.info = 'read current'; + //params.debug = true; + tasks.push(params); + } - //vykon - { - let params = getParams(); + //vykon + { + let params = getParams(); - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 76;//výkon - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = time + 4100; - params.info = 'read power'; - //params.debug = true; - - tasks.push(params); - } - //dimming - { - let params = getParams(); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 1;//dimming - params.recipient = 1;//slave - params.rw = 0;//read - params.timestamp = time + 4200; - params.info = 'read dimming'; - //params.debug = true; + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 76;//výkon + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 4100; + params.info = 'read power'; + //params.debug = true; + + tasks.push(params); + } + //dimming + { + let params = getParams(); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 1;//dimming + params.recipient = 1;//slave + params.rw = 0;//read + params.timestamp = time + 4200; + params.info = 'read dimming'; + //params.debug = true; + + tasks.push(params); + } - tasks.push(params); - } + } + } - } - } + }, sec * 1000); + } + + + function reportOfflineNodeStatus(line) { + + logger.info("Cmd-mngr: ------>reportOffLineNodeStatus for line ", line); + + values = {}; + values["dimming"] = 0;//brightness + values["power"] = 0;//výkon + values["current"] = 0;//prúd + values["status"] = "OFFLINE"; + + const date = Date.now(); + + Object.keys(nodesData).forEach(node => { + + //potrebujem nody k danej linii + if (line == nodesData[node].line || line == undefined) { + + let tbname = nodesData[node].tbname; + let nodeStatus = nodesData[node].status; + + //in case we have reported offline node status, we return (continue with next node) + if (nodeStatus === "OFFLINE") return; + + nodesData[node].node_status_before_offline = nodeStatus; + nodesData[node].status = "OFFLINE"; + nodesData[node].readout = {}; + + sendTelemetry({ ...values }, tbname, date); + } + }); + + } + + + function turnLine(onOrOff, line, info) { + let obj = { + line: line, + command: onOrOff, + info: info + }; + + //logger.debug("linia", line, obj); + instance.send(SEND_TO.dido_controller, obj); + } + + + function detectIfResponseIsValid(bytes) { + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let type = "RESPONSE"; + if (bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] + else if (bytes[4] == 0) type = "RESPONSE"; + else if (bytes[4] == 1) type = "ERROR"; + else if (bytes[4] == 2) type = "EVENT"; + else type = "UNKNOWN"; - }, sec * 1000); - } + let message = "OK"; + let error = ""; + if (type == "BROADCAST") return { message, type, error }; + let crc = crc16('ARC', bytes.slice(0, 9)); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; - function reportOfflineNodeStatus(line) { - - logger.debug("Cmd-mngr: ------>reportOfflineNodeStatus for line", line); - - values = {}; - values["dimming"] = 0;//brightness - values["power"] = 0;//výkon - values["current"] = 0;//prúd - values["status"] = "OFFLINE"; - - const date = Date.now(); + if (c1 != bytes[9]) { + //CRC_ERROR + message = "NOK"; + error = "CRC_ERROR c1"; + instance.send(SEND_TO.debug, "CRC_ERROR c1"); + } - Object.keys(nodesData).forEach(node => { + if (c2 != bytes[10]) { + //CRC_ERROR + message = "NOK"; + error = "CRC_ERROR c2"; + instance.send(SEND_TO.debug, "CRC_ERROR c2"); + } - //potrebujem nody k danej linii - if (line == nodesData[node].line || line == undefined) { - - let tbname = nodesData[node].tbname; - let nodeStatus = nodesData[node].status; - - //in case we have reported offline node status, we return (continue with next node) - if (nodeStatus === "OFFLINE") return; - - nodesData[node].node_status_before_offline = nodeStatus; - nodesData[node].status = "OFFLINE"; - nodesData[node].readout = {}; - - sendTelemetry({ ...values }, tbname, date); - } - }) + //crc error + if (type != "RESPONSE") { + instance.send(SEND_TO.debug, bytes); + instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); - } + //logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes); + error = "type is: " + type; - function turnLine(onOrOff, line, info) { - let obj = { - line: line, - command: onOrOff, - info: info - }; - - //logger.debug("linia", line, obj); - instance.send(SEND_TO.dido_controller, obj); - } + message = "NOK"; + } + return { message, type, error }; + } - function detectIfResponseIsValid(bytes) { - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - let type = "RESPONSE"; - if (bytes.length == 1) type = "BROADCAST"; // odpoved z rsPortu na broadcast command: ["broadcast"] - else if (bytes[4] == 0) type = "RESPONSE"; - else if (bytes[4] == 1) type = "ERROR"; - else if (bytes[4] == 2) type = "EVENT"; - else type = "UNKNOWN"; + //BUILD TASKS// + function buildTasks(params) { - let message = "OK"; - let error = ""; - if (type == "BROADCAST") return { message, type, error }; + //return; + console.log("buidTAaasks start ****************", params); + monitor.info("buildTasks - params", params); - let crc = crc16('ARC', bytes.slice(0, 9)); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; + let processLine; //defined line + let init = false; + let processLineProfiles = true; + let processBroadcast = true; + let processNodes = true; - if (c1 != bytes[9]) { - //CRC_ERROR - message = "NOK"; - error = "CRC_ERROR c1"; - instance.send(SEND_TO.debug, "CRC_ERROR c1"); - } + if (params == undefined) { + init = true; + tasks = []; + logger.debug("-->buildTasks clear tasks"); + } + else { + processLineProfiles = false; + processBroadcast = false; + processNodes = false; - if (c2 != bytes[10]) { - //CRC_ERROR - message = "NOK"; - error = "CRC_ERROR c2"; - instance.send(SEND_TO.debug, "CRC_ERROR c2"); - } + processLineProfiles = params.processLineProfiles; + processLine = params.line; + } - //crc error - if (type != "RESPONSE") { - instance.send(SEND_TO.debug, bytes); - instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]); + let now = new Date(); - //logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes); + //process line profiles + if (processLineProfiles) { - error = "type is: " + type; + let keys = Object.keys(relaysData); - message = "NOK"; - } + for (let i = 0; i < keys.length; i++) { - return { message, type, error }; - } + let line = parseInt(keys[i]); + let profilestr = relaysData[line].profile; + if (processLine != undefined) { + if (processLine != line) continue; + } - //BUILD TASKS// - function buildTasks(params) { + try { - //return; - console.log("buidTAaasks start ****************", params); - monitor.info("buildTasks - params", params); + /** + * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... + */ + if (profilestr === "") throw ("Profile is not defined"); + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("Profile is empty"); - let processLine; //defined line - let init = false; - let processLineProfiles = true; - let processBroadcast = true; - let processNodes = true; + monitor.info("buildTasks: profile for line", line); + monitor.info("profile:", profile); - if (params == undefined) { - init = true; - tasks = []; - logger.debug("-->buildTasks clear tasks"); - } - else { - processLineProfiles = false; - processBroadcast = false; - processNodes = false; + let time_points = profile.intervals; - processLineProfiles = params.processLineProfiles; - processLine = params.line; - } + // add name to regular profile timepoint and delete unused end_time key: + time_points.forEach(point => { + point.name = "profileTimepoint" + delete point.end_time; + }); - let now = new Date(); + //monitor.info("buildTasks: time_points", time_points); - //process line profiles - if (processLineProfiles) { - let keys = Object.keys(relaysData); + /** + * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. + * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn + * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching + */ + if (profile.astro_clock == true) { - for (let i = 0; i < keys.length; i++) { + // if astro clock true, we remove all regular profile points + time_points = []; - let line = parseInt(keys[i]); - let profilestr = relaysData[line].profile; + let sunCalcResult = calculateDuskDawn(new Date(), line); - if (processLine != undefined) { - if (processLine != line) continue; - } + // adding dusk dawn to timpoints + if (profile.dawn_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dawn"], "value": 0, "name": "dawn" }); + if (profile.dusk_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dusk"], "value": 1, "name": "dusk" }); - try { + //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) + //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window + if (profile.dawn_lux_sensor == true) { + let [ahours, aminutes] = sunCalcResult["dawn"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dawn_lux_sensor_time_window, 0); - /** - * we process line profiles: timepoints, astro clock, lux_sensor, offsets ... - */ - if (profilestr === "") throw ("Profile is not defined"); - let profile = JSON.parse(profilestr); - if (Object.keys(profile).length === 0) throw ("Profile is empty"); + let strDate = ad.getHours() + ":" + ad.getMinutes(); + time_points.push({ "value": 0, "start_time": strDate, "name": "luxOff" }); + } - monitor.info("buildTasks: profile for line", line); - monitor.info("profile:", profile); + if (profile.dusk_lux_sensor == true) { + let [ahours, aminutes] = sunCalcResult["dusk"].split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dusk_lux_sensor_time_window, 0); - let time_points = profile.intervals; + let strDate = ad.getHours() + ":" + ad.getMinutes(); + time_points.push({ "value": 1, "start_time": strDate, "name": "luxOn" }); + //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing + } + } - // add name to regular profile timepoint and delete unused end_time key: - time_points.forEach(point => { - point.name = "profileTimepoint" - delete point.end_time; - }); + //sort time_points + time_points.sort(function(a, b) { - //monitor.info("buildTasks: time_points", time_points); + let [ahours, aminutes] = a.start_time.split(':'); + let [bhours, bminutes] = b.start_time.split(':'); + let ad = new Date(); + ad.setHours(parseInt(ahours), parseInt(aminutes), 0); - /** - * if astro_clock is true, we create timepoints, that switch on/off relays accordingly. - * we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn - * if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching - */ - if (profile.astro_clock == true) { + let bd = new Date(); + bd.setHours(parseInt(bhours), parseInt(bminutes), 0); - // if astro clock true, we remove all regular profile points - time_points = []; + return ad.getTime() - bd.getTime(); + }); - let sunCalcResult = calculateDuskDawn(new Date(), line); + console.log("line timepoints ........", time_points); - // adding dusk dawn to timpoints - if (profile.dawn_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dawn"], "value": 0, "name": "dawn" }); - if (profile.dusk_lux_sensor == false) time_points.push({ "start_time": sunCalcResult["dusk"], "value": 1, "name": "dusk" }); + let currentValue = 0; + if (time_points.length > 0) currentValue = time_points[time_points.length - 1].value; - //if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit) - //force to turn off after timestamp: dawn + dawn_lux_sensor_time_window - if (profile.dawn_lux_sensor == true) { - let [ahours, aminutes] = sunCalcResult["dawn"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dawn_lux_sensor_time_window, 0); + monitor.info("-->comming events turn on/off lines:"); + for (let t = 0; t < time_points.length; t++) { - let strDate = ad.getHours() + ":" + ad.getMinutes(); - time_points.push({ "value": 0, "start_time": strDate, "name": "luxOff" }); - } + let start_time = new Date(); + let [hours, minutes] = time_points[t].start_time.split(':'); + start_time.setHours(parseInt(hours), parseInt(minutes), 0); - if (profile.dusk_lux_sensor == true) { - let [ahours, aminutes] = sunCalcResult["dusk"].split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours), parseInt(aminutes) + profile.dusk_lux_sensor_time_window, 0); + //task is in the past + if (now.getTime() > start_time.getTime()) { + currentValue = time_points[t].value; - let strDate = ad.getHours() + ":" + ad.getMinutes(); - time_points.push({ "value": 1, "start_time": strDate, "name": "luxOn" }); - //time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing - } - } + //timepoint is in past, we add 24 hours + start_time.setDate(start_time.getDate() + 1); + } + + let params = getParams(); + params.type = "relay"; + params.line = parseInt(line); + params.value = time_points[t].value; + params.tbname = relaysData[line].tbname; + params.timestamp = start_time.getTime(); + + // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day + if (time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24 * 60; + + //astro timepoints will be recalculated dynamically: + params.timePointName = time_points[t].name; + + // if astro timepoint, we save time window: + if (['luxOn', 'luxOff', 'dusk', 'dawn'].includes(params.timePointName)) { + params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; + params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; + } + + if (params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; + else if (params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; + + params.debug = true; + + //turn on/off line + tasks.push(params); + monitor.info("TimePoint params: ", params.info, start_time); + } + + monitor.info("-->time_points final", line, time_points); + + //ensure to turn on/off according to calculated currentValue + let params = getParams(); + params.type = "relay"; + params.line = parseInt(line); + params.tbname = relaysData[line].tbname; + params.value = currentValue; + params.timestamp = i; + params.debug = true; + + //logger.debug(now.toLocaleString("sk-SK")); + monitor.info("-->currentValue for relay", line, currentValue); + + //turn on/off line + if (params.value == 0) params.info = "turn off line on startup: " + line; + else if (params.value == 1) params.info = "turn on line on startup: " + line; + + tasks.push(params); + + } catch (error) { + if (profilestr !== "") { + //errLogger.error(profilestr, error); + console.log(`Cmd_mngr: Unable to process line profile ${line}. Error: `, error); + errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); + } else { + turnLine("off", line, "No line profile. Switching it off on startup"); + } + } - //sort time_points - time_points.sort(function(a, b) { + } + //logger.debug("tasks:"); + //logger.debug(tasks); + } - let [ahours, aminutes] = a.start_time.split(':'); - let [bhours, bminutes] = b.start_time.split(':'); - let ad = new Date(); - ad.setHours(parseInt(ahours), parseInt(aminutes), 0); + //NOTE: PROCESS DEFAULT BROADCASTS - Time of dusk, Time of dawn, Actual Time + if (processBroadcast) { - let bd = new Date(); - bd.setHours(parseInt(bhours), parseInt(bminutes), 0); + let d = new Date(); + let time = d.getTime(); + let sunCalcResult = calculateDuskDawn(); + + { + let params = getParams(); + + params.address = 0xffffffff;//broadcast + params.byte1 = sunCalcResult["dusk_hours"]; + params.byte2 = sunCalcResult["dusk_minutes"]; + params.recipient = 2;//2 broadcast, + params.register = 6;//Time of dusk + params.rw = 1;//write + params.type = "node-regular-write"; + params.timestamp = time + 60000; + params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk + params.info = "Broadcast-duskTime"; + + tasks.push(params); + } + + { + let params = getParams(); + + params.address = 0xffffffff;//broadcast + params.byte1 = sunCalcResult["dawn_hours"]; + params.byte2 = sunCalcResult["dawn_minutes"]; + params.recipient = 2; //2 broadcast + params.register = 7;//Time of dawn + params.rw = 1;//write + params.type = "node-regular-write"; + params.timestamp = time + 60001; + params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn + params.info = "Broadcast-dawnTime"; + + tasks.push(params); + } + + { + let params = getParams(); + + params.address = 0xffffffff;//broadcast + params.byte1 = d.getHours(); + params.byte2 = d.getMinutes(); + params.recipient = 2; //2 broadcast + params.register = 87;//Actual time + params.rw = 1;//write + params.type = "node-regular-write"; + params.timestamp = time + 60002; + params.addMinutesToTimestamp = 5; + params.info = "run broadcast: Actual time"; + + tasks.push(params); + } + + } + + //process nodes & tasks - read node's data + if (processNodes) { + + let time = Date.now(); + + for (let k in nodesData) { + let address = parseInt(k); + let tbname = nodesData[k].tbname; + let register = 0; + + for (let i = 0; i < listOfCommands.length; i++) { + + register = listOfCommands[i]; + let addMinutesToTimestamp = priorities[register]; + + let params = getParams(); + + params.address = address; + params.recipient = 1; + params.register = register; + params.type = register == 1 ? "node-dimming-read" : "node-regular-read"; + params.tbname = tbname; + params.timestamp = time + 5000 + i * 500 + addMinutesToTimestamp * 1000; //to make slight time difference + params.addMinutesToTimestamp = addMinutesToTimestamp; + params.info = "Node regular read command"; + + tasks.push(params); + } + + } + } + + + //niektore ulohy sa vygeneruju iba 1x pri starte!!! + if (!init) return; + + + //Master node FW version - modifies SETTINGS.masterNodeIsResponding + { + let params = getParams(); + params.type = "cmd-master"; + params.register = 4; + params.address = 0; + params.timestamp = 0; + params.addMinutesToTimestamp = 5; + params.tbname = SETTINGS.rvoTbName; + params.info = "Master node FW verzia"; + //params.debug = true; + + tasks.push(params); + } + + //kazdu hodinu skontrolovat nastavenie profilov + { + let params = getParams(); + params.type = "process_profiles"; + params.timestamp = Date.now() + 60001; + params.addMinutesToTimestamp = 60;//60 = every hour + params.info = "detekcia nespracovaných profilov linie a nodov"; + //params.debug = true; + + tasks.push(params); + } + + monitor.info("tasks created:", tasks.length); + } + + + /** + * We process line profile, where "astro_clock": true + * example profile: + * + "dawn_lux_sensor": true, + "dusk_lux_sensor": true, + "dawn_lux_sensor_value": 5, + "dusk_lux_sensor_value": 5, + "dawn_astro_clock_offset": 0, + "dusk_astro_clock_offset": 10, + "dawn_lux_sensor_time_window": 30, + "dusk_lux_sensor_time_window": 30, + "dawn_astro_clock_time_window": 60, + "dusk_astro_clock_time_window": 60 + + * if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line. + * if dusk: we do oposite + * + * dawn: usvit - lux je nad hranicou - vypnem + * dusk: sumrak - lux je pod hranicou - zapnem + */ + function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) { + + let now = new Date(); + let currentTimestamp = now.getTime(); + let keys = Object.keys(relaysData); + + for (let i = 0; i < keys.length; i++) { + + let line = keys[i]; //line is turned off by default + let profilestr = relaysData[line].profile; + const contactor = relaysData[line].contactor; + + try { + + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); + + if (profile.astro_clock == true) { + let sunCalcResult = calculateDuskDawn(now, line); + + //usvit + if (profile.dawn_lux_sensor == true) { + let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut + let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); + + if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { + if (lux_sensor_value > profile.dawn_lux_sensor_value) { + if (contactor) turnLine("off", line, "Profile: dawn - turnOff line according to lux sensor"); + } + } + } + + //sumrak + if (profile.dusk_lux_sensor == true) { + let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); + let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); + + if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { + if (lux_sensor_value < profile.dusk_lux_sensor_value) { + if (!contactor) turnLine("on", line, "Profile: dusk - turnOn line according to lux sensor"); + } + } + } + + } + + } catch (error) { + if (profilestr !== "") monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); + } + + } + + } + + /** + * function updates status and time_of_last_communication of node in the dbNodes + * it only updates if conditions are met + * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) + * if newStatus of node is always receiving false, and it is already for more than SETTINGS.node_status_nok_time value, we update status to "NOK" in tb + * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). + */ + function updateNodeStatus(node, newStatus) { + //MASTER + if (node == 0) return; + + let nodeObj = nodesData[node]; + if (nodeObj == undefined) return; + + let nodeCurrentStatus = nodeObj.status; + const now = Date.now(); + + let data = null; + + if (nodeCurrentStatus === "OFFLINE") { + data = { status: newStatus }; + nodeDbStatusModify(node, data); + return; + } + else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; + else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { + data = { time_of_last_communication: now }; + nodeDbStatusModify(node, data); + return; + } + else if (newStatus == false && nodeCurrentStatus == false) return true; + else if (newStatus == false && nodeCurrentStatus == true) { + if (nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; + else { + data = { status: newStatus }; + nodeDbStatusModify(node, data); + return true; + } + } + else if (newStatus == true && nodeCurrentStatus == false) { + data = { status: newStatus, time_of_last_communication: now }; + nodeDbStatusModify(node, data); + return; + } + + } + + + function nodeDbStatusModify(node, data) { + dbNodes.modify(data).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + if (!err) { + nodesData[node] = { ...nodesData[node], ...data }; + } + }); + }); + } + + + async function runTasks() { + + clearInterval(interval); + + let currentTimestamp = Date.now(); + + //report dusk, dawn--------------------------------- + if (reportDuskDawn.dusk_time < currentTimestamp) { + //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund + if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { + //reportovali sme? + if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { + //sendNotification("Cmd-mngr: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); + reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; + } + } + + var nextDay = new Date(); + nextDay.setDate(nextDay.getDate() + 1); + + sunCalcResult = calculateDuskDawn(nextDay); + reportDuskDawn.dusk_time = sunCalcResult.dusk_time; + } + + if (reportDuskDawn.dawn_time < currentTimestamp) { + //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund + if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { + //reportovali sme? + if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { + //sendNotification(": calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); + reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; + } + } + + var nextDay = new Date(); + nextDay.setDate(nextDay.getDate() + 1); + + sunCalcResult = calculateDuskDawn(nextDay); + reportDuskDawn.dawn_time = sunCalcResult.dawn_time; + + } + //-------------------------------------------------------- + + //sort tasks based on timestamp + tasks.sort(function(a, b) { + if (a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) { + return a.priority - b.priority; + } + return a.timestamp - b.timestamp; + }); + + if (tasks.length == 0) { + instance.send(SEND_TO.debug, "no tasks created"); + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + if (!rsPort.isOpen) { + instance.send(SEND_TO.debug, "!rsPort.isOpen"); + //await rsPort.open(); + //console.log("Cmd-mngr: !rsPort.isOpen"); + } + + let currentTask = tasks[0]; + + if (currentTask.debug) { + //logger.debug("--->task to process", currentTask); + } + + if (currentTask.timestamp <= currentTimestamp) { + let params = { ...tasks[0] }; + + //allow terminal commands + if (SETTINGS.maintenance_mode && params.type !== "cmd-terminal") { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + let type = params.type; + let tbname = params.tbname; + let node = params.address; + let register = params.register; + let line = null; + let itIsNodeCommand; + + if (nodesData[node] !== undefined) { + line = nodesData[node].line; + itIsNodeCommand = true; + } + + if (params.line !== undefined) line = params.line; + + if (params.addMinutesToTimestamp > 0 || params.timePointName) { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } else { + tasks.shift(); + } + + //kontrola nespracovanych profilov nodov + if (type == "process_profiles") { + //na vsetky zapnutych liniach sa spracuju nespracovane profily nodov + loadRelaysData(); + interval = setInterval(runTasks, SHORT_INTERVAL); + return; + } + + //relay + if (type == "relay") { + + const timePointName = params.timePointName; + const value = params.value; + + let date = new Date(); + date.setDate(date.getDate() + 1);//next day + + let sunCalcResult; + if (timePointName) sunCalcResult = calculateDuskDawn(date, params.line); + + if (timePointName == "dawn") { + tasks[0].timestamp = sunCalcResult.dawn_time; + } + else if (timePointName == "dusk") { + tasks[0].timestamp = sunCalcResult.dusk_time; + } + else if (timePointName == "luxOn") { + tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; + } + else if (timePointName == "luxOff") { + tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; + } + else if (timePointName == "profileTimepoint") { + tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; + } + + let info = "aplikovany bod profilu"; + let onOrOff = ""; + value == 1 ? onOrOff = "on" : onOrOff = "off"; + + turnLine(onOrOff, params.line, info); + + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + if (!SETTINGS.masterNodeIsResponding) { + //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal cmd-master + errorHandler.sendMessageToService("Master node is not responding"); + + let stop = true; + + if (type === "cmd-terminal" || type === "cmd-master") stop = false; + if (stop) { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + } + + let contactorStatus = 1; + if (relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; + + if (line === 0 || contactorStatus === 0 || FLOW.deviceStatus.state_of_breaker[line] === "Off") { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors + // check if rotary_switch_state == "Off" + // state_of_braker: disconnected = true? + + if (!rsPort.isOpen) { + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + //RE-CALCULATE VALUES + //set actual time for broadcast + if (register == 87 && params.recipient === 2) { + var d = new Date(); + params.byte1 = d.getHours();//h + params.byte2 = d.getMinutes();//m + } + + //SET DUSK/DAWN FOR BROADCAST + //Time of dusk + if (register == 6 && params.recipient === 2) { + + if (type != "cmd-terminal") { + let sunCalcResult = calculateDuskDawn(); + params.byte1 = sunCalcResult["dusk_hours"];//h + params.byte2 = sunCalcResult["dusk_minutes"];//m + } + } + + //Time of dawn + if (register == 7 && params.recipient === 2) { + if (type != "cmd-terminal") { + let sunCalcResult = calculateDuskDawn(); + params.byte1 = sunCalcResult["dawn_hours"];//h + params.byte2 = sunCalcResult["dawn_minutes"];//m + } + } + //----------------------- + + instance.send(SEND_TO.debug, "address: " + node + " register:" + register + "type: " + type); + + var startTime, endTime; + startTime = new Date(); + + let saveToTb = true; + if (!tbname) saveToTb = false; + + let resp = com_generic(node, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); + let readBytes = 11; + let timeout = 4000; + + + // await keyword is important, otherwise incorrect data is returned! + await writeData(rsPort, resp, readBytes, timeout).then(function(data) { + + //sometimes happens, that status of node changes even if line was turned off and should be offline. To prevent this, we return if line contactor is 0: + if (itIsNodeCommand && line && relaysData[line].contactor !== 1) return; + + endTime = new Date(); + var timeDiff = endTime - startTime; + + //data je array z 11 bytov: 1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data a 2 byty CRC + let dataBytes = data.slice(5, 9); + let result = detectIfResponseIsValid(data); + + //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK + let message = result.message; // OK, NOK + let message_type = result.type; + + if (params.hasOwnProperty("debug")) { + if (params.debug) { + console.log("detected response:", result); + logger.debug("Cmd-mngr: writeData done " + message_type + " duration: " + timeDiff + " type: " + params.debug, params, result); + } + } + + let values = {}; + + //CMD FINISHED + if (message == "OK") { + + updateNodeStatus(node, true); + + //write + if (type == "set_node_profile") { + let result = cmdCounterResolve(node); + if (result == 0) { + dbNodes.modify({ processed: true }).where("node", node).make(function(builder) { + builder.callback(function(err, response) { + + sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); + + logger.debug("--> profil úspešne odoslaný na node č. " + node); + nodesData[node].processed = true; + nodeProfileSendFail.delete(node); + }); + }); + } + } + + //parse read response + if (params.rw == 0) { + values = processResponse(register, dataBytes); //read + } + + if (itIsNodeCommand) { + values.comm_status = "OK"; + values.status = "OK"; + nodesData[node].readout = { ...nodesData[node].readout, ...values }; + } + + //master node + if (node == 0) { + sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); + SETTINGS.masterNodeIsResponding = true; + if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; + } + + if (params.debug) { + //logger.debug("saveToTb", saveToTb, tbname, values); + } + + if (saveToTb && type != "node-regular-read") { + sendTelemetry(values, tbname); + } + else { + if (type == "cmd-terminal") { + terminalCommandResponse(params, "SUCCESS", data); + } + } + + } + else { + terminalCommandResponse(params, "ERROR", data); + handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); + + if (params.hasOwnProperty("debug")) { + if (params.debug) { + //logger.debug("writeData err: ", error, result, params); + logger.debug("writeData err: ", tbname, node, register, values); + } + } + + //logger.debug(error, result, params); + } + }).catch(function(reason) { + + //console.log("writeData catch exception", reason); + instance.send(SEND_TO.debug, reason); + + terminalCommandResponse(params, "FAILURE", null, reason); + handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); + + if (params.hasOwnProperty("debug")) { + if (params.debug) { + logger.debug("-->WRITE FAILED: " + reason, params.debug, params); + } + } + + }); + + } + else { + if (currentTask.debug) { + // currentTask.timestamp <= currentTimestamp && logger.debug("currentTask is not processed - task is in the future", currentTask); + } + + interval = setInterval(runTasks, LONG_INTERVAL); + return; + } + + //console.log("----->runTasks - setInterval", new Date()); + interval = setInterval(runTasks, SHORT_INTERVAL); + } + + + function repeatCommand(params) { + params.repeatCounter++; + if (params.repeatCounter < 4) { + params.timestamp = 0; + params.addMinutesToTimestamp = 0; + tasks.push(params); + } + } + + function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb) { + + let node = params.address; + let register = params.register; + let type = params.type; + let tbName = params.tbname; + if (!tbName) return; + + let values = {}; + + let updateStatus = updateNodeStatus(node, false); + + if (itIsNodeCommand) { + values.comm_status = "NOK"; + nodesData[node].readout.comm_status = "NOK"; + repeatCommand(params); + } + + if (updateStatus) { + values.status = "NOK"; + nodesData[node].readout.status = "NOK"; + } + + if (type === "node-regular-read") return; + + //master node + if (node == 0) { + sendNotification("Cmd-mngr: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); + logger.debug("master_node_is_not_responding", params); + SETTINGS.masterNodeIsResponding = false; + + if (register == 4) values["master_node_version"] = "NOK"; + } + + if (type == "set_node_profile") { + delete cmdCounter[node]; + logger.debug("profil nebol úspešne odoslaný na node č. ", params); + + if (!nodeProfileSendFail.has(node)) { + sendNotification("Cmd-mngr: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); + nodeProfileSendFail.add(node); + } + } + + // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); + if (saveToTb) { + sendTelemetry(values, tbName); + } + + } + + + function sendNodesData() { + Object.keys(nodesData).forEach(node => { + if (nodesData[node]["status"] !== "OFFLINE") { + sendTelemetry(nodesData[node].readout, nodesData[node].tbname); + nodesData[node].readout = {}; + } + }) + } + + + /** + * function handles requests from terminal + * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. + * FAILURE means, that we got into catch block of writeData function. + */ + function terminalCommandResponse(params, responseType, data = null, reason = "") { //success, error, failure + + if (params.refFlowdataKey === undefined) { + //console.log("params.refFlowdataKey is undefined", params); + return; + } + + let message = null; + let type = null; + + switch (responseType) { + case "SUCCESS": + message = "cmd-terminal SUCCESS"; + type = "SUCCESS"; + break; + case "ERROR": + message = "cmd-terminal FAILED"; + type = "ERROR"; + break; + case "FAILURE": + message = "ERROR WRITE FAILED: " + reason; + type = "ERROR"; + break; + default: + type = undefined; + } + + logger.debug(message); + + //make http response + let responseObj = {} + responseObj["type"] = type; + + if (responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; + else responseObj["bytes"] = data; + + let refFlowdata = refFlowdataObj[params.refFlowdataKey]; //holds reference to httprequest flowdata + if (refFlowdata) { + refFlowdata.data = responseObj; + instance.send(SEND_TO.http_response, refFlowdata); + } + } + + + /** + * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function + */ + function reportEdgeDateTimeAndNumberOfLuminaires() { + + //Number of ok and nok nodes on platform does not equals to total number of nodes. + //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + + const ts = Date.now(); + const keys = Object.keys(nodesData_clone); + + const number_of_luminaires = keys.length; + let number_of_ok_luminaires = 0; + let number_of_nok_luminaires = 0; + + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + let nodeObj = nodesData_clone[key]; + if (nodeObj.tbname == undefined) continue; + + if (nodeObj.status === "OFFLINE") { + nodeObj.node_status_before_offline === true ? number_of_ok_luminaires++ : number_of_nok_luminaires++; + } + else if (nodeObj.status == true) number_of_ok_luminaires++; + else number_of_nok_luminaires++; + + } + + const values = { + "number_of_luminaires": number_of_luminaires, + "number_of_ok_luminaires": number_of_ok_luminaires, + "number_of_nok_luminaires": number_of_nok_luminaires, + "edge_date_time": ts - ts % 60000 //round to full minute + }; + + sendTelemetry(values, SETTINGS.rvoTbName, ts); + } + + + function handleRsPort() { + + if (rsPort) { + rsPort.removeAllListeners(); + rsPort = null; + } + + //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" + // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM + // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI + + if (SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; + rsPort = new SerialPort(`/dev/${SETTINGS.serial_port}`, { autoOpen: false }); + //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit + //rsPort.setMaxListeners(0); + + rsPort.on('open', async function() { + + logger.debug("Cmd-mngr: rsPort opened success"); + + await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { + instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); + + logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); + + }).catch(function(reason) { + instance.send(SEND_TO.debug, "Cmd-mngr: RPC runSyncExec - promise rejected:" + reason); + }); + }); + + rsPort.on('error', function(err) { + errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); + monitor.info("Cmd-mngr: Error on rsPort", err.message); + }); + + rsPort.on("close", () => { + monitor.info("Cmd-mngr: rsPort closed, reconnecting..."); + setTimeout(handleRsPort, 1000); + }); + + rsPort.open(); + } + + + instance.on("close", () => { + clearInterval(interval); + clearInterval(customTasksInterval); + clearInterval(setCorrectTime); + clearInterval(sendNodeReadout); + clearInterval(accelerometerInterval); + rsPort.close(); + }); + + instance.on("0", _ => { + main(); + }) + + instance.on("1", async function(flowdata) { + + //instance.send(SEND_TO.debug, "on Data"); + //instance.send(SEND_TO.debug, flowdata); + + //logger.debug(flowdata.data); + + //just testing functions + if (flowdata.data == "open") { + if (!rsPort.isOpen) rsPort.open(); + return; + } + else if (flowdata.data == "close") { + rsPort.close(); + return; + } + else if (flowdata.data == "clean") { + tasks = []; + return; + } + else if (flowdata.data == "buildtasks") { + //build & run + return; + } + else if (flowdata.data == "run") { + //durations = []; + + if (tasks.length == 0) { + + buildTasks(); + + if (rsPort.isOpen) { + interval = setInterval(runTasks, 100); + } + else { + instance.send(SEND_TO.debug, "port is not opened!!!"); + } + } + } + else { + //terminal data - object + //logger.debug("flowdata", flowdata.data); + + if (typeof flowdata.data === 'object') { + //logger.debug("dido", flowdata.data); + if (flowdata.data.hasOwnProperty("sender")) { + //data from dido_controller + if (flowdata.data.sender == "dido_controller") { + + if (flowdata.data.hasOwnProperty("cmd")) { + let cmd = flowdata.data.cmd; + + if (cmd == "buildTasks") { + clearInterval(interval); + + logger.debug("-->Cmd-mngr: BUILD TASKS"); + buildTasks(); + + //logger.debug("tasks:"); + //logger.debug(tasks); + + logger.debug("-->Cmd-mngr: RUN TASKS"); + interval = setInterval(runTasks, 5000); + } + else if (cmd == "reload_relays") { + loadRelaysData(flowdata.data.line); + + if (flowdata.data.dataChanged) { + if (!flowdata.data.value) { + reportOfflineNodeStatus(flowdata.data.line); + } + else { + reportOnlineNodeStatus(flowdata.data.line); + } + } + + } + else if (cmd == "rotary_switch_state") { + let value = flowdata.data.value; + + //state was changed + if (rotary_switch_state != value) { + if (value == "Off") { + //vyreportovat vsetky svietdla + reportOfflineNodeStatus(); + } + + rotary_switch_state = value; + } + } + else if (cmd == "lux_sensor") { + lux_sensor = parseInt(flowdata.data.value); + + // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? + if (lux_sensor < 100) { + + // we send lux_sensor value to all nodes: + let params = getParams(PRIORITY_TYPES.node_broadcast); + + params.recipient = 2;//2 broadcast, address = 0 + params.address = 0xffffffff;//Broadcast + + let ba = longToByteArray(lux_sensor); + + params.byte3 = ba[1];//msb + params.byte4 = ba[0]; + params.timestamp = PRIORITY_TYPES.node_broadcast; + params.info = "run broadcast: Actual Lux level from cabinet"; + params.register = 95;//Actual Lux level from cabinet + params.rw = 1;//write + + tasks.push(params); + + //process profiles + turnOnOffLinesAccordingToLuxSensor(lux_sensor); + } + } + else if (cmd == "state_of_breaker") { + //istic linie + let value = flowdata.data.value; + let line = parseInt(flowdata.data.line); + + let dataChanged = false; + if (state_of_breaker[line] != value) dataChanged = true; + + state_of_breaker[line] = value; - return ad.getTime() - bd.getTime(); - }); + let status = "OK"; + if (value == "Off") status = "NOK"; + + if (dataChanged) { + + if (relaysData.hasOwnProperty(line)) { + let tbname = relaysData[line].tbname; + + if (value == "Off") sendNotification("Cmd-mngr: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + else sendNotification("Cmd-mngr: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); + + //report status liniu + sendTelemetry({ status: status }, tbname) + + //current value + if (value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii + } + + } + } + else { + logger.debug("undefined cmd", cmd); + } + } + } + + return; + } + + //data from worksys + if (flowdata.data.hasOwnProperty("topic")) { + + let data = getNested(flowdata.data, "content", "data"); + + //if we get temperature in senica from senica-prod01 + let temperature = getNested(flowdata.data, "content", "senica_temperature"); + + if (temperature !== undefined) { + temperatureInSenica = temperature; + return; + } + + if (data === undefined) { + console.log("Invalid rpc command came from platform"); + return; + } + + let command = data.params.command; + let method = data.method; + let profile = data.params.payload; + if (profile == undefined) profile = ""; + let entity = data.params.entities[0]; + let entity_type = entity.entity_type; + let tbname = entity.tb_name; + + instance.send(SEND_TO.debug, flowdata.data); + logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); + logger.debug("----------------------------"); + + if (entity_type == "street_luminaire" || entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") { + if (method == "set_command") { + + //let command = data.params.command; + let value = data.params.payload.value; + + if (command == "dimming") { + + let nodeWasFound = false; + let keys = Object.keys(nodesData); + + //logger.debug("-----", keys); + + for (let i = 0; i < keys.length; i++) { + let node = keys[i]; + //logger.debug( node, nodesData[node], tbname); + + if (tbname == nodesData[node].tbname) { + let params = getParams(PRIORITY_TYPES.high_priority); + + value = parseInt(value); + if (value > 0) value = value + 128; + + params.type = "node-onetime-write"; + params.tbname = tbname; + params.address = node; + params.register = 1; + params.recipient = 1; + params.byte4 = value; + params.rw = 1; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'set dimming from platform'; + //params.debug = true; + + //debug(params); + logger.debug("dimming", params); + + tasks.push(params); + + setTimeout(function() { + + //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority + //a pridame aj vyreportovanie dimmingu + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 1; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read dimming (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - vykon + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 76; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Power (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - prud svietidla + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 75; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read Input Current (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + //pridame aj vyreportovanie - power faktor - ucinnik + { + let params = getParams(PRIORITY_TYPES.high_priority); + + params.type = "node-onetime-read"; + params.tbname = tbname; + params.address = node; + params.register = 77; + params.recipient = 1; + params.rw = 0; + params.timestamp = PRIORITY_TYPES.high_priority; + params.info = 'read power factor (after set dimming from platform)'; + //params.debug = true; + + tasks.push(params); + } + + }, 4000); + + nodeWasFound = true; + break; + } + } + + if (!nodeWasFound) { + logger.debug("set dimming from platform", "unable to find tbname", tbname); + } + } + else { + instance.send(SEND_TO.debug, "undefined command " + command); + logger.debug("undefined command", command); + } + + return; + } + else if (method == "set_profile") { + //nastav profil nodu + logger.debug("-->set_profile for node", data.params); + logger.debug("------profile data", profile); + //instance.send(SEND_TO.debug, "set_profile" + command); + + let keys = Object.keys(nodesData); + for (let i = 0; i < keys.length; i++) { + let node = keys[i]; + if (tbname == nodesData[node].tbname) { + + if (profile != "") profile = JSON.stringify(profile); + dbNodes.modify({ processed: false, profile: profile }).where("node", node).make(function(builder) { + + builder.callback(function(err, response) { + + logger.debug("worksys - update node profile done", profile); + if (profile === "") logger.debug("worksys - update node profile done - profile is empty"); + + //profil úspešne prijatý pre node č. xx + sendNotification("Cmd-mngr", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); + + nodesData[node].processed = false; + nodesData[node].profile = profile; + + processNodeProfile(node); + }); + }); + } + } + } + else { + + instance.send(SEND_TO.debug, "unknown method " + method); + logger.debug("unknown method", method); + + return; + } + } + + //nastav profil linie z platformy + else if (entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") { + //profil linie + //relays.table line:number|tbname:string|contactor:number|profile:string + //najdeme line relaysData + + if (method == "set_profile") { + + logger.debug("-->set_profile for line", data.params); + logger.debug("profile data:", profile); + + let keys = Object.keys(relaysData); + for (let i = 0; i < keys.length; i++) { + let line = keys[i]; + if (tbname == relaysData[line].tbname) { + //zmazeme tasky + removeTask({ type: "relay", line: line }); + + if (profile != "") profile = JSON.stringify(profile); + dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) { + + builder.callback(function(err, response) { + + //update profile + logger.debug("worksys - update relay profile done:", profile); + instance.send(SEND_TO.debug, "worksys - update relay profile done"); + + relaysData[line].profile = profile; + + loadRelaysData(line) + logger.debug("loadRelaysData DONE for line", line); + + buildTasks({ processLineProfiles: true, line: line }); + + sendNotification("Cmd-mngr: set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); + }); + }); + break; + } + } + } + else if (method == "set_command") { + let value = data.params.payload.value; + + if (command === "switch") { + + // if we receive rpc from platform, to switch maintenance mode, we set SETTINGS.maintenance_mode flow variable to value; + if (entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; - console.log("line timepoints ........", time_points); + const relayObject = getObjectByTbValue(relaysData, tbname); + let line = 0; + if (isObject(relayObject)) line = relayObject.line; - let currentValue = 0; - if (time_points.length > 0) currentValue = time_points[time_points.length - 1].value; + // v relaysData je contactor bud 0 alebo 1, ale z platformy prichadza true, false; + if (value == false) turnLine("off", line, "command received from platform"); + else turnLine("on", line, "command received from platform"); + } + } + else { + instance.send(SEND_TO.debug, "undefined method " + method); + logger.debug("undefined method", method); + } - monitor.info("-->comming events turn on/off lines:"); - for (let t = 0; t < time_points.length; t++) { + return; + } + else { + instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); + logger.debug("UNKNOW entity_type", entity_type); + } + return; + } - let start_time = new Date(); - let [hours, minutes] = time_points[t].start_time.split(':'); - start_time.setHours(parseInt(hours), parseInt(minutes), 0); + //terminal + if (!rsPort.isOpen) await rsPort.open(); - //task is in the past - if (now.getTime() > start_time.getTime()) { - currentValue = time_points[t].value; + let params = flowdata.data.body; + if (params == undefined) { + //logger.debug("Cmd-mngr: flowdata.data.body is undefined"); + return; + } - //timepoint is in past, we add 24 hours - start_time.setDate(start_time.getDate() + 1); - } - - let params = getParams(); - params.type = "relay"; - params.line = parseInt(line); - params.value = time_points[t].value; - params.tbname = relaysData[line].tbname; - params.timestamp = start_time.getTime(); - - // it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day - if (time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24 * 60; - - //astro timepoints will be recalculated dynamically: - params.timePointName = time_points[t].name; - - // if astro timepoint, we save time window: - if (['luxOn', 'luxOff', 'dusk', 'dawn'].includes(params.timePointName)) { - params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window; - params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window; - } - - if (params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line; - else if (params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line; - - params.debug = true; - - //turn on/off line - tasks.push(params); - monitor.info("TimePoint params: ", params.info, start_time); - } - - monitor.info("-->time_points final", line, time_points); - - //ensure to turn on/off according to calculated currentValue - let params = getParams(); - params.type = "relay"; - params.line = parseInt(line); - params.tbname = relaysData[line].tbname; - params.value = currentValue; - params.timestamp = i; - params.debug = true; - - //logger.debug(now.toLocaleString("sk-SK")); - monitor.info("-->currentValue for relay", line, currentValue); - - //turn on/off line - if (params.value == 0) params.info = "turn off line on startup: " + line; - else if (params.value == 1) params.info = "turn on line on startup: " + line; - - tasks.push(params); - - } catch (error) { - if (profilestr !== "") { - //errLogger.error(profilestr, error); - console.log(`Cmd_manager: Unable to process line profile ${line}. Error: `, error); - errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error"); - } else { - turnLine("off", line, "No line profile. Switching it off on startup"); - } - } + params.priority = PRIORITY_TYPES.terminal; + params.type = "cmd-terminal"; + params.tbname = ""; + params.timestamp = PRIORITY_TYPES.terminal; + params.addMinutesToTimestamp = 0;// do not repeat task!!! + params.debug = true; - } - //logger.debug("tasks:"); - //logger.debug(tasks); - } + let timestamp = Date.now(); + params.refFlowdataKey = timestamp; + //params.refFlowdata = flowdata; + //refFlowdata = flowdata; + //console.log("flowdata", flowdata); - //NOTE: PROCESS DEFAULT BROADCASTS - Time of dusk, Time of dawn, Actual Time - if (processBroadcast) { + cleanUpRefFlowdataObj(); - let d = new Date(); - let time = d.getTime(); - let sunCalcResult = calculateDuskDawn(); - - { - let params = getParams(); - - params.address = 0xffffffff;//broadcast - params.byte1 = sunCalcResult["dusk_hours"]; - params.byte2 = sunCalcResult["dusk_minutes"]; - params.recipient = 2;//2 broadcast, - params.register = 6;//Time of dusk - params.rw = 1;//write - params.type = "node-regular-write"; - params.timestamp = time + 60000; - params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk - params.info = "Broadcast-duskTime"; - - tasks.push(params); - } - - { - let params = getParams(); - - params.address = 0xffffffff;//broadcast - params.byte1 = sunCalcResult["dawn_hours"]; - params.byte2 = sunCalcResult["dawn_minutes"]; - params.recipient = 2; //2 broadcast - params.register = 7;//Time of dawn - params.rw = 1;//write - params.type = "node-regular-write"; - params.timestamp = time + 60001; - params.addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn - params.info = "Broadcast-dawnTime"; - - tasks.push(params); - } - - { - let params = getParams(); - - params.address = 0xffffffff;//broadcast - params.byte1 = d.getHours(); - params.byte2 = d.getMinutes(); - params.recipient = 2; //2 broadcast - params.register = 87;//Actual time - params.rw = 1;//write - params.type = "node-regular-write"; - params.timestamp = time + 60002; - params.addMinutesToTimestamp = 5; - params.info = "run broadcast: Actual time"; - - tasks.push(params); - } - - } - - //process nodes & tasks - read node's data - if (processNodes) { - - let time = Date.now(); - - for (let k in nodesData) { - let address = parseInt(k); - let tbname = nodesData[k].tbname; - let register = 0; - - for (let i = 0; i < listOfCommands.length; i++) { - - register = listOfCommands[i]; - let addMinutesToTimestamp = priorities[register]; - - let params = getParams(); - - params.address = address; - params.recipient = 1; - params.register = register; - params.type = register == 1 ? "node-dimming-read" : "node-regular-read"; - params.tbname = tbname; - params.timestamp = time + 5000 + i * 500 + addMinutesToTimestamp * 1000; //to make slight time difference - params.addMinutesToTimestamp = addMinutesToTimestamp; - params.info = "Node regular read command"; - - tasks.push(params); - } - - } - } - - - //niektore ulohy sa vygeneruju iba 1x pri starte!!! - if (!init) return; - - - //Master node FW version - modifies SETTINGS.masterNodeIsResponding - { - let params = getParams(); - params.type = "cmd-master"; - params.register = 4; - params.address = 0; - params.timestamp = 0; - params.addMinutesToTimestamp = 5; - params.tbname = SETTINGS.rvoTbName; - params.info = "Master node FW verzia"; - //params.debug = true; - - tasks.push(params); - } - - //kazdu hodinu skontrolovat nastavenie profilov - { - let params = getParams(); - params.type = "process_profiles"; - params.timestamp = Date.now() + 60001; - params.addMinutesToTimestamp = 60;//60 = every hour - params.info = "detekcia nespracovaných profilov linie a nodov"; - //params.debug = true; - - tasks.push(params); - } - - monitor.info("tasks created:", tasks.length); - } - - - /** - * We process line profile, where "astro_clock": true - * example profile: - * - "dawn_lux_sensor": true, - "dusk_lux_sensor": true, - "dawn_lux_sensor_value": 5, - "dusk_lux_sensor_value": 5, - "dawn_astro_clock_offset": 0, - "dusk_astro_clock_offset": 10, - "dawn_lux_sensor_time_window": 30, - "dusk_lux_sensor_time_window": 30, - "dawn_astro_clock_time_window": 60, - "dusk_astro_clock_time_window": 60 - - * if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line. - * if dusk: we do oposite - * - * dawn: usvit - lux je nad hranicou - vypnem - * dusk: sumrak - lux je pod hranicou - zapnem - */ - function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value) { - - let now = new Date(); - let currentTimestamp = now.getTime(); - let keys = Object.keys(relaysData); - - for (let i = 0; i < keys.length; i++) { - - let line = keys[i]; //line is turned off by default - let profilestr = relaysData[line].profile; - const contactor = relaysData[line].contactor; - - try { - - let profile = JSON.parse(profilestr); - if (Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined"); - - if (profile.astro_clock == true) { - let sunCalcResult = calculateDuskDawn(now, line); - - //usvit - if (profile.dawn_lux_sensor == true) { - let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut - let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt(profile.dawn_lux_sensor_time_window) * 1000 * 60); - - if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { - if (lux_sensor_value > profile.dawn_lux_sensor_value) { - if (contactor) turnLine("off", line, "Profile: dawn - turnOff line according to lux sensor"); - } - } - } - - //sumrak - if (profile.dusk_lux_sensor == true) { - let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); - let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt(profile.dusk_lux_sensor_time_window) * 1000 * 60); - - if (currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2) { - if (lux_sensor_value < profile.dusk_lux_sensor_value) { - if (!contactor) turnLine("on", line, "Profile: dusk - turnOn line according to lux sensor"); - } - } - } - - } - - } catch (error) { - if (profilestr !== "") monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error); - } - - } - - } - - /** - * function updates status and time_of_last_communication of node in the dbNodes - * it only updates if conditions are met - * it only updates time_of_last_communication of node, if the last written time was more than 10 minutes ago (600000 miliseconds) - * if newStatus of node is always receiving false, and it is already for more than SETTINGS.node_status_nok_time value, we update status to "NOK" in tb - * function returns true, if status of node needs to be updated in TB (newStatus attribute is false in this case). - */ - function updateNodeStatus(node, newStatus) { - //MASTER - if (node == 0) return; - - let nodeObj = nodesData[node]; - if (nodeObj == undefined) return; - - let nodeCurrentStatus = nodeObj.status; - const now = Date.now(); - - let data = null; - - if (nodeCurrentStatus === "OFFLINE") { - data = { status: newStatus }; - nodeDbStatusModify(node, data); - return; - } - else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication > now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) return; - else if (newStatus == true && nodeCurrentStatus == true && nodeObj.time_of_last_communication < now - TIME_AFTER_WE_UPDATE_LAST_NODE_COMMUNICATION) { - data = { time_of_last_communication: now }; - nodeDbStatusModify(node, data); - return; - } - else if (newStatus == false && nodeCurrentStatus == false) return true; - else if (newStatus == false && nodeCurrentStatus == true) { - if (nodeObj.time_of_last_communication + SETTINGS.node_status_nok_time > now) return; - else { - data = { status: newStatus }; - nodeDbStatusModify(node, data); - return true; - } - } - else if (newStatus == true && nodeCurrentStatus == false) { - data = { status: newStatus, time_of_last_communication: now }; - nodeDbStatusModify(node, data); - return; - } - - } - - - function nodeDbStatusModify(node, data) { - dbNodes.modify(data).where("node", node).callback(function(err, response) { - if (!err) { - nodesData[node] = { ...nodesData[node], ...data }; - } - }); - } - - - async function runTasks() { - - clearInterval(interval); - - let currentTimestamp = Date.now(); - - //report dusk, dawn--------------------------------- - if (reportDuskDawn.dusk_time < currentTimestamp) { - //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if ((currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000) { - //reportovali sme? - if (reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time) { - //sendNotification("CMD Manager: calculated Time of dusk", SETTINGS.rvoTbName, "dusk_has_occured", { value: sunCalcResult["dusk"] }, "", SEND_TO.tb, instance); - reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time; - } - } - - var nextDay = new Date(); - nextDay.setDate(nextDay.getDate() + 1); - - sunCalcResult = calculateDuskDawn(nextDay); - reportDuskDawn.dusk_time = sunCalcResult.dusk_time; - } - - if (reportDuskDawn.dawn_time < currentTimestamp) { - //vyreportuj iba ak nie je velky rozdiel napr. 60 sekund - if ((currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000) { - //reportovali sme? - if (reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time) { - //sendNotification("CMD Manager: calculated Time of dawn", SETTINGS.rvoTbName, "dawn_has_occured", { value: sunCalcResult["dawn"] }, "", SEND_TO.tb, instance); - reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time; - } - } - - var nextDay = new Date(); - nextDay.setDate(nextDay.getDate() + 1); - - sunCalcResult = calculateDuskDawn(nextDay); - reportDuskDawn.dawn_time = sunCalcResult.dawn_time; - - } - //-------------------------------------------------------- - - //sort tasks based on timestamp - tasks.sort(function(a, b) { - if (a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp) { - return a.priority - b.priority; - } - return a.timestamp - b.timestamp; - }); - - if (tasks.length == 0) { - instance.send(SEND_TO.debug, "no tasks created"); - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - if (!rsPort.isOpen) { - instance.send(SEND_TO.debug, "!rsPort.isOpen"); - //await rsPort.open(); - //console.log("Cmd_manager - !rsPort.isOpen"); - } - - let currentTask = tasks[0]; - - if (currentTask.debug) { - //logger.debug("--->task to process", currentTask); - } - - if (currentTask.timestamp <= currentTimestamp) { - let params = { ...tasks[0] }; - - //allow terminal commands - if (SETTINGS.maintenance_mode && params.type !== "cmd-terminal") { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - let type = params.type; - let tbname = params.tbname; - let node = params.address; - - let register = params.register; - let line = null; - let itIsNodeCommand; - - if (nodesData[node] !== undefined) { - line = nodesData[node].line; - itIsNodeCommand = true; - } - - if (params.line !== undefined) line = params.line; - - if (params.addMinutesToTimestamp > 0 || params.timePointName) { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } else { - tasks.shift(); - } - - //kontrola nespracovanych profilov nodov - if (type == "process_profiles") { - //na vsetky zapnutych liniach sa spracuju nespracovane profily nodov - loadRelaysData(); - interval = setInterval(runTasks, SHORT_INTERVAL); - return; - } - - //relay - if (type == "relay") { - - const timePointName = params.timePointName; - const value = params.value; - - let date = new Date(); - date.setDate(date.getDate() + 1);//next day - - let sunCalcResult; - if (timePointName) sunCalcResult = calculateDuskDawn(date, params.line); - - if (timePointName == "dawn") { - tasks[0].timestamp = sunCalcResult.dawn_time; - } - else if (timePointName == "dusk") { - tasks[0].timestamp = sunCalcResult.dusk_time; - } - else if (timePointName == "luxOn") { - tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000; - } - else if (timePointName == "luxOff") { - tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000; - } - else if (timePointName == "profileTimepoint") { - tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000; - } - - let info = "aplikovany bod profilu"; - let onOrOff = ""; - value == 1 ? onOrOff = "on" : onOrOff = "off"; - - turnLine(onOrOff, params.line, info); - - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - if (!SETTINGS.masterNodeIsResponding) { - //ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal cmd-master - errorHandler.sendMessageToService("Master node is not responding"); - - let stop = true; - - if (type === "cmd-terminal" || type === "cmd-master") stop = false; - if (stop) { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - } - - let contactorStatus = 1; - if (relaysData[line] != undefined) contactorStatus = relaysData[line].contactor; - - if (line === 0 || contactorStatus === 0 || FLOW.deviceStatus.state_of_breaker[line] === "Off") { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - // TODO: -> status offline for rvo if rotary_switch_state is OFF, this is source of errors - //check if rotary_switch_state == "Off" - // state_of_braker: disconnected = true? - - if (!rsPort.isOpen) { - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - //RE-CALCULATE VALUES - //set actual time for broadcast - if (register == 87 && params.recipient === 2) { - var d = new Date(); - params.byte1 = d.getHours();//h - params.byte2 = d.getMinutes();//m - } - - //SET DUSK/DAWN FOR BROADCAST - //Time of dusk - if (register == 6 && params.recipient === 2) { - - if (type != "cmd-terminal") { - let sunCalcResult = calculateDuskDawn(); - params.byte1 = sunCalcResult["dusk_hours"];//h - params.byte2 = sunCalcResult["dusk_minutes"];//m - } - } - - //Time of dawn - if (register == 7 && params.recipient === 2) { - if (type != "cmd-terminal") { - let sunCalcResult = calculateDuskDawn(); - params.byte1 = sunCalcResult["dawn_hours"];//h - params.byte2 = sunCalcResult["dawn_minutes"];//m - } - } - //----------------------- - - instance.send(SEND_TO.debug, "address: " + node + " register:" + register + "type: " + type); - - var startTime, endTime; - startTime = new Date(); - - let saveToTb = true; - if (!tbname) saveToTb = false; - - let resp = com_generic(node, params.recipient, params.rw, register, params.name, params.byte1, params.byte2, params.byte3, params.byte4); - let readBytes = 11; - let timeout = 4000; - - - // await keyword is important, otherwise incorrect data is returned! - await writeData(rsPort, resp, readBytes, timeout).then(function(data) { - - //sometimes happens, that status of node changes even if line was turned off and should be offline. To prevent this, we return if line contactor is 0: - if (itIsNodeCommand && line && relaysData[line].contactor !== 1) return; - - endTime = new Date(); - var timeDiff = endTime - startTime; - - //data je array z 11 bytov: 1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data a 2 byty CRC - let dataBytes = data.slice(5, 9); - let result = detectIfResponseIsValid(data); - - if(register === 79) console.log("node responsee: ", dataBytes); - //ak sa odpoved zacina 0 - je to v poriadku, inak je NOK - let message = result.message; // OK, NOK - let message_type = result.type; - - if (params.hasOwnProperty("debug")) { - if (params.debug) { - console.log("detected response:", result); - logger.debug("Cmd-mngr: writeData done " + message_type + " duration: " + timeDiff + " type: " + params.debug, params, result); - } - } - - let values = {}; - - //CMD FINISHED - if (message == "OK") { - - updateNodeStatus(node, true); - - //write - if (type == "set_node_profile") { - let result = cmdCounterResolve(node); - if (result == 0) { - dbNodes.modify({ processed: true }).where("node", node).callback(function(err, response) { - - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance); - - logger.debug("--> profil úspešne odoslaný na node č. " + node); - nodesData[node].processed = true; - nodeProfileSendFail.delete(node); - }); - } - } - - //parse read response - if (params.rw == 0) { - values = processResponse(register, dataBytes); //read - console.log("command params: ", params.address, register); - } - - if (itIsNodeCommand) { - values.comm_status = "OK"; - values.status = "OK"; - nodesData[node].readout = { ...nodesData[node].readout, ...values }; - } - - //master node - if (node == 0) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status"); - SETTINGS.masterNodeIsResponding = true; - if (register == 4) values["edge_fw_version"] = SETTINGS.edge_fw_version; - } - - if (params.debug) { - //logger.debug("saveToTb", saveToTb, tbname, values); - } - - if (saveToTb && type != "node-regular-read") { - sendTelemetry(values, tbname); - } - else { - if (type == "cmd-terminal") { - terminalCommandResponse(params, "SUCCESS", data); - } - } - - } - else { - - terminalCommandResponse(params, "ERROR", data); - handleNokResponseOnRsPort("handleNOK else block", params, itIsNodeCommand, saveToTb); + refFlowdataObj[timestamp] = flowdata; - if (params.hasOwnProperty("debug")) { - if (params.debug) { - //logger.debug("writeData err: ", error, result, params); - logger.debug("writeData err: ", tbname, node, register, values); - } - } - - //logger.debug(error, result, params); - } - }).catch(function(reason) { - - //console.log("writeData catch exception", reason); - instance.send(SEND_TO.debug, reason); - - terminalCommandResponse(params, "FAILURE", null, reason); - handleNokResponseOnRsPort("handleNOK catch block", params, itIsNodeCommand, saveToTb); - - if (params.hasOwnProperty("debug")) { - if (params.debug) { - logger.debug("-->WRITE FAILED: " + reason, params.debug, params); - } - } - - }); - - } - else { - if (currentTask.debug) { - // currentTask.timestamp <= currentTimestamp && logger.debug("currentTask is not processed - task is in the future", currentTask); - } - - interval = setInterval(runTasks, LONG_INTERVAL); - return; - } - - //console.log("----->runTasks - setInterval", new Date()); - interval = setInterval(runTasks, SHORT_INTERVAL); - } - - - function handleNokResponseOnRsPort(message, params, itIsNodeCommand, saveToTb) { - - let node = params.address; - let register = params.register; - let type = params.type; - let tbName = params.tbname; - if (!tbName) return; - - let values = {}; - - let updateStatus = updateNodeStatus(node, false); - - if (itIsNodeCommand) { - values.comm_status = "NOK"; - nodesData[node].readout.comm_status = "NOK"; - } - - if (updateStatus) { - values.status = "NOK"; - nodesData[node].readout.status = "NOK"; - } - - if (type === "node-regular-read") return; - - //master node - if (node == 0) { - sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status"); - logger.debug("master_node_is_not_responding", params); - SETTINGS.masterNodeIsResponding = false; - - if (register == 4) values["master_node_version"] = "NOK"; - } - - if (type == "set_node_profile") { - delete cmdCounter[node]; - logger.debug("profil nebol úspešne odoslaný na node č. ", params); - - if (!nodeProfileSendFail.has(node)) { - sendNotification("CMD Manager: process cmd", tbName, "configuration_of_dimming_profile_to_node_failed", { node: node }, "", SEND_TO.tb, instance); - nodeProfileSendFail.add(node); - } - } - - // console.log("------",node, register, type, itIsNodeCommand, updateStatus, saveToTb, values); - if (saveToTb) { - sendTelemetry(values, tbName); - } - - } - - function sendNodesData() { - Object.keys(nodesData).forEach(node => { - if (nodesData[node]["status"] !== "OFFLINE") { - sendTelemetry(nodesData[node].readout, nodesData[node].tbname); - nodesData[node].readout = {}; - } - }) - } - - - /** - * function handles requests from terminal - * responseType can be "SUCCESS", "ERROR" or "FAILURE", depending on rsPort data. - * FAILURE means, that we got into catch block of writeData function. - */ - function terminalCommandResponse(params, responseType, data = null, reason = "") { //success, error, failure - - if (params.refFlowdataKey === undefined) { - //console.log("params.refFlowdataKey is undefined", params); - return; - } - else { - console.log("params.refFlowdataKey: ", params); - } - - let message = null; - let type = null; - - switch (responseType) { - case "SUCCESS": - message = "cmd-terminal SUCCESS"; - type = "SUCCESS"; - break; - case "ERROR": - message = "cmd-terminal FAILED"; - type = "ERROR"; - break; - case "FAILURE": - message = "ERROR WRITE FAILED: " + reason; - type = "ERROR"; - break; - default: - type = undefined; - } - - logger.debug(message); - logger.debug(params); - - //make http response - let responseObj = {} - responseObj["type"] = type; - - if (responseType == "FAILURE") responseObj["message"] = "ERROR WRITE FAILED: " + reason; - else responseObj["bytes"] = data; - - let refFlowdata = refFlowdataObj[params.refFlowdataKey]; //holds reference to httprequest flowdata - if (refFlowdata) { - refFlowdata.data = responseObj; - instance.send(SEND_TO.http_response, refFlowdata); - } - } - - - /** - * function handles tasks, that are not needed to run through masterNode. To make them run smooth without waiting for other tasks to be completed, we moved them in separate function - */ - function reportEdgeDateTimeAndNumberOfLuminaires() { - - //Number of ok and nok nodes on platform does not equals to total number of nodes. - //possible error is, that nodesData object is changing all the time. To make a proper calculation of ok,nok luminaires, we make a copy of it: - let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); - - const ts = Date.now(); - const keys = Object.keys(nodesData_clone); - - const number_of_luminaires = keys.length; - let number_of_ok_luminaires = 0; - let number_of_nok_luminaires = 0; - - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let nodeObj = nodesData_clone[key]; - if (nodeObj.tbname == undefined) continue; - - if (nodeObj.status === "OFFLINE") { - nodeObj.node_status_before_offline === true ? number_of_ok_luminaires++ : number_of_nok_luminaires++; - } - else if (nodeObj.status == true) number_of_ok_luminaires++; - else number_of_nok_luminaires++; - - } - - const values = { - "number_of_luminaires": number_of_luminaires, - "number_of_ok_luminaires": number_of_ok_luminaires, - "number_of_nok_luminaires": number_of_nok_luminaires, - "edge_date_time": ts - ts % 60000 //round to full minute - }; - - sendTelemetry(values, SETTINGS.rvoTbName, ts); - } - - - function handleRsPort() { - - console.log("cmd_man: handleRsPort called"); - //! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0" - // const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM - // const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI - - if (SETTINGS.serial_port == "" || SETTINGS.serial_port == undefined || SETTINGS.serial_port.length === 1) SETTINGS.serial_port = "ttymxc4"; - console.log('SETTINGS.serial_port', SETTINGS.serial_port); - //rsPort = new SerialPort({path: `/dev/${SETTINGS.serial_port}`, baudRate: 57600, autoOpen: false }); - rsPort = new SerialPort({ path: "/dev/ttyUSB0", baudRate: 9600, autoOpen: false }); - //(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit - //rsPort.setMaxListeners(0); - - rsPort.on('open', async function() { - - logger.debug("CMD manager - rsPort opened success"); - console.log("CMD manager - rsPort opened success"); - - await runSyncExec(`stty -F /dev/${SETTINGS.serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function(status) { - instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status); - - logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status); - - }).catch(function(reason) { - instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason); - console.log("cmd_man: rsport error", reason); - }); - }); - - rsPort.on('error', function(err) { - - //TODO report to service!!! - //errLogger.error(exports.title, "unable to open port", SETTINGS.serial_port, err.message); - errorHandler.sendMessageToService([exports.title, "unable to open port", SETTINGS.serial_port, err.message], 0); - console.log("cmd_manager: unable to open rsport", SETTINGS.serial_port, err.message); - instance.send(SEND_TO.debug, err.message); - }); - - rsPort.on("close", () => { - setTimeout(() => rsPort.open(), 1000); - }); - - - rsPort.open(function(err) { - if (err) console.log('rsport open error', err); - }) - } - - - instance.on("close", () => { - clearInterval(interval); - clearInterval(customTasksInterval); - clearInterval(setCorrectTime); - clearInterval(sendNodeReadout); - clearInterval(accelerometerInterval); - rsPort.close(); - }); - - instance.on("0", _ => { - main(); - }) - - instance.on("1", async function(flowdata) { - - //instance.send(SEND_TO.debug, "on Data"); - //instance.send(SEND_TO.debug, flowdata); - - //logger.debug(flowdata.data); - - //just testing functions - if (flowdata.data == "open") { - if (!rsPort.isOpen) rsPort.open(); - return; - } - else if (flowdata.data == "close") { - rsPort.close(); - return; - } - else if (flowdata.data == "clean") { - tasks = []; - return; - } - else if (flowdata.data == "buildtasks") { - //build & run - return; - } - else if (flowdata.data == "run") { - //durations = []; - - if (tasks.length == 0) { - - buildTasks(); - - if (rsPort.isOpen) { - interval = setInterval(runTasks, 100); - } - else { - instance.send(SEND_TO.debug, "port is not opened!!!"); - } - } - } - else { - //terminal data - object - //logger.debug("flowdata", flowdata.data); - - if (typeof flowdata.data === 'object') { - //logger.debug("dido", flowdata.data); - if (flowdata.data.hasOwnProperty("sender")) { - //data from dido_controller - if (flowdata.data.sender == "dido_controller") { - - if (flowdata.data.hasOwnProperty("cmd")) { - let cmd = flowdata.data.cmd; - - if (cmd == "buildTasks") { - clearInterval(interval); - - logger.debug("-->CMD MANAGER - BUILD TASKS"); - buildTasks(); - - //logger.debug("tasks:"); - //logger.debug(tasks); - - logger.debug("-->CMD MANAGER - RUN TASKS"); - interval = setInterval(runTasks, 5000); - } - else if (cmd == "reload_relays") { - loadRelaysData(flowdata.data.line); - - if (flowdata.data.dataChanged) { - if (!flowdata.data.value) { - reportOfflineNodeStatus(flowdata.data.line); - } - else { - reportOnlineNodeStatus(flowdata.data.line); - } - } - - } - else if (cmd == "rotary_switch_state") { - let value = flowdata.data.value; - - //state was changed - if (rotary_switch_state != value) { - if (value == "Off") { - //vyreportovat vsetky svietdla - reportOfflineNodeStatus(); - } - - rotary_switch_state = value; - } - } - else if (cmd == "lux_sensor") { - lux_sensor = parseInt(flowdata.data.value); - - // POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ?? - if (lux_sensor < 100) { - - // we send lux_sensor value to all nodes: - let params = getParams(PRIORITY_TYPES.node_broadcast); - - params.recipient = 2;//2 broadcast, address = 0 - params.address = 0xffffffff;//Broadcast - - let ba = longToByteArray(lux_sensor); - - params.byte3 = ba[1];//msb - params.byte4 = ba[0]; - params.timestamp = PRIORITY_TYPES.node_broadcast; - params.info = "run broadcast: Actual Lux level from cabinet"; - params.register = 95;//Actual Lux level from cabinet - params.rw = 1;//write - - tasks.push(params); - - //process profiles - turnOnOffLinesAccordingToLuxSensor(lux_sensor); - } - } - else if (cmd == "state_of_breaker") { - //istic linie - let value = flowdata.data.value; - let line = parseInt(flowdata.data.line); - - let dataChanged = false; - if (state_of_breaker[line] != value) dataChanged = true; - - state_of_breaker[line] = value; + //fix + //params.address = params.adress; + logger.debug("received from terminal", params); + logger.debug("date/time:", new Date()); + logger.debug("tasks length:", tasks.length); - let status = "OK"; - if (value == "Off") status = "NOK"; - - if (dataChanged) { - - if (relaysData.hasOwnProperty(line)) { - let tbname = relaysData[line].tbname; - - if (value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); - else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", { line: line }, "", SEND_TO.tb, instance, "circuit_breaker"); - - //report status liniu - sendTelemetry({ status: status }, tbname) - - //current value - if (value == "Off") reportOfflineNodeStatus(line); //vyreportovat vsetky svietidla na linii - } - - } - } - else { - logger.debug("undefined cmd", cmd); - } - } - } - - return; - } - - //data from worksys - if (flowdata.data.hasOwnProperty("topic")) { - - let data = getNested(flowdata.data, "content", "data"); - - //if we get temperature in senica from senica-prod01 - let temperature = getNested(flowdata.data, "content", "senica_temperature"); - - if (temperature !== undefined) { - temperatureInSenica = temperature; - return; - } - - if (data === undefined) { - console.log("Invalid rpc command came from platform"); - return; - } - - let command = data.params.command; - let method = data.method; - let profile = data.params.payload; - if (profile == undefined) profile = ""; - let entity = data.params.entities[0]; - let entity_type = entity.entity_type; - let tbname = entity.tb_name; - - instance.send(SEND_TO.debug, flowdata.data); - logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method); - logger.debug("----------------------------"); - - if (entity_type == "street_luminaire" || entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4") { - if (method == "set_command") { - - //let command = data.params.command; - let value = data.params.payload.value; - - if (command == "dimming") { - - let nodeWasFound = false; - let keys = Object.keys(nodesData); - - //logger.debug("-----", keys); - - for (let i = 0; i < keys.length; i++) { - let node = keys[i]; - //logger.debug( node, nodesData[node], tbname); - - if (tbname == nodesData[node].tbname) { - let params = getParams(PRIORITY_TYPES.high_priority); - - value = parseInt(value); - if (value > 0) value = value + 128; - - params.type = "node-onetime-write"; - params.tbname = tbname; - params.address = node; - params.register = 1; - params.recipient = 1; - params.byte4 = value; - params.rw = 1; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'set dimming from platform'; - //params.debug = true; - - //debug(params); - logger.debug("dimming", params); - - tasks.push(params); - - setTimeout(function() { - - //spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority - //a pridame aj vyreportovanie dimmingu - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 1; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read dimming (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - vykon - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 76; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Power (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - prud svietidla - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 75; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read Input Current (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - //pridame aj vyreportovanie - power faktor - ucinnik - { - let params = getParams(PRIORITY_TYPES.high_priority); - - params.type = "node-onetime-read"; - params.tbname = tbname; - params.address = node; - params.register = 77; - params.recipient = 1; - params.rw = 0; - params.timestamp = PRIORITY_TYPES.high_priority; - params.info = 'read power factor (after set dimming from platform)'; - //params.debug = true; - - tasks.push(params); - } - - }, 4000); - - nodeWasFound = true; - break; - } - } - - if (!nodeWasFound) { - logger.debug("set dimming from platform", "unable to find tbname", tbname); - } - } - else { - instance.send(SEND_TO.debug, "undefined command " + command); - logger.debug("undefined command", command); - } - - return; - } - else if (method == "set_profile") { - //nastav profil nodu - logger.debug("-->set_profile for node", data.params); - logger.debug("------profile data", profile); - //instance.send(SEND_TO.debug, "set_profile" + command); - - let keys = Object.keys(nodesData); - for (let i = 0; i < keys.length; i++) { - let node = keys[i]; - if (tbname == nodesData[node].tbname) { - - if (profile != "") profile = JSON.stringify(profile); - dbNodes.modify({ processed: false, profile: profile }).where("node", node).callback(function(err, response) { - - logger.debug("worksys - update node profile done", profile); - if (profile === "") logger.debug("worksys - update node profile done - profile is empty"); - - //profil úspešne prijatý pre node č. xx - sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", { node: node }, profile, SEND_TO.tb, instance); - - nodesData[node].processed = false; - nodesData[node].profile = profile; - - processNodeProfile(node); - }); - } - } - } - else { - - instance.send(SEND_TO.debug, "unknown method " + method); - logger.debug("unknown method", method); - - return; - } - } - - //nastav profil linie z platformy - else if (entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se") { - //profil linie - //relays.table line:number|tbname:string|contactor:number|profile:string - //najdeme line relaysData - - if (method == "set_profile") { - - logger.debug("-->set_profile for line", data.params); - logger.debug("profile data:", profile); - - let keys = Object.keys(relaysData); - for (let i = 0; i < keys.length; i++) { - let line = keys[i]; - if (tbname == relaysData[line].tbname) { - //zmazeme tasky - removeTask({ type: "relay", line: line }); - - if (profile != "") profile = JSON.stringify(profile); - dbRelays.modify({ profile: profile }).where("line", line).callback(function(err, response) { - - //update profile - logger.debug("worksys - update relay profile done:", profile); - instance.send(SEND_TO.debug, "worksys - update relay profile done"); - - relaysData[line].profile = profile; - - loadRelaysData(line) - logger.debug("loadRelaysData DONE for line", line); - - buildTasks({ processLineProfiles: true, line: line }); - - sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", { line: line }, profile, SEND_TO.tb, instance); - }); - break; - } - } - } - else if (method == "set_command") { - let value = data.params.payload.value; - - if (command === "switch") { - - // if we receive rpc from platform, to switch maintenance mode, we set SETTINGS.maintenance_mode flow variable to value; - if (entity_type === "edb" || entity_type === "edb_ver4_se") SETTINGS.maintenance_mode = value; - - const relayObject = getObjectByTbValue(relaysData, tbname); - let line = 0; - if (isObject(relayObject)) line = relayObject.line; + //tasks = []; - // v relaysData je contactor bud 0 alebo 1, ale z platformy prichadza true, false; - if (value == false) turnLine("off", line, "command received from platform"); - else turnLine("on", line, "command received from platform"); - } - } - else { - instance.send(SEND_TO.debug, "undefined method " + method); - logger.debug("undefined method", method); - } + //add to tasks + tasks.push(params); - return; - } - else { - instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type); - logger.debug("UNKNOW entity_type", entity_type); - } - return; - } + } + } + }) - //terminal - if (!rsPort.isOpen) await rsPort.open(); - let params = flowdata.data.body; - if (params == undefined) { - //logger.debug("CMD manager flowdata.data.body is undefined"); - return; - } + //function gets value of a nested property in an object and returns undefined if it does not exists: + function getNested(obj, ...args) { + return args.reduce((obj, level) => obj && obj[level], obj) + } - params.priority = PRIORITY_TYPES.terminal; - params.type = "cmd-terminal"; - params.tbname = ""; - params.timestamp = PRIORITY_TYPES.terminal; - params.addMinutesToTimestamp = 0;// do not repeat task!!! - params.debug = true; - let timestamp = Date.now(); - params.refFlowdataKey = timestamp; - //params.refFlowdata = flowdata; - //refFlowdata = flowdata; + /** + * setCorrectTime function runs once per hour + * If it is 3 o'clock, it sets actual time, which is got from services + * https://service-prod01.worksys.io/gettime + * If also detects Read Only Filesystem once a day + */ + function setCorrectPlcTimeOnceADay() { - //console.log("flowdata", flowdata); + const currentTime = new Date(); + if (currentTime.getHours() != 3) return; - cleanUpRefFlowdataObj(); + RESTBuilder.make(function(builder) { - refFlowdataObj[timestamp] = flowdata; + if (!builder) return; - //fix - //params.address = params.adress; - logger.debug("received from terminal", params); - logger.debug("date/time:", new Date()); - logger.debug("tasks length:", tasks.length); + builder.method('GET'); + builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); - //tasks = []; + builder.callback(function(err, response, output) { - //add to tasks - tasks.push(params); + if (err) { + console.log(err); + return; + } - } - } - }) + const res = output.response; + try { - //function gets value of a nested property in an object and returns undefined if it does not exists: - function getNested(obj, ...args) { - return args.reduce((obj, level) => obj && obj[level], obj) - } + const obj = JSON.parse(res); + let d = new Date(obj.date); + const now = new Date(); - /** - * setCorrectTime function runs once per hour - * If it is 3 o'clock, it sets actual time, which is got from services - * https://service-prod01.worksys.io/gettime - * If also detects Read Only Filesystem once a day - */ - function setCorrectPlcTimeOnceADay() { + let diffInMinutes = now.getTimezoneOffset(); + console.log("---->TimezoneOffset", diffInMinutes); - const currentTime = new Date(); - if (currentTime.getHours() != 3) return; + if (d instanceof Date) { - RESTBuilder.make(function(builder) { + // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); - if (!builder) return; + let year = d.getFullYear(); + let month = addZeroBefore(d.getMonth() + 1); + let day = addZeroBefore(d.getDate()); - builder.method('GET'); - builder.url('http://192.168.252.2:8004/gettime?projects_id=1'); + let hours = addZeroBefore(d.getHours()); + let minutes = addZeroBefore(d.getMinutes()); + let seconds = addZeroBefore(d.getSeconds()); - builder.callback(function(err, response, output) { + let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - if (err) { - console.log(err); - return; - } + exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); + console.log(dateStr); - const res = output.response; + monitor.info("failed timedatectl set-time", err, stderr); + } + else { + monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); + } - try { + }); + } - const obj = JSON.parse(res); - let d = new Date(obj.date); + } catch (error) { + logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); + monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); + } - const now = new Date(); + // we detect readOnlyFileSystem once an hour as well + detectReadOnlyFilesystem(); - let diffInMinutes = now.getTimezoneOffset(); - console.log("---->TimezoneOffset", diffInMinutes); + }); + }); - if (d instanceof Date) { + } - // monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours()); - let year = d.getFullYear(); - let month = addZeroBefore(d.getMonth() + 1); - let day = addZeroBefore(d.getDate()); + function detectReadOnlyFilesystem() { + exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { + if (err || stderr) { + console.error(err); + console.log(stderr); - let hours = addZeroBefore(d.getHours()); - let minutes = addZeroBefore(d.getMinutes()); - let seconds = addZeroBefore(d.getSeconds()); + } else { + //console.log("Read-only", stdout); - let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + let lines = stdout + ""; + lines = lines.split("\n"); - exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); - console.log(dateStr); + let readOnlyDetected = ""; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith("/dev/mmcblk0p2")) { + readOnlyDetected = lines[i]; + } + } - monitor.info("failed timedatectl set-time", err, stderr); - } - else { - monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr); - } + if (readOnlyDetected !== "") { + errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); + monitor.info("Read only filesystem detected"); + } - }); - } + } + }); + } - } catch (error) { - logger.debug("setCorrectPlcTimeOnceADay - function error", error, res); - monitor.info("setCorrectPlcTimeOnceADay - function error", error, res); - } - // we detect readOnlyFileSystem once an hour as well - detectReadOnlyFilesystem(); - }); - }); - } - function detectReadOnlyFilesystem() { - exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => { - if (err || stderr) { - console.error(err); - console.log(stderr); - } else { - //console.log("Read-only", stdout); - let lines = stdout + ""; - lines = lines.split("\n"); + ///helper functions + function sendTelemetry(values, tbname, date = Date.now()) { + const dataToTb = { + [tbname]: [ + { + "ts": date, + "values": values + } + ] + } - let readOnlyDetected = ""; - for (let i = 0; i < lines.length; i++) { - if (lines[i].startsWith("/dev/mmcblk0p2")) { - readOnlyDetected = lines[i]; - } - } + tbHandler.sendToTb(dataToTb, instance); + } - if (readOnlyDetected !== "") { - errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected); - monitor.info("Read only filesystem detected"); - } + function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) { - } - }); - } + if (date === undefined) date = new Date(); + //if(duskOffset === undefined) duskOffset = 0; + //if(dawnOffset === undefined) dawnOffset = 0; + //let line = keys[i]; + let profilestr = ""; + if (relaysData[line] != undefined) profilestr = relaysData[line].profile; + let result = {}; + var times = SunCalc.getTimes(date, latitude, longitude); + let dawn = new Date(times.sunrise);//usvit + let dusk = new Date(times.sunset);//sumrak + //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 + //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 + let dusk_astro_clock_offset = duskOffset;//minutes + let dawn_astro_clock_offset = dawnOffset;//minutes - ///helper functions - function sendTelemetry(values, tbname, date = Date.now()) { - const dataToTb = { - [tbname]: [ - { - "ts": date, - "values": values - } - ] - } + try { - tbHandler.sendToTb(dataToTb, instance); - } + let profile = JSON.parse(profilestr); + if (Object.keys(profile).length === 0) throw ("profile is not defined"); - function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0) { + //Jednoduchý režim + if (profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) { - 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; + //Režim astrohodín + if (profile.astro_clock == true) { + //if(profile.dusk_lux_sensor == false) + { + if (profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt(profile.dusk_astro_clock_offset); + } - let result = {}; + //if(profile.dawn_lux_sensor == false) + { + if (profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt(profile.dawn_astro_clock_offset); + } - var times = SunCalc.getTimes(date, latitude, longitude); - let dawn = new Date(times.sunrise);//usvit - let dusk = new Date(times.sunset);//sumrak + } + //dusk - súmrak + //down, sunrise - svitanie - //http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06 - //https://mapa.zoznam.sk/zisti-gps-suradnice-m6 + } 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()); - let dusk_astro_clock_offset = duskOffset;//minutes - let dawn_astro_clock_offset = dawnOffset;//minutes + dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset * 60000); + dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset * 60000); - try { + result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); + result.dusk_hours = dusk.getHours(); + result.dusk_minutes = dusk.getMinutes(); - let profile = JSON.parse(profilestr); - if (Object.keys(profile).length === 0) throw ("profile is not defined"); + result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + result.dawn_hours = dawn.getHours(); + result.dawn_minutes = dawn.getMinutes(); - //Jednoduchý režim - if (profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false) { + 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; - //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); - } + return result; + } - //if(profile.dawn_lux_sensor == false) - { - if (profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt(profile.dawn_astro_clock_offset); - } - } + function processResponse(register, bytes) { + + let values = {}; + + let byte3 = bytes[0]; + let byte2 = bytes[1]; + let byte1 = bytes[2]; + let byte0 = bytes[3]; + + //status + if (register == 0) { + let statecode = bytesToInt(bytes); + values = { "statecode": statecode }; + return values; + } + + //Dimming, CCT + else if (register == 1) { + let brightness = 0; + let dimming = byte0; + if (dimming > 128) { + //dimming = -128; + brightness = dimming - 128; + } + + //cct + //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 + let cct; + if (byte3 == 1) cct = byte2 * 256 + byte1; + else cct = bytesToInt(bytes.slice(0, 3)); + + //cct podla auditu + + values["dimming"] = brightness; + return values; + } + + // + else if (register == 4) { + values["master_node_version"] = bytes[1] + "." + bytes[2]; + //logger.debug("FW Version", register, bytes); + } + + //Napätie + else if (register == 74) { + let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); + values["voltage"] = Number(voltage); + } + + //Prúd + else if (register == 75) { + let current = bytesToInt(bytes); + values["current"] = current; + } + + //výkon + else if (register == 76) { + let power = (bytesToInt(bytes) * 0.1).toFixed(2); + values["power"] = Number(power); + } + + //účinník + else if (register == 77) { + let power_factor = Math.cos(bytesToInt(bytes) * 0.1 * (Math.PI / 180)).toFixed(2); + values["power_factor"] = Number(power_factor); + } + + //frekvencia + else if (register == 78) { + let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); + values["frequency"] = Number(frequency); + } + + //energia + else if (register == 79) { + let energy = bytesToInt(bytes); + values["energy"] = energy / 1000; //energia v kWh -> delit 1000 + } + + //doba života + else if (register == 80) { + let lifetime = (bytesToInt(bytes) / 60).toFixed(2); + values["lifetime"] = Number(lifetime); + } + + //nastavenie profilu + else if (register == 8) { + let time_schedule_settings = bytesToInt(bytes); + values["time_schedule_settings"] = time_schedule_settings; + } + + //naklon - nateraz sa z nodu nevycitava! kvoli problemom s accelerometrom a vracanymi hodnotami, posielame temp a x y z vo funkcii accelerometerData() + else if (register == 84) { + values["temperature"] = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; + values["inclination_x"] = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; + values["inclination_y"] = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; + values["inclination_z"] = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; + } + + //FW verzia nodu + else if (register == 89) { + //formát: "Byte3: Byte2.Byte1 (Byte0)" + values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; + } + + else if (register == 87 || register == 6 || register == 7) { + var d = new Date(); + d.setHours(byte3, byte2, 0, 0); + let timestamp = d.getTime(); + + //aktuálny čas + if (register == 87) values["actual_time"] = timestamp; + //čas súmraku + else if (register == 6) values["dusk_time"] = timestamp; + //čas úsvitu + else if (register == 7) values["dawn_time"] = timestamp; + } + + return values; + } + + + //byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB + function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { + let resp = []; + + let cmd = register; + + if (typeof adresa === 'string') adresa = parseInt(adresa); + if (typeof byte1 === 'string') byte1 = parseInt(byte1); + if (typeof byte2 === 'string') byte2 = parseInt(byte2); + if (typeof byte3 === 'string') byte3 = parseInt(byte3); + if (typeof byte4 === 'string') byte4 = parseInt(byte4); + + if (rw === 0) { + cmd = cmd + 0x8000; + } + + //master + if (rec === 0) adresa = 0; + + if (rec === 2) { + adresa = 0xffffffff;//Broadcast + } + + //recipient + if (rec === 3) { + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(0xFF); + resp.push(adresa & 0xFF);//band + } + else { + resp.push((adresa >> 24) & 0xFF);//rshift + resp.push((adresa >> 16) & 0xFF); + resp.push((adresa >> 8) & 0xFF); + resp.push(adresa & 0xFF); + + if (rec === 2) { + resp.push(0xFF); + } + else resp.push(0); + } + + resp.push((cmd >> 8) & 0xFF);//rshift + resp.push(cmd & 0xFF);//band + resp.push(byte1 & 0xFF);//band + resp.push(byte2 & 0xFF);//band + resp.push(byte3 & 0xFF);//band + resp.push(byte4 & 0xFF);//band - //dusk - súmrak - //down, sunrise - svitanie + //let data = '12345'; + let crc = crc16('ARC', resp); + let c1 = (crc >> 8) & 0xFF; + let c2 = crc & 0xFF; - } catch (error) { - if (profilestr != "") { - logger.debug(profilestr); - logger.debug(error); - } - } + resp.push(c1); + resp.push(c2); - result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes()); - result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes()); + //logger.debug("checksum", crc); + //logger.debug("resp", resp); - dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset * 60000); - dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset * 60000); + return resp; - 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(); + function getObjectByTbValue(object, tbname) { + return object[Object.keys(object).find(key => object[key].tbname === tbname)]; + } - result.dusk_time = dusk.getTime(); - result.dawn_time = dawn.getTime(); + function isObject(item) { + return (typeof item === "object" && !Array.isArray(item) && item !== null); + } - result.dusk_astro_clock_offset = dusk_astro_clock_offset; - result.dawn_astro_clock_offset = dawn_astro_clock_offset; - return result; - } + // we fake data, that should be received from accelerometer, as they are a bit unreliable. (temperature, x,y,z) + function accelerometerData() { + if (temperatureInSenica === null) return; - function processResponse(register, bytes) { - - let values = {}; - - let byte3 = bytes[0]; - let byte2 = bytes[1]; - let byte1 = bytes[2]; - let byte0 = bytes[3]; - - //status - if (register == 0) { - let statecode = bytesToInt(bytes); - values = { "statecode": statecode }; - return values; - } - - //Dimming, CCT - else if (register == 1) { - let brightness = 0; - let dimming = byte0; - if (dimming > 128) { - //dimming = -128; - brightness = dimming - 128; - } - - //cct - //Ak Byte3 == 1: CCT = (Byte2*256)+Byte1 - let cct; - if (byte3 == 1) cct = byte2 * 256 + byte1; - else cct = bytesToInt(bytes.slice(0, 3)); - - //cct podla auditu - - values["dimming"] = brightness; - return values; - } - - // - else if (register == 4) { - values["master_node_version"] = bytes[1] + "." + bytes[2]; - //logger.debug("FW Version", register, bytes); - } - - //Napätie - else if (register == 74) { - let voltage = (bytesToInt(bytes) * 0.1).toFixed(1); - values["voltage"] = Number(voltage); - } - - //Prúd - else if (register == 75) { - let current = bytesToInt(bytes); - values["current"] = current; - } - - //výkon - else if (register == 76) { - let power = (bytesToInt(bytes) * 0.1).toFixed(2); - values["power"] = Number(power); - } - - //účinník - else if (register == 77) { - let power_factor = Math.cos(bytesToInt(bytes) * 0.1 * (Math.PI / 180)).toFixed(2); - values["power_factor"] = Number(power_factor); - } - - //frekvencia - else if (register == 78) { - let frequency = (bytesToInt(bytes) * 0.1).toFixed(2); - values["frequency"] = Number(frequency); - } - - //energia - else if (register == 79) { - let energy = bytesToInt(bytes); - console.log("bytesToIng ",bytesToInt(bytes)) - //Energiu treba reportovať v kWh -> delit 1000 - values["energy"] = energy / 1000; - } - - //doba života - else if (register == 80) { - let lifetime = (bytesToInt(bytes) / 60).toFixed(2); - values["lifetime"] = Number(lifetime); - } - - //nastavenie profilu - else if (register == 8) { - let time_schedule_settings = bytesToInt(bytes); - values["time_schedule_settings"] = time_schedule_settings; - } - - //naklon - nateraz sa z nodu nevycitava! kvoli problemom s accelerometrom a vracanymi hodnotami, posielame temp a x y z vo funkcii accelerometerData() - else if (register == 84) { - values["temperature"] = byte3 >= 128 ? (byte3 - 128) * (-1) : byte3; - values["inclination_x"] = byte2 >= 128 ? (byte2 - 128) * (-1) : byte2; - values["inclination_y"] = byte1 >= 128 ? (byte1 - 128) * (-1) : byte1; - values["inclination_z"] = byte0 >= 128 ? (byte0 - 128) * (-1) : byte0; - } - - //FW verzia nodu - else if (register == 89) { - //formát: "Byte3: Byte2.Byte1 (Byte0)" - values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")"; - } - - else if (register == 87 || register == 6 || register == 7) { - var d = new Date(); - d.setHours(byte3, byte2, 0, 0); - let timestamp = d.getTime(); - - //aktuálny čas - if (register == 87) values["actual_time"] = timestamp; - //čas súmraku - else if (register == 6) values["dusk_time"] = timestamp; - //čas úsvitu - else if (register == 7) values["dawn_time"] = timestamp; - } - - return values; - } - - - //byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB - function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) { - let resp = []; - - let cmd = register; - - if (typeof adresa === 'string') adresa = parseInt(adresa); - if (typeof byte1 === 'string') byte1 = parseInt(byte1); - if (typeof byte2 === 'string') byte2 = parseInt(byte2); - if (typeof byte3 === 'string') byte3 = parseInt(byte3); - if (typeof byte4 === 'string') byte4 = parseInt(byte4); - - if (rw === 0) { - cmd = cmd + 0x8000; - } - - //master - if (rec === 0) adresa = 0; - - if (rec === 2) { - adresa = 0xffffffff;//Broadcast - } - - //recipient - if (rec === 3) { - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push(0xFF); - resp.push(adresa & 0xFF);//band - } - else { - resp.push((adresa >> 24) & 0xFF);//rshift - resp.push((adresa >> 16) & 0xFF); - resp.push((adresa >> 8) & 0xFF); - resp.push(adresa & 0xFF); - - if (rec === 2) { - resp.push(0xFF); - } - else resp.push(0); - } - - resp.push((cmd >> 8) & 0xFF);//rshift - resp.push(cmd & 0xFF);//band - resp.push(byte1 & 0xFF);//band - resp.push(byte2 & 0xFF);//band - resp.push(byte3 & 0xFF);//band - resp.push(byte4 & 0xFF);//band - - //let data = '12345'; - let crc = crc16('ARC', resp); - let c1 = (crc >> 8) & 0xFF; - let c2 = crc & 0xFF; + //clone nodesData and relaysData objects + let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); + let relaysData_clone = JSON.parse(JSON.stringify(relaysData)); - resp.push(c1); - resp.push(c2); + for (const key in relaysData_clone) { - //logger.debug("checksum", crc); - //logger.debug("resp", resp); + const lineData = relaysData_clone[key]; + const lineNumber = lineData.line; + const contactor = lineData.contactor; - return resp; + if (lineNumber === 0) continue; - } + if (contactor === 1) { - function getObjectByTbValue(object, tbname) { - return object[Object.keys(object).find(key => object[key].tbname === tbname)]; - } + let date = Date.now(); - function isObject(item) { - return (typeof item === "object" && !Array.isArray(item) && item !== null); - } + Object.keys(nodesData_clone).forEach((node, index) => { + setTimeout(function() { - // we fake data, that should be received from accelerometer, as they are a bit unreliable. (temperature, x,y,z) - function accelerometerData() { + if (nodesData_clone[node].line === lineNumber) { - if (temperatureInSenica === null) return; + // NOTE: if status of luminaire is NOK or OFFLINE, we do not send data; + let status = nodesData_clone[node].status; + if (status === "OFFLINE" || !status) return; - //clone nodesData and relaysData objects - let nodesData_clone = JSON.parse(JSON.stringify(nodesData)); - let relaysData_clone = JSON.parse(JSON.stringify(relaysData)); + let x = null; + if (naklony.hasOwnProperty(node)) x = naklony[node].naklon; + if (x === null) x = 0; - for (const key in relaysData_clone) { + sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData_clone[node].tbname, date); + } - const lineData = relaysData_clone[key]; - const lineNumber = lineData.line; - const contactor = lineData.contactor; + }, (index + 1) * 500); + }) - if (lineNumber === 0) continue; - - if (contactor === 1) { - - let date = Date.now(); - - Object.keys(nodesData_clone).forEach((node, index) => { - - setTimeout(function() { - - if (nodesData_clone[node].line === lineNumber) { - - // NOTE: if status of luminaire is NOK or OFFLINE, we do not send data; - let status = nodesData_clone[node].status; - if (status === "OFFLINE" || !status) return; - - let x = null; - if (naklony.hasOwnProperty(node)) x = naklony[node].naklon; - if (x === null) x = 0; - - sendTelemetry({ temperature: Math.round(temperatureInSenica + 10 + Math.floor(Math.random() * 3)), inclination_x: x, inclination_y: 0, inclination_z: 0 }, nodesData_clone[node].tbname, date); - } - - }, (index + 1) * 500); - }) - - } - } - } + } + } + } } // end of instance.export diff --git a/flow/db_init.js b/flow/db_init.js index 2a5caf9..60a0510 100644 --- a/flow/db_init.js +++ b/flow/db_init.js @@ -6,33 +6,14 @@ exports.version = '1.0.2'; exports.icon = 'sign-out'; exports.output = 2; -exports.html = `
-
-
-
Hostname or IP address (if not empty - setting will override db setting)
-
-
-
Port
-
-
-
-
-
@(Client id)
-
-
-
@(Username)
-
-
-
`; - - exports.readme = ` -# DB initialization + # DB initialization `; const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const { initNotification } = require('./helper/notification_reporter'); const errorHandler = require('./helper/ErrorToServiceHandler'); +const total_energy = require('../databases/total_energy'); const SEND_TO = { db_init: 0, @@ -41,7 +22,6 @@ const SEND_TO = { exports.install = async function(instance) { - const dbNodes = TABLE("nodes"); const dbRelays = TABLE("relays"); const dbSettings = TABLE("settings"); @@ -70,7 +50,7 @@ exports.install = async function(instance) { Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {}) dbs.settings = { - edge_fw_version: "2025-04-24", //rok-mesiac-den + edge_fw_version: "2025-07-08", //rok-mesiac-den language: responseSettings[0]["lang"], rvo_name: responseSettings[0]["rvo_name"], project_id: responseSettings[0]["project_id"], @@ -98,6 +78,11 @@ exports.install = async function(instance) { maintenance_mode: false, } + + let rvo_number = responseSettings[0]["rvo_name"].match(/\D+(\d{1,2})_/)[1]; + dbs.settings.energy_to_switch_lamps = total_energy[rvo_number]; + if (dbs.settings.energy_to_switch_lamps === undefined) console.log('=============== db_init.js: energy_to_switch_lamps is undefined'); + FLOW.dbLoaded = true; errorHandler.setProjectId(dbs.settings.project_id); initNotification(); diff --git a/flow/designer.json b/flow/designer.json index e5d2a7c..7565777 100644 --- a/flow/designer.json +++ b/flow/designer.json @@ -36,13 +36,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#DA4453", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#DA4453", + "notes": "" }, { "id": "1612776786008", @@ -89,14 +89,14 @@ "text": "Connected", "color": "green" }, - "color": "#888600", - "notes": "", "options": { "username": "", "clientid": "", "port": "1883", "host": "" - } + }, + "color": "#888600", + "notes": "" }, { "id": "1612778461252", @@ -129,11 +129,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1612783322136", @@ -144,20 +144,22 @@ "y": 324, "connections": {}, "disabledio": { - "input": [], + "input": [ + 0 + ], "output": [] }, "state": { "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615551060773", @@ -177,13 +179,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#DA4453", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#DA4453", + "notes": "" }, { "id": "1615563373927", @@ -203,13 +205,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#DA4453", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#DA4453", + "notes": "" }, { "id": "1615566865233", @@ -227,11 +229,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1615798582262", @@ -251,13 +253,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615802995322", @@ -275,13 +277,13 @@ "text": "Disabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": false - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615809128443", @@ -299,13 +301,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1615809595184", @@ -323,11 +325,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1616165795916", @@ -356,9 +358,6 @@ "text": "Listening", "color": "green" }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, "options": { "timeout": 10, "cachepolicy": 0, @@ -368,12 +367,15 @@ "method": "POST", "name": "", "flags": [ - 10000, "id:1616165795916", - "post" + "post", + 10000 ], "emptyresponse": false - } + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false }, { "id": "1616165824813", @@ -391,11 +393,11 @@ "text": "", "color": "gray" }, - "color": "#5D9CEC", - "notes": "", "options": { "datatype": "json" - } + }, + "color": "#5D9CEC", + "notes": "" }, { "id": "1617104731852", @@ -415,13 +417,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1617114651703", @@ -446,12 +448,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "datatype": "object", - "data": "{line: 2, command: \"off\", force: true}" - } + "data": "{line: 3, command: \"turnOff\", force: true}", + "datatype": "object" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1617115013095", @@ -469,11 +471,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1617284749681", @@ -498,12 +500,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "data": "profile_nodes", - "datatype": "string" - } + "datatype": "string", + "data": "profile_nodes" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1618235171399", @@ -528,11 +530,11 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { "data": "run" - } + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1618300858252", @@ -550,13 +552,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1618393583970", @@ -574,11 +576,11 @@ "text": "from-dido-controller", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "from-dido-controller" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618393674428", @@ -603,11 +605,11 @@ "text": "platform-rpc-call", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "platform-rpc-call" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618393759854", @@ -636,11 +638,11 @@ "text": "cmd_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "cmd_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618393827655", @@ -658,11 +660,11 @@ "text": "cmd_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "cmd_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618558465485", @@ -680,11 +682,11 @@ "text": "platform-rpc-call", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "platform-rpc-call" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1618572059773", @@ -709,12 +711,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "data": "{line: 2, command: \"on\", force: true}", - "datatype": "object" - } + "datatype": "object", + "data": "{line: 1, command: \"turnOn\", force: true}" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1619515097737", @@ -767,9 +769,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#5D9CEC", - "notes": "", - "options": {} + "notes": "" }, { "id": "1619605019281", @@ -798,9 +800,6 @@ "text": "Listening", "color": "green" }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -810,11 +809,14 @@ "method": "GET", "name": "", "flags": [ - 5000, "id:1619605019281", - "get" + "get", + 5000 ] - } + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false }, { "id": "1619784672383", @@ -839,12 +841,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "datatype": "object", - "data": "{command: \"turnOnAlarm\"}" - } + "data": "{command: \"turnOnAlarm\"}", + "datatype": "object" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1619784812964", @@ -869,12 +871,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "datatype": "object", - "data": "{command: \"turnOffAlarm\"}" - } + "data": "{command: \"turnOffAlarm\"}", + "datatype": "object" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1621340721628", @@ -892,11 +894,11 @@ "text": "modbus_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "modbus_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1622640022885", @@ -921,9 +923,6 @@ "text": "Listening", "color": "green" }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, "options": { "timeout": 5, "cachepolicy": 0, @@ -932,11 +931,14 @@ "url": "/db_connector", "method": "POST", "flags": [ - 5000, "id:1622640022885", - "post" + "post", + 5000 ] - } + }, + "color": "#5D9CEC", + "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", + "cloning": false }, { "id": "1622640073521", @@ -961,11 +963,11 @@ "text": "", "color": "gray" }, - "color": "#2134B0", - "notes": "", "options": { "edge": "undefined" - } + }, + "color": "#2134B0", + "notes": "" }, { "id": "1622641420685", @@ -983,9 +985,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#5D9CEC", - "notes": "", - "options": {} + "notes": "" }, { "id": "1634303504177", @@ -1007,15 +1009,15 @@ "output": [] }, "state": { - "text": "595.82 MB / 982.12 MB", + "text": "840.05 MB / 985.68 MB", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { "enabled": true, "interval": 30000 - } + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1634303533779", @@ -1037,16 +1039,16 @@ "output": [] }, "state": { - "text": "3.80 GB / 6.86 GB", + "text": "5.78 GB / 7.26 GB", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { "enabled": true, "path": "/", "interval": 30000 - } + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1634303595494", @@ -1075,11 +1077,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1634303602169", @@ -1097,11 +1099,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1634303685503", @@ -1119,11 +1121,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1634303743260", @@ -1149,13 +1151,13 @@ "text": "", "color": "gray" }, - "color": "#5D9CEC", - "notes": "", "options": { - "url": "http://192.168.252.2:8004/sentmessage", + "stringify": "json", "method": "POST", - "stringify": "json" - } + "url": "http://192.168.252.2:8004/sentmessage" + }, + "color": "#5D9CEC", + "notes": "" }, { "id": "1634463186563", @@ -1175,13 +1177,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634464580289", @@ -1210,13 +1212,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, "code": "let response = {};\nresponse.cpu = value.cpu;\nresponse.uptime = value.uptime;\n\nsend(0, response);", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1634465243324", @@ -1236,13 +1238,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634465281992", @@ -1271,13 +1273,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, "code": "value.sender = \"ram\";\n\nlet response = {};\n\nresponse.memory_total = Math.round(value.total / (1024 ** 2));\nresponse.memory_free = Math.round(value.free / (1024 ** 2));\nresponse.memory_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1634465338103", @@ -1297,13 +1299,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634465821120", @@ -1332,13 +1334,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, "code": "value.sender = \"hdd\";\n\nlet response = {};\n\nresponse.hdd_total = Math.round(value.total / (1024 ** 2));\nresponse.hdd_free = Math.round(value.free / (1024 ** 2));\nresponse.hdd_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1634465892500", @@ -1358,13 +1360,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634484067516", @@ -1384,13 +1386,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1634488120710", @@ -1419,11 +1421,11 @@ "text": "", "color": "gray" }, - "color": "#2134B0", - "notes": "", "options": { "edge": "undefined" - } + }, + "color": "#2134B0", + "notes": "" }, { "id": "1635327431236", @@ -1443,13 +1445,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1635936391935", @@ -1467,11 +1469,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1637069803394", @@ -1493,11 +1495,9 @@ "output": [] }, "state": { - "text": "2.8% / 99.24 MB", + "text": "2.4% / 74.33 MB", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "monitorfiles": true, "monitorconnections": true, @@ -1505,7 +1505,9 @@ "monitorconsumption": true, "enabled": true, "interval": 30000 - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1683664161036", @@ -1525,13 +1527,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1683981346282", @@ -1560,11 +1562,11 @@ "text": "from-dido-controller", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "from-dido-controller" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1684055037116", @@ -1584,13 +1586,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1684060205000", @@ -1610,13 +1612,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1684179110403", @@ -1634,13 +1636,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1699963668903", @@ -1681,11 +1683,11 @@ "text": "", "color": "gray" }, - "color": "#2134B0", - "notes": "", "options": { "edge": "undefined" - } + }, + "color": "#2134B0", + "notes": "" }, { "id": "1699964678894", @@ -1714,11 +1716,11 @@ "text": "modbus_to_dido", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "modbus_to_dido" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1699964793925", @@ -1738,13 +1740,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1699965957410", @@ -1793,9 +1795,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#2134B0", - "notes": "", - "options": {} + "notes": "" }, { "id": "1700411878636", @@ -1844,9 +1846,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#5CB36D", - "notes": "", - "options": {} + "notes": "" }, { "id": "1714752862828", @@ -1864,13 +1866,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1717441414646", @@ -1899,13 +1901,13 @@ "text": "", "color": "gray" }, - "color": "#656D78", - "notes": "", "options": { "keepmessage": true, "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n\telse if(value.status === \"NOK-thermometer\")\n\t{\n\t\tsend(0, {\"thermometer\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"temperature\"))\n\t{\n\t\tsend(0, {\"thermometer\": \"OK\"});\n\t}\n}", "outputs": 1 - } + }, + "color": "#656D78", + "notes": "" }, { "id": "1717442627834", @@ -1925,13 +1927,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1717442631338", @@ -1949,11 +1951,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1718016045116", @@ -1978,11 +1980,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1718016052341", @@ -2011,15 +2013,15 @@ "text": "Running", "color": "gray" }, - "color": "#30E193", - "notes": "", "options": { "slack_channel": "C071KN2Q8SK", "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "test_rvo_debian12" - } + "name": "rvo_senica_33_10.0.0.127" + }, + "color": "#30E193", + "notes": "" }, { "id": "1718016073501", @@ -2044,13 +2046,13 @@ "text": "", "color": "gray" }, - "color": "#5D9CEC", - "notes": "", "options": { - "url": "http://192.168.252.2:8004/slack", + "stringify": "json", "method": "POST", - "stringify": "json" - } + "url": "http://192.168.252.2:8004/slack" + }, + "color": "#5D9CEC", + "notes": "" }, { "id": "1718016086212", @@ -2070,13 +2072,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1718016094070", @@ -2101,12 +2103,12 @@ "text": "", "color": "gray" }, - "color": "#F6BB42", - "notes": "", "options": { - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }", - "datatype": "object" - } + "datatype": "object", + "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" + }, + "color": "#F6BB42", + "notes": "" }, { "id": "1729855334955", @@ -2124,11 +2126,11 @@ "text": "platform-rpc-call", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "platform-rpc-call" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1729855371093", @@ -2146,13 +2148,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1731068658334", @@ -2177,11 +2179,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731068754606", @@ -2210,15 +2212,15 @@ "text": "Connected", "color": "green" }, - "color": "#888600", - "notes": "", "options": { "username": "", "clientid": "", "port": "2764", "host": "192.168.252.2", - "topic": "u38" - } + "topic": "" + }, + "color": "#888600", + "notes": "" }, { "id": "1731069001548", @@ -2249,9 +2251,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#888600", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069033416", @@ -2269,11 +2271,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069059135", @@ -2298,9 +2300,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#888600", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069079243", @@ -2318,13 +2320,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1731069116691", @@ -2349,9 +2351,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069131637", @@ -2376,9 +2378,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069137374", @@ -2403,9 +2405,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069179846", @@ -2430,9 +2432,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069192937", @@ -2457,9 +2459,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731069264443", @@ -2484,11 +2486,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069334626", @@ -2513,11 +2515,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069548145", @@ -2542,11 +2544,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731069567152", @@ -2571,11 +2573,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731070156936", @@ -2600,11 +2602,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1731234189516", @@ -2629,9 +2631,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1731234189551", @@ -2656,9 +2658,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" }, { "id": "1732700042559", @@ -2687,9 +2689,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#888600", - "notes": "", - "options": {} + "notes": "" }, { "id": "1732700057052", @@ -2714,11 +2716,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1732700071298", @@ -2736,13 +2738,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1732700642917", @@ -2760,11 +2762,11 @@ "text": "tb-push", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "tb-push" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1732889185927", @@ -2782,13 +2784,13 @@ "text": "Enabled", "color": "gray" }, - "color": "#967ADC", - "notes": "", "options": { "type": "data", "repository": false, "enabled": true - } + }, + "color": "#967ADC", + "notes": "" }, { "id": "1733574412965", @@ -2813,11 +2815,11 @@ "text": "db-init", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "db-init" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1747561603739", @@ -2835,11 +2837,11 @@ "text": "send-to-services", "color": "gray" }, - "color": "#303E4D", - "notes": "", "options": { "wirename": "send-to-services" - } + }, + "color": "#303E4D", + "notes": "" }, { "id": "1747562867845", @@ -2857,9 +2859,9 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#704cff", - "notes": "", - "options": {} + "notes": "" }, { "id": "1749211698385", @@ -2884,10 +2886,10 @@ "text": "", "color": "gray" }, + "options": {}, "color": "#F6BB42", - "notes": "", - "options": {} + "notes": "" } ], - "version": 624 -} \ No newline at end of file + "version": 615 +} diff --git a/flow/designer.json_orig b/flow/designer.json_orig deleted file mode 100644 index eb18e86..0000000 --- a/flow/designer.json_orig +++ /dev/null @@ -1,2775 +0,0 @@ -{ - "tabs": [ - { - "name": "MAIN PUSH", - "linker": "main-push", - "id": "1612772287426", - "index": 0 - }, - { - "name": "CMD manager", - "linker": "cmd-manager", - "id": "1615551125555", - "index": 1 - }, - { - "name": "Devices", - "linker": "devices", - "id": "1611921777196", - "index": 2 - } - ], - "components": [ - { - "id": "1611951142547", - "component": "debug", - "tab": "1611921777196", - "name": "ERROR", - "x": 598, - "y": 60, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#DA4453", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1612776786008", - "component": "wsmqttpublish", - "tab": "1612772287426", - "name": "WS MQTT publish", - "x": 290.75, - "y": 189, - "connections": { - "0": [ - { - "index": "0", - "id": "1615551060773" - } - ], - "1": [ - { - "index": "0", - "id": "1618300858252" - }, - { - "index": "0", - "id": "1618558465485" - } - ], - "2": [ - { - "index": "0", - "id": "1634303685503" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Connected", - "color": "green" - }, - "color": "#888600", - "notes": "", - "options": { - "username": "", - "clientid": "", - "port": "1883", - "host": "" - } - }, - { - "id": "1612778461252", - "component": "virtualwirein", - "tab": "1612772287426", - "name": "tb-push", - "x": 72.75, - "y": 328, - "connections": { - "0": [ - { - "index": "0", - "id": "1612783322136" - }, - { - "index": "1", - "id": "1612776786008" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "tb-push", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "tb-push" - } - }, - { - "id": "1612783322136", - "component": "debug", - "tab": "1612772287426", - "name": "to TB", - "x": 290.75, - "y": 330, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1615551060773", - "component": "debug", - "tab": "1612772287426", - "name": "errors from MQTT Broker", - "x": 594, - "y": 57, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#DA4453", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1615563373927", - "component": "debug", - "tab": "1615551125555", - "name": "Debug", - "x": 755, - "y": 155, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#DA4453", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1615566865233", - "component": "virtualwireout", - "tab": "1615551125555", - "name": "tb-push", - "x": 755, - "y": 248, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "tb-push", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "tb-push" - } - }, - { - "id": "1615798582262", - "component": "debug", - "tab": "1615551125555", - "name": "CMD_debug", - "x": 755, - "y": 346, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1615802995322", - "component": "debug", - "tab": "1611921777196", - "name": "Debug", - "x": 596.8833312988281, - "y": 566.3500061035156, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Disabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": false - } - }, - { - "id": "1615809128443", - "component": "debug", - "tab": "1611921777196", - "name": "tempToTb", - "x": 595.8833312988281, - "y": 658.3500061035156, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1615809595184", - "component": "virtualwireout", - "tab": "1611921777196", - "name": "tb-push", - "x": 597.8833312988281, - "y": 377.25, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "tb-push", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "tb-push" - } - }, - { - "id": "1616165795916", - "component": "httproute", - "tab": "1615551125555", - "name": "POST /terminal", - "x": 135, - "y": 547, - "connections": { - "0": [ - { - "index": "0", - "id": "1684060205000" - }, - { - "index": "1", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Listening", - "color": "green" - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /terminal__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __false__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, - "options": { - "timeout": 10, - "cachepolicy": 0, - "cacheexpire": "5 minutes", - "size": 5, - "url": "/terminal", - "method": "POST", - "name": "", - "flags": [ - 10000, - "id:1616165795916", - "post" - ], - "emptyresponse": false - } - }, - { - "id": "1616165824813", - "component": "httpresponse", - "tab": "1615551125555", - "name": "HTTP Response", - "x": 753, - "y": 423, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#5D9CEC", - "notes": "", - "options": { - "datatype": "json" - } - }, - { - "id": "1617104731852", - "component": "debug", - "tab": "1615551125555", - "name": "DIDO_Debug", - "x": 669, - "y": 1040, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1617114651703", - "component": "trigger", - "tab": "1615551125555", - "name": "turnOff line", - "x": 133, - "y": 1161, - "connections": { - "0": [ - { - "index": "1", - "id": "1699963668903" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "data": "{line: 1, command: \"off\", force: true}", - "datatype": "object" - } - }, - { - "id": "1617115013095", - "component": "virtualwireout", - "tab": "1615551125555", - "name": "tb-push", - "x": 669, - "y": 1150, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "tb-push", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "tb-push" - } - }, - { - "id": "1617284749681", - "component": "trigger", - "tab": "1615551125555", - "name": "update profile / node", - "x": 112, - "y": 208, - "connections": { - "0": [ - { - "index": "1", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "datatype": "string", - "data": "profile_nodes" - } - }, - { - "id": "1618235171399", - "component": "trigger", - "tab": "1615551125555", - "name": "tun tasks", - "x": 184, - "y": 279, - "connections": { - "0": [ - { - "index": "1", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "data": "run" - } - }, - { - "id": "1618300858252", - "component": "debug", - "tab": "1612772287426", - "name": "wsmqtt-exit1", - "x": 597.8833312988281, - "y": 149, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1618393583970", - "component": "virtualwireout", - "tab": "1615551125555", - "name": "to-cmd-manager", - "x": 668.8833312988281, - "y": 1269, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "from-dido-controller", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "from-dido-controller" - } - }, - { - "id": "1618393674428", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "platform-rpc-call", - "x": 132.88333129882812, - "y": 367, - "connections": { - "0": [ - { - "index": "1", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "platform-rpc-call", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "platform-rpc-call" - } - }, - { - "id": "1618393759854", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "cmd_to_dido", - "x": 119.88333129882812, - "y": 1007, - "connections": { - "0": [ - { - "index": "0", - "id": "1683664161036" - }, - { - "index": "1", - "id": "1699963668903" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "cmd_to_dido", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "cmd_to_dido" - } - }, - { - "id": "1618393827655", - "component": "virtualwireout", - "tab": "1615551125555", - "name": "cmd_to_dido", - "x": 752.8833312988281, - "y": 527, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "cmd_to_dido", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "cmd_to_dido" - } - }, - { - "id": "1618558465485", - "component": "virtualwireout", - "tab": "1612772287426", - "name": "platform-rpc-call", - "x": 597.8833312988281, - "y": 247, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "platform-rpc-call", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "platform-rpc-call" - } - }, - { - "id": "1618572059773", - "component": "trigger", - "tab": "1615551125555", - "name": "turnOn line", - "x": 132, - "y": 1085, - "connections": { - "0": [ - { - "index": "1", - "id": "1699963668903" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "datatype": "object", - "data": "{line: 1, command: \"on\", force: true}" - } - }, - { - "id": "1619515097737", - "component": "cmd_manager", - "tab": "1615551125555", - "name": "CMD Manager", - "x": 452.1091003417969, - "y": 341.05455017089844, - "connections": { - "0": [ - { - "index": "0", - "id": "1615563373927" - } - ], - "1": [ - { - "index": "0", - "id": "1615566865233" - }, - { - "index": "0", - "id": "1615798582262" - } - ], - "2": [ - { - "index": "0", - "id": "1616165824813" - } - ], - "3": [ - { - "index": "0", - "id": "1618393827655" - } - ], - "4": [ - { - "index": "0", - "id": "1635936391935" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#5D9CEC", - "notes": "", - "options": {} - }, - { - "id": "1619605019281", - "component": "httproute", - "tab": "1615551125555", - "name": "GET db", - "x": 173, - "y": 653, - "connections": { - "0": [ - { - "index": "0", - "id": "1684060205000" - }, - { - "index": "1", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Listening", - "color": "green" - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __GET /db__\n- flags: undefined\n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, - "options": { - "timeout": 5, - "cachepolicy": 0, - "cacheexpire": "5 minutes", - "size": 5, - "url": "/db", - "method": "GET", - "name": "", - "flags": [ - 5000, - "id:1619605019281", - "get" - ] - } - }, - { - "id": "1619784672383", - "component": "trigger", - "tab": "1615551125555", - "name": "turnOnAlarm", - "x": 117, - "y": 1242, - "connections": { - "0": [ - { - "index": "1", - "id": "1699963668903" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "data": "{command: \"turnOnAlarm\"}", - "datatype": "object" - } - }, - { - "id": "1619784812964", - "component": "trigger", - "tab": "1615551125555", - "name": "turnOffAlarm", - "x": 118, - "y": 1307, - "connections": { - "0": [ - { - "index": "1", - "id": "1699963668903" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "data": "{command: \"turnOffAlarm\"}", - "datatype": "object" - } - }, - { - "id": "1621340721628", - "component": "virtualwireout", - "tab": "1611921777196", - "name": "modbus_to_dido", - "x": 599, - "y": 471, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "modbus_to_dido", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "modbus_to_dido" - } - }, - { - "id": "1622640022885", - "component": "httproute", - "tab": "1615551125555", - "name": "POST /db_connector", - "x": 98, - "y": 1586, - "connections": { - "0": [ - { - "index": "0", - "id": "1622640073521" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Listening", - "color": "green" - }, - "color": "#5D9CEC", - "notes": "### Configuration\n\n- __POST /db_connector__\n- flags: \n- maximum request data length: __5 kB__\n- empty response: __undefined__\n- cache policy: __no cache__\n- cache expire: __5 minutes__", - "cloning": false, - "options": { - "timeout": 5, - "cachepolicy": 0, - "cacheexpire": "5 minutes", - "size": 5, - "url": "/db_connector", - "method": "POST", - "flags": [ - 5000, - "id:1622640022885", - "post" - ] - } - }, - { - "id": "1622640073521", - "component": "db_connector", - "tab": "1615551125555", - "name": "DbConnector", - "x": 372, - "y": 1572, - "connections": { - "1": [ - { - "index": "0", - "id": "1622641420685" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#2134B0", - "notes": "", - "options": { - "edge": "undefined" - } - }, - { - "id": "1622641420685", - "component": "httpresponse", - "tab": "1615551125555", - "name": "HTTP Response", - "x": 596, - "y": 1586, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#5D9CEC", - "notes": "", - "options": {} - }, - { - "id": "1634303504177", - "component": "monitormemory", - "tab": "1612772287426", - "name": "RAM", - "x": 69.88333129882812, - "y": 888.5, - "connections": { - "0": [ - { - "index": "0", - "id": "1634465281992" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "704.30 MB / 982.12 MB", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "enabled": true, - "interval": 30000 - } - }, - { - "id": "1634303533779", - "component": "monitordisk", - "tab": "1612772287426", - "name": "disk", - "x": 70.88333129882812, - "y": 982.5, - "connections": { - "0": [ - { - "index": "0", - "id": "1634465821120" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "5.45 GB / 6.86 GB", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "enabled": true, - "path": "/", - "interval": 30000 - } - }, - { - "id": "1634303595494", - "component": "virtualwirein", - "tab": "1612772287426", - "name": "send-to-services", - "x": 51.883331298828125, - "y": 1400.5, - "connections": { - "0": [ - { - "index": "0", - "id": "1634463186563" - }, - { - "index": "1", - "id": "1634488120710" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "send-to-services", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "send-to-services" - } - }, - { - "id": "1634303602169", - "component": "virtualwireout", - "tab": "1612772287426", - "name": "send-to-services", - "x": 426.8833312988281, - "y": 878.5, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "send-to-services", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "send-to-services" - } - }, - { - "id": "1634303685503", - "component": "virtualwireout", - "tab": "1612772287426", - "name": "send-to-services", - "x": 600.8833312988281, - "y": 341.5, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "send-to-services", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "send-to-services" - } - }, - { - "id": "1634303743260", - "component": "httprequest", - "tab": "1612772287426", - "name": "192.168.252.2:8004/sentmessage", - "reference": "", - "x": 506.8833312988281, - "y": 1331.7333374023438, - "connections": { - "0": [ - { - "index": "0", - "id": "1635327431236" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#5D9CEC", - "notes": "", - "options": { - "stringify": "json", - "method": "POST", - "url": "http://192.168.252.2:8004/sentmessage" - } - }, - { - "id": "1634463186563", - "component": "debug", - "tab": "1612772287426", - "name": "Debug", - "x": 305.75, - "y": 1442, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1634464580289", - "component": "code", - "tab": "1612772287426", - "name": "Code", - "x": 245, - "y": 787, - "connections": { - "0": [ - { - "index": "0", - "id": "1634465243324" - }, - { - "index": "0", - "id": "1634303602169" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#656D78", - "notes": "", - "options": { - "keepmessage": true, - "code": "let response = {};\nresponse.cpu = value.cpu;\nresponse.uptime = value.uptime;\n\nsend(0, response);", - "outputs": 1 - } - }, - { - "id": "1634465243324", - "component": "debug", - "tab": "1612772287426", - "name": "Debug", - "x": 428, - "y": 784, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1634465281992", - "component": "code", - "tab": "1612772287426", - "name": "Code", - "x": 245, - "y": 884, - "connections": { - "0": [ - { - "index": "0", - "id": "1634465338103" - }, - { - "index": "0", - "id": "1634303602169" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#656D78", - "notes": "", - "options": { - "keepmessage": true, - "code": "value.sender = \"ram\";\n\nlet response = {};\n\nresponse.memory_total = Math.round(value.total / (1024 ** 2));\nresponse.memory_free = Math.round(value.free / (1024 ** 2));\nresponse.memory_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", - "outputs": 1 - } - }, - { - "id": "1634465338103", - "component": "debug", - "tab": "1612772287426", - "name": "Debug", - "x": 429, - "y": 976, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1634465821120", - "component": "code", - "tab": "1612772287426", - "name": "Code", - "x": 245, - "y": 978, - "connections": { - "0": [ - { - "index": "0", - "id": "1634465892500" - }, - { - "index": "0", - "id": "1634303602169" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#656D78", - "notes": "", - "options": { - "keepmessage": true, - "code": "value.sender = \"hdd\";\n\nlet response = {};\n\nresponse.hdd_total = Math.round(value.total / (1024 ** 2));\nresponse.hdd_free = Math.round(value.free / (1024 ** 2));\nresponse.hdd_used = Math.round(value.used / (1024 ** 2));\n\nsend(0, response);", - "outputs": 1 - } - }, - { - "id": "1634465892500", - "component": "debug", - "tab": "1612772287426", - "name": "Debug", - "x": 432, - "y": 1068, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1634484067516", - "component": "debug", - "tab": "1612772287426", - "name": "Send info", - "x": 513, - "y": 1441, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1634488120710", - "component": "infosender", - "tab": "1612772287426", - "name": "Info sender", - "x": 301, - "y": 1336, - "connections": { - "0": [ - { - "index": "0", - "id": "1634484067516" - }, - { - "index": "0", - "id": "1634303743260" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#2134B0", - "notes": "", - "options": { - "edge": "undefined" - } - }, - { - "id": "1635327431236", - "component": "debug", - "tab": "1612772287426", - "name": "Debug", - "x": 837.8833312988281, - "y": 1325.5, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1635936391935", - "component": "virtualwireout", - "tab": "1615551125555", - "name": "send-to-services", - "x": 753, - "y": 623, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "send-to-services", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "send-to-services" - } - }, - { - "id": "1637069803394", - "component": "monitorconsumption", - "tab": "1612772287426", - "name": "CPU", - "x": 69, - "y": 791, - "connections": { - "0": [ - { - "index": "0", - "id": "1634464580289" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "3.6% / 111.50 MB", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "monitorfiles": true, - "monitorconnections": true, - "monitorsize": true, - "monitorconsumption": true, - "enabled": true, - "interval": 30000 - } - }, - { - "id": "1683664161036", - "component": "debug", - "tab": "1615551125555", - "name": "CMDtoDIDO", - "x": 392, - "y": 1012, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1683981346282", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "from-dido-controller", - "x": 112, - "y": 459, - "connections": { - "0": [ - { - "index": "0", - "id": "1684055037116" - }, - { - "index": "1", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "from-dido-controller", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "from-dido-controller" - } - }, - { - "id": "1684055037116", - "component": "debug", - "tab": "1615551125555", - "name": "from dido to cmd", - "x": 451, - "y": 532, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1684060205000", - "component": "debug", - "tab": "1615551125555", - "name": "HTTP routes", - "x": 450, - "y": 639, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1684179110403", - "component": "debug", - "tab": "1611921777196", - "name": "MDBToDido", - "x": 598, - "y": 147, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1699963668903", - "component": "dido_controller", - "tab": "1615551125555", - "name": "DIDO_Controller", - "x": 397, - "y": 1131, - "connections": { - "0": [ - { - "index": "0", - "id": "1617104731852" - } - ], - "1": [ - { - "index": "0", - "id": "1617104731852" - }, - { - "index": "0", - "id": "1617115013095" - } - ], - "2": [ - { - "index": "0", - "id": "1618393583970" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#2134B0", - "notes": "", - "options": { - "edge": "undefined" - } - }, - { - "id": "1699964678894", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "modbus_to_dido", - "x": 96, - "y": 924, - "connections": { - "0": [ - { - "index": "0", - "id": "1699963668903" - }, - { - "index": "0", - "id": "1699964793925" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "modbus_to_dido", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "modbus_to_dido" - } - }, - { - "id": "1699964793925", - "component": "debug", - "tab": "1615551125555", - "name": "modbusToDido", - "x": 388, - "y": 920, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1699965957410", - "component": "modbus_reader", - "tab": "1611921777196", - "name": "Modbus reader", - "x": 232, - "y": 175, - "connections": { - "0": [ - { - "index": "0", - "id": "1611951142547" - } - ], - "1": [ - { - "index": "0", - "id": "1621340721628" - }, - { - "index": "0", - "id": "1684179110403" - }, - { - "index": "0", - "id": "1717441414646" - } - ], - "2": [ - { - "index": "0", - "id": "1615809595184" - }, - { - "index": "0", - "id": "1714752862828" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#2134B0", - "notes": "", - "options": {} - }, - { - "id": "1700411878636", - "component": "thermometer", - "tab": "1611921777196", - "name": "Thermometer", - "x": 234.75, - "y": 444, - "connections": { - "0": [ - { - "index": "0", - "id": "1615802995322" - } - ], - "1": [ - { - "index": "0", - "id": "1615809595184" - }, - { - "index": "0", - "id": "1615809128443" - } - ], - "2": [ - { - "index": "0", - "id": "1621340721628" - }, - { - "index": "0", - "id": "1732889185927" - }, - { - "index": "0", - "id": "1717441414646" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#5CB36D", - "notes": "", - "options": {} - }, - { - "id": "1714752862828", - "component": "debug", - "tab": "1611921777196", - "name": "MDBToTb", - "x": 766, - "y": 324, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1717441414646", - "component": "code", - "tab": "1611921777196", - "name": "device-status", - "x": 764.0833282470703, - "y": 222, - "connections": { - "0": [ - { - "index": "0", - "id": "1717442627834" - }, - { - "index": "0", - "id": "1717442631338" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#656D78", - "notes": "", - "options": { - "keepmessage": true, - "code": "if(value.hasOwnProperty(\"status\"))\n{\n\tif(value.status.includes(\"-em\"))\n\t{\n\t\tsend(0, {\"em_status\": \"NOK\"});\n\t}\n\telse if(value.status.includes(\"twilight\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"NOK\"});\n\t}\n\telse if(value.status === \"NOK-thermometer\")\n\t{\n\t\tsend(0, {\"thermometer\": \"NOK\"});\n\t}\n}\n\nif(value.hasOwnProperty(\"values\"))\n{\n\tif(value.values.hasOwnProperty(\"twilight_sensor\"))\n\t{\n\t\tsend(0, {\"lux_sensor\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"Phase_1_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_voltage\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Total_power\") ||\n\t\t\tvalue.values.hasOwnProperty(\"Phase_1_current\"))\n\t{\n\t\tsend(0, {\"em_status\": \"OK\"});\n\t}\n\telse if(value.values.hasOwnProperty(\"temperature\"))\n\t{\n\t\tsend(0, {\"thermometer\": \"OK\"});\n\t}\n}", - "outputs": 1 - } - }, - { - "id": "1717442627834", - "component": "debug", - "tab": "1611921777196", - "name": "modbus service", - "x": 966.0833282470703, - "y": 165, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1717442631338", - "component": "virtualwireout", - "tab": "1611921777196", - "name": "send-to-services", - "x": 968.0833282470703, - "y": 268, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "send-to-services", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "send-to-services" - } - }, - { - "id": "1718016045116", - "component": "virtualwirein", - "tab": "1612772287426", - "name": "tb-push", - "x": 77.75, - "y": 1630, - "connections": { - "0": [ - { - "index": "0", - "id": "1718016052341" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "tb-push", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "tb-push" - } - }, - { - "id": "1718016052341", - "component": "slack_filter", - "tab": "1612772287426", - "name": "Slack Filter", - "x": 296, - "y": 1671, - "connections": { - "0": [ - { - "index": "0", - "id": "1718016086212" - }, - { - "index": "0", - "id": "1718016073501" - } - ] - }, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Running", - "color": "gray" - }, - "color": "#30E193", - "notes": "", - "options": { - "slack_channel": "C071KN2Q8SK", - "tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]", - "message_includes": "[\"is responding again\", \"Flow has been restarted\", \"Node db has changed\"]", - "types": "[\"emergency\", \"critical\", \"error\", \"alert\"]", - "name": "test_rvo_debian12" - } - }, - { - "id": "1718016073501", - "component": "httprequest", - "tab": "1612772287426", - "name": "http://192.168.252.2:8004/slack", - "x": 495, - "y": 1753, - "connections": { - "0": [ - { - "index": "0", - "id": "1718016086212" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#5D9CEC", - "notes": "", - "options": { - "stringify": "json", - "method": "POST", - "url": "http://192.168.252.2:8004/slack" - } - }, - { - "id": "1718016086212", - "component": "debug", - "tab": "1612772287426", - "name": "Debug", - "x": 832, - "y": 1664, - "connections": {}, - "disabledio": { - "input": [ - 0 - ], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1718016094070", - "component": "trigger", - "tab": "1612772287426", - "name": "Trigger", - "x": 79, - "y": 1723, - "connections": { - "0": [ - { - "index": "0", - "id": "1718016052341" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": { - "datatype": "object", - "data": "{ \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\": [ { \"ts\": 1716289039281, \"values\": { \"_event\": { \"type\": \"alert\", \"status\": \"new\", \"source\": { \"func\": \"CMD Manager: process cmd\", \"component\": \"1619515097737\", \"component_name\": \"CMD Manager\", \"edge\": \"g9OxBZ5KRwNznlY6pAppqEAWXvjdEL4eGQobMDy2\" }, \"message\": \"NOW CONNECTED TO SLACK !\", \"message_data\": \"\" } } } ] }" - } - }, - { - "id": "1731068658334", - "component": "virtualwirein", - "tab": "1612772287426", - "name": "db-init", - "x": 79.75, - "y": 164, - "connections": { - "0": [ - { - "index": "0", - "id": "1612776786008" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731069001548", - "component": "db_init", - "tab": "1612772287426", - "name": "DB Initialization", - "x": 1003.75, - "y": 240.25, - "connections": { - "0": [ - { - "index": "0", - "id": "1731069033416" - } - ], - "1": [ - { - "index": "0", - "id": "1747561603739" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#888600", - "notes": "", - "options": {} - }, - { - "id": "1731069033416", - "component": "virtualwireout", - "tab": "1612772287426", - "name": "db-init", - "x": 1244.75, - "y": 233.25, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731069059135", - "component": "showdb", - "tab": "1612772287426", - "name": "Show db data", - "x": 1121.75, - "y": 814.25, - "connections": { - "0": [ - { - "index": "0", - "id": "1731069079243" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#888600", - "notes": "", - "options": {} - }, - { - "id": "1731069079243", - "component": "debug", - "tab": "1612772287426", - "name": "dbData", - "x": 1324.75, - "y": 863.25, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1731069116691", - "component": "trigger", - "tab": "1612772287426", - "name": "settings", - "x": 867.75, - "y": 667.75, - "connections": { - "0": [ - { - "index": "0", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1731069131637", - "component": "trigger", - "tab": "1612772287426", - "name": "relaysData", - "x": 798.75, - "y": 733.75, - "connections": { - "0": [ - { - "index": "1", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1731069137374", - "component": "trigger", - "tab": "1612772287426", - "name": "nodesData", - "x": 762.75, - "y": 801.75, - "connections": { - "0": [ - { - "index": "2", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1731069179846", - "component": "trigger", - "tab": "1612772287426", - "name": "pinsData", - "x": 782.75, - "y": 867.75, - "connections": { - "0": [ - { - "index": "3", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1731069192937", - "component": "trigger", - "tab": "1612772287426", - "name": "sample data", - "x": 801.75, - "y": 933.75, - "connections": { - "0": [ - { - "index": "4", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1731069264443", - "component": "virtualwirein", - "tab": "1612772287426", - "name": "db-init", - "x": 63.75, - "y": 1279, - "connections": { - "0": [ - { - "index": "0", - "id": "1634488120710" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731069334626", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "db-init", - "x": 172.88333129882812, - "y": 129, - "connections": { - "0": [ - { - "index": "0", - "id": "1619515097737" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731069548145", - "component": "virtualwirein", - "tab": "1611921777196", - "name": "db-init", - "x": 46.75, - "y": 192, - "connections": { - "0": [ - { - "index": "0", - "id": "1699965957410" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731069567152", - "component": "virtualwirein", - "tab": "1611921777196", - "name": "db-init", - "x": 44.75, - "y": 465, - "connections": { - "0": [ - { - "index": "0", - "id": "1700411878636" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731070156936", - "component": "virtualwirein", - "tab": "1615551125555", - "name": "db-init", - "x": 126.88333129882812, - "y": 1377, - "connections": { - "0": [ - { - "index": "2", - "id": "1699963668903" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1731234189516", - "component": "trigger", - "tab": "1612772287426", - "name": "monitor.txt", - "x": 821.75, - "y": 1000.75, - "connections": { - "0": [ - { - "index": "5", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1731234189551", - "component": "trigger", - "tab": "1612772287426", - "name": "err.txt", - "x": 862.75, - "y": 1064.75, - "connections": { - "0": [ - { - "index": "6", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - }, - { - "id": "1732700042559", - "component": "nodesdb_change_check", - "tab": "1612772287426", - "name": "Nodes DB change check", - "x": 263.8833312988281, - "y": 1993.2333984375, - "connections": { - "0": [ - { - "index": "0", - "id": "1732700071298" - }, - { - "index": "0", - "id": "1732700642917" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#888600", - "notes": "", - "options": {} - }, - { - "id": "1732700057052", - "component": "virtualwirein", - "tab": "1612772287426", - "name": "db-init", - "x": 84.75, - "y": 1994, - "connections": { - "0": [ - { - "index": "0", - "id": "1732700042559" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "db-init", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "db-init" - } - }, - { - "id": "1732700071298", - "component": "debug", - "tab": "1612772287426", - "name": "nodesChange", - "x": 561.8833312988281, - "y": 2055.2333984375, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1732700642917", - "component": "virtualwireout", - "tab": "1612772287426", - "name": "tb-push", - "x": 557.8833312988281, - "y": 1949, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "tb-push", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "tb-push" - } - }, - { - "id": "1732889185927", - "component": "debug", - "tab": "1611921777196", - "name": "tempToDido", - "x": 594.8833312988281, - "y": 753, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "Enabled", - "color": "gray" - }, - "color": "#967ADC", - "notes": "", - "options": { - "type": "data", - "repository": false, - "enabled": true - } - }, - { - "id": "1747561603739", - "component": "virtualwireout", - "tab": "1612772287426", - "name": "send-to-services", - "x": 1243.8833312988281, - "y": 334.5, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "send-to-services", - "color": "gray" - }, - "color": "#303E4D", - "notes": "", - "options": { - "wirename": "send-to-services" - } - }, - { - "id": "1747562867845", - "component": "comment", - "tab": "1612772287426", - "name": "FLOW STARTING POINT", - "x": 1003.5666656494141, - "y": 178, - "connections": {}, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#704cff", - "notes": "", - "options": {} - }, - { - "id": "1750771612786", - "component": "trigger", - "tab": "1612772287426", - "name": "devices", - "x": 896.75, - "y": 1122.75, - "connections": { - "0": [ - { - "index": "7", - "id": "1731069059135" - } - ] - }, - "disabledio": { - "input": [], - "output": [] - }, - "state": { - "text": "", - "color": "gray" - }, - "color": "#F6BB42", - "notes": "", - "options": {} - } - ], - "version": 624 -} \ No newline at end of file diff --git a/flow/dido_controller.js b/flow/dido_controller.js index 8843fe0..d16fb19 100644 --- a/flow/dido_controller.js +++ b/flow/dido_controller.js @@ -364,7 +364,7 @@ exports.install = function(instance) { data.map(item => { let value = item['value']; - let pin = item["dev"] + item["circuit"]; // for example "ro1_03" or "di1_01" + let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01" if (pin == undefined) return; switchLogic(pin, value); @@ -516,9 +516,9 @@ exports.install = function(instance) { } else if (ws) { - //pin = "ro1_03" or "di1_01" ... we must make just "1_01" with slice method + //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method monitor.info(`Dido: turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info); - let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(2), "value": value }; + let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value }; ws.send(JSON.stringify(cmd)); } @@ -754,9 +754,9 @@ exports.install = function(instance) { pins = [4, 6]; } } else if (controllerType === "unipi") { - pins = ["di1_01", "di1_04", "di1_05"]; + pins = ["input1_01", "input1_04", "input1_05"]; if (hasMainSwitch === 1) { - pins = ["di1_01", "di1_04"]; + pins = ["input1_01", "input1_04"]; } } @@ -781,7 +781,7 @@ exports.install = function(instance) { for (const pinIndex of pinIndexes) { if (previousValues[pinIndex] === 0) { - if ((pinIndex === 6 || pinIndex === 'di1_01' || pinIndex === 'di1_05') && SETTINGS.maintenance_mode) continue; + if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && SETTINGS.maintenance_mode) continue; status = "NOK"; break; } @@ -798,7 +798,7 @@ exports.install = function(instance) { // we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]] - // we pass two values in case of websocket ==> switchLogic("ro1_03",1) ==> ["ro1_03",1] + // we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1] const switchLogic = (...args) => { let values = {}; @@ -849,18 +849,18 @@ exports.install = function(instance) { else if (type == "rotary_switch_state") { // combination of these two pins required to get result let pin2, pin3; - if (pinIndex == 2 || pinIndex == "di1_02") { + if (pinIndex == 2 || pinIndex == "input1_02") { pin2 = newPinValue; - pin3 = previousValues[3] || previousValues["di1_03"]; + pin3 = previousValues[3] || previousValues["input1_03"]; if (pin3 == undefined) { previousValues[pinIndex] = newPinValue; return; } } - else if (pinIndex == 3 || pinIndex == "di1_03") { + else if (pinIndex == 3 || pinIndex == "input1_03") { pin3 = newPinValue; - pin2 = previousValues[2] || previousValues["di1_02"]; + pin2 = previousValues[2] || previousValues["input1_02"]; if (pin2 == undefined) { previousValues[pinIndex] = newPinValue; @@ -913,7 +913,7 @@ exports.install = function(instance) { } //Dverovy kontakt - pin 6 - //! Ak je rvo s dvoma dverovymi kontaktami, ked pride z evoku signal z di1_05, co bol predytm "state_of_main switch" handlujeme ho teraz ako 'door_condition' + //! Ak je rvo s dvoma dverovymi kontaktami, ked pride z evoku signal z input1_05, co bol predytm "state_of_main switch" handlujeme ho teraz ako 'door_condition' else if (type == "door_condition" || type === "state_of_main_switch") { newPinValue === 0 ? value = "open" : value = "closed"; @@ -1369,60 +1369,60 @@ exports.install = function(instance) { //! pins.table --> from UNIPI // pin:string|type:string|line:number -// *|di1_01|state_of_main_switch|0|........... -// *|di1_02|rotary_switch_state|0|........... -// *|di1_03|rotary_switch_state|0|........... +// *|input1_01|state_of_main_switch|0|........... +// *|input1_02|rotary_switch_state|0|........... +// *|input1_03|rotary_switch_state|0|........... // *|intut1_04|power_supply|0|........... -// *|di1_05|door_condition|0|........... -// *|di1_06|state_of_breaker|1|........... -// *|di1_07|state_of_breaker|2|........... -// *|di1_08|state_of_breaker|3|........... -// *|ro1_02|state_of_contactor|1|........... -// *|ro1_03|state_of_contactor|2|........... -// *|ro1_04|state_of_contactor|3|........... +// *|input1_05|door_condition|0|........... +// *|input1_06|state_of_breaker|1|........... +// *|input1_07|state_of_breaker|2|........... +// *|input1_08|state_of_breaker|3|........... +// *|relay1_02|state_of_contactor|1|........... +// *|relay1_03|state_of_contactor|2|........... +// *|relay1_04|state_of_contactor|3|........... // *|287D8776E0013CE9|temperature|0|........... //! pins_data --> from UNIPI // { -// di1_01: { -// pin: 'di1_01', +// input1_01: { +// pin: 'input1_01', // type: 'door_condition', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// di1_02: { -// pin: 'di1_02', +// input1_02: { +// pin: 'input1_02', // type: 'rotary_switch_state', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// di1_03: { -// pin: 'di1_03', +// input1_03: { +// pin: 'input1_03', // type: 'rotary_switch_state', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// di1_04: { -// pin: 'di1_04', +// input1_04: { +// pin: 'input1_04', // type: 'power_supply', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// di1_05: { -// pin: 'di1_05', +// input1_05: { +// pin: 'input1_05', // type: 'state_of_main_switch', // line: 0, // tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8' // }, -// di1_06: { -// pin: 'di1_06', +// input1_06: { +// pin: 'input1_06', // type: 'state_of_breaker', // line: 1, // tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo' // }, -// ro1_02: { -// pin: 'ro1_02', +// relay1_02: { +// pin: 'relay1_02', // type: 'state_of_contactor', // line: 1, // tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo' diff --git a/flow/helper/DataToTbHandler.js b/flow/helper/DataToTbHandler.js index ef6942a..716ef7b 100644 --- a/flow/helper/DataToTbHandler.js +++ b/flow/helper/DataToTbHandler.js @@ -1,186 +1,186 @@ class DataToTbHandler { - constructor(index) { - this.index = index; + constructor(index) { + this.index = index; - // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) - this.timeToHoldTbValue = 30 * 60; //30 minutes - this.previousValues = {}; - this.debug = false; - this.messageCounter = 0; - this.itIsNodeReadout = false; - this.sender = ""; + // time, after new value for the given key will be resend to tb (e.g. {status: "OK"}) + this.timeToHoldTbValue = 30 * 60; //30 minutes + this.previousValues = {}; + this.debug = false; + this.messageCounter = 0; + this.itIsNodeReadout = false; + this.sender = ""; - // if attribute change difference is less than limit value, we do not send to tb. - this.attributeChangeLimit = { - temperature: 0.5, - Phase_1_voltage: 2, - Phase_2_voltage: 2, - Phase_3_voltage: 2, - Phase_1_current: 0.1, - Phase_2_current: 0.1, - Phase_3_current: 0.1, - Phase_1_power: 2, - Phase_2_power: 2, - Phase_3_power: 2, - total_power: 2, - total_energy: 1, - Phase_1_pow_factor: 0.1, - Phase_2_pow_factor: 0.1, - Phase_3_pow_factor: 0.1, - power_factor: 0.1, - lifetime: 2, - voltage: 2, - power: 2, - frequency: 3, - energy: 0.1, - current: 2, - inclination_x: 10, - inclination_y: 10, - inclination_z: 10 - }; + // if attribute change difference is less than limit value, we do not send to tb. + this.attributeChangeLimit = { + temperature: 0.5, + Phase_1_voltage: 2, + Phase_2_voltage: 2, + Phase_3_voltage: 2, + Phase_1_current: 0.1, + Phase_2_current: 0.1, + Phase_3_current: 0.1, + Phase_1_power: 2, + Phase_2_power: 2, + Phase_3_power: 2, + total_power: 2, + total_energy: 1, + Phase_1_pow_factor: 0.1, + Phase_2_pow_factor: 0.1, + Phase_3_pow_factor: 0.1, + power_factor: 0.1, + lifetime: 2, + voltage: 2, + power: 2, + frequency: 3, + energy: 0.1, + current: 2, + inclination_x: 10, + inclination_y: 10, + inclination_z: 10 + }; - } + } - dump() { - console.log("----------------------------"); - console.log("previousValues", this.previousValues); - console.log("----------------------------"); - } + dump() { + console.log("----------------------------"); + console.log("previousValues", this.previousValues); + console.log("----------------------------"); + } - setSender(sender) { - this.sender = sender; - } + setSender(sender) { + this.sender = sender; + } - isEmptyObject(obj) { - for (var _ in obj) { - return false; - } - return true; - } + isEmptyObject(obj) { + for (var _ in obj) { + return false; + } + return true; + } - sendToTb(data, instance) { + sendToTb(data, instance) { - //not to modify data object, we do deep copy: - let dataCopy = JSON.parse(JSON.stringify(data)); + //not to modify data object, we do deep copy: + let dataCopy = JSON.parse(JSON.stringify(data)); - let keys = Object.keys(dataCopy); + let keys = Object.keys(dataCopy); - if (keys.length == 0) { - if (this.debug) console.log("sendToTb received empty object", dataCopy); - return; - } + if (keys.length == 0) { + if (this.debug) console.log("sendToTb received empty object", dataCopy); + return; + } - let tbname = keys[0]; - let ts; + let tbname = keys[0]; + let ts; - let arrayOfValues = dataCopy[tbname]; - let arrayOfValuesToSend = []; + let arrayOfValues = dataCopy[tbname]; + let arrayOfValuesToSend = []; - for (let i = 0; i < arrayOfValues.length; i++) { + for (let i = 0; i < arrayOfValues.length; i++) { - ts = arrayOfValues[i].ts; - let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); + ts = arrayOfValues[i].ts; + let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values); - if (!this.isEmptyObject(values)) { - arrayOfValuesToSend.push({ ts: ts, values: values }); - } + if (!this.isEmptyObject(values)) { + arrayOfValuesToSend.push({ ts: ts, values: values }); + } - } + } - if (arrayOfValuesToSend.length == 0) { - //if(this.debug) console.log("data not sent - empty array"); - return; - } + if (arrayOfValuesToSend.length == 0) { + //if(this.debug) console.log("data not sent - empty array"); + return; + } - this.messageCounter++; + this.messageCounter++; - let dataToTbModified = { - [tbname]: arrayOfValuesToSend - } + let dataToTbModified = { + [tbname]: arrayOfValuesToSend + } - //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); - //if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); - instance.send(this.index, dataToTbModified); - } + //console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance); + //if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend); + instance.send(this.index, dataToTbModified); + } - getDiffTimestamp(key) { - //TODO set different value for given key!!! - //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h - return this.timeToHoldTbValue * 1000; - } + getDiffTimestamp(key) { + //TODO set different value for given key!!! + //if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h + return this.timeToHoldTbValue * 1000; + } - prepareValuesForTb(tbname, timestamp, values) { + prepareValuesForTb(tbname, timestamp, values) { - let keys = Object.keys(values); + let keys = Object.keys(values); - if (keys.includes("lifetime")) this.itIsNodeReadout = true; + if (keys.includes("lifetime")) this.itIsNodeReadout = true; - if (!this.previousValues.hasOwnProperty(tbname)) { - this.previousValues[tbname] = {}; - } + if (!this.previousValues.hasOwnProperty(tbname)) { + this.previousValues[tbname] = {}; + } - //if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values); + //if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values); - for (let i = 0; i < keys.length; i++) { + for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let value = values[key]; + let key = keys[i]; + let value = values[key]; - if (!this.previousValues[tbname].hasOwnProperty(key)) { - this.previousValues[tbname][key] = { ts: timestamp, value: value }; - continue; - } + if (!this.previousValues[tbname].hasOwnProperty(key)) { + this.previousValues[tbname][key] = { ts: timestamp, value: value }; + continue; + } - // attributeData ==> {voltage: {ts:333333, value:5}} - let attributeData = this.previousValues[tbname][key]; - let attributeToChange = false; - if (key in this.attributeChangeLimit) attributeToChange = true; - let limit = this.attributeChangeLimit[key]; - let timestampDiffToRemoveKey; + // attributeData ==> {voltage: {ts:333333, value:5}} + let attributeData = this.previousValues[tbname][key]; + let attributeToChange = false; + if (key in this.attributeChangeLimit) attributeToChange = true; + let limit = this.attributeChangeLimit[key]; + let timestampDiffToRemoveKey; - //this will ensure "node statecode" will be sent just once an hour - if (this.itIsNodeReadout && key === "statecode") { - attributeData.value = value; - this.itIsNodeReadout = false; - timestampDiffToRemoveKey = 1 * 60 * 60 * 1000; // 1 hour - } + //this will ensure "node statecode" will be sent just once an hour + if (this.itIsNodeReadout && key === "statecode") { + attributeData.value = value; + this.itIsNodeReadout = false; + timestampDiffToRemoveKey = 1 * 60 * 60 * 1000; // 1 hour + } - if (key === "twilight_sensor" && value > 100) { - attributeData.value = value; - } + if (key === "twilight_sensor" && value > 100) { + attributeData.value = value; + } - //if edge, master or node version do not change, send just once a day: - if (["edge_fw_version", "master_node_version", "fw_version"].includes(key)) { - timestampDiffToRemoveKey = 24 * 60 * 60 * 1000; - } + //if edge, master or node version do not change, send just once a day: + if (["edge_fw_version", "master_node_version", "fw_version"].includes(key)) { + timestampDiffToRemoveKey = 24 * 60 * 60 * 1000; + } - if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { + if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) { - let diff = timestamp - attributeData.ts; - if (!timestampDiffToRemoveKey) timestampDiffToRemoveKey = this.getDiffTimestamp(key); + let diff = timestamp - attributeData.ts; + if (!timestampDiffToRemoveKey) timestampDiffToRemoveKey = this.getDiffTimestamp(key); - if (diff > timestampDiffToRemoveKey) { - attributeData.ts = Date.now(); - //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); - } - else { - delete values[key]; - //if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey); - } - } - else { - attributeData.value = value; - attributeData.ts = timestamp; - } + if (diff > timestampDiffToRemoveKey) { + attributeData.ts = Date.now(); + //if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter); + } + else { + delete values[key]; + //if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey); + } + } + else { + attributeData.value = value; + attributeData.ts = timestamp; + } - } + } - return values; - } + return values; + } } module.exports = DataToTbHandler; diff --git a/flow/helper/utils.js b/flow/helper/utils.js index 57b0f89..9c054aa 100644 --- a/flow/helper/utils.js +++ b/flow/helper/utils.js @@ -1,170 +1,112 @@ -function bytesToInt_orig(bytes, numberOfBytes) { - - let buffer = []; - if (Array.isArray(bytes)) { - buffer = bytes.slice(0); - if (numberOfBytes != undefined) { - buffer = bytes.slice(bytes.length - numberOfBytes); - } - } - else buffer.push(bytes); - //var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; - - let l = (buffer.length - 1) * 8; - let decimal = 0; - for (let i = 0; i < buffer.length; i++) { - var s = buffer[i] << l; - if (l < 8) s = buffer[i] - decimal = decimal + s; - l = l - 8; - } - // console.log("decimal utils.js: ", decimal); - - let decimal1 = 0n; - for (let i = 0; i < buffer.length; i++) { - decimal1 += BigInt(buffer[i]) * (2n ** BigInt((buffer.length - 1 - i) * 8)); - } - // console.log("decimal biging utils.js: ", decimal1); - return decimal; -} - -//bytestouintBE -function bytesToInt(bytes, numberOfBytes) { - - let buffer = []; - if (Array.isArray(bytes)) { - buffer = bytes.slice(0); - if (numberOfBytes != undefined) { - buffer = bytes.slice(bytes.length - numberOfBytes); - } - } - else buffer.push(bytes); - - console.log(bytes, buffer); - - let result = 0; - for (let i = 0; i < buffer.length; i++) { - result = (result << 8) | bytes[i]; - } - // console.log("decimal biging utils.js: ", decimal1); - - console.log("originall: ", bytesToInt_orig(buffer)); - console.log("uint little endian: ", bytesToUintLE(buffer)); - console.log('neww: ', result >>> 0); - return result >>> 0; -} - -function bytesToUintLE(bytes, numberOfBytes) { - - let buffer = []; - if (Array.isArray(bytes)) { - buffer = bytes.slice(0); - if (numberOfBytes != undefined) { - buffer = bytes.slice(bytes.length - numberOfBytes); - } - } - else buffer.push(bytes); - //var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; - - let result = 0; - for (let i = buffer.length - 1; i <= 0; i--) { - result = (result << 8) | bytes[i]; - } - return result >>> 0; -} - - -function resizeArray(arr, newSize, defaultValue) { - while (newSize > arr.length) - arr.push(defaultValue); - arr.length = newSize; -} - -longToByteArray = function(/*long*/long) { - // we want to represent the input as a 8-bytes array - var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; - - for (var index = 0; index < byteArray.length; index++) { - var byte = long & 0xff; - byteArray[index] = byte; - long = (long - byte) / 256; - } - - return byteArray; -}; - -function addDays(date, days) { - var result = new Date(date); - result.setDate(result.getDate() + days); - return result; -} - -/* -sleep(2000).then(() => { - // Do something after the sleep! - - -}); -*/ - -function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - -function isEmptyObject(obj) { - for (var name in obj) { - return false; - } - return true; -} - -function convertUTCDateToLocalDate(date) { - var newDate = new Date(date); - newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset()); - return newDate; -} - -function addZeroBefore(n) { - return (n < 10 ? '0' : '') + n; -} - -var convertBase = function() { - - function convertBase(baseFrom, baseTo) { - return function(num) { - return parseInt(num, baseFrom).toString(baseTo); - - }; - } - - // binary to decimal - convertBase.bin2dec = convertBase(2, 10); - - // binary to hexadecimal - convertBase.bin2hex = convertBase(2, 16); - - // decimal to binary - convertBase.dec2bin = convertBase(10, 2); - - // decimal to hexadecimal - convertBase.dec2hex = convertBase(10, 16); - - // hexadecimal to binary - convertBase.hex2bin = convertBase(16, 2); - - // hexadecimal to decimal - convertBase.hex2dec = convertBase(16, 10); - - return convertBase; -}(); - -module.exports = { - bytesToInt, - longToByteArray, - addDays, - addZeroBefore, - resizeArray, - isEmptyObject, - sleep, - convertUTCDateToLocalDate -} +function bytesToInt(bytes, numberOfBytes) { + let buffer = []; + if (Array.isArray(bytes)) { + buffer = bytes.slice(0); + if (numberOfBytes != undefined) { + buffer = bytes.slice(bytes.length - numberOfBytes); + } + } + else buffer.push(bytes); + + let result = 0; + for (let i = 0; i < buffer.length; i++) { + result = (result << 8) | buffer[i]; + } + + return result >>> 0; //ensure it's an unsigned 32-bit number +} + +function resizeArray(arr, newSize, defaultValue) { + while (newSize > arr.length) + arr.push(defaultValue); + arr.length = newSize; +} + +longToByteArray = function(/*long*/long) { + // we want to represent the input as a 8-bytes array + var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (var index = 0; index < byteArray.length; index++) { + var byte = long & 0xff; + byteArray[index] = byte; + long = (long - byte) / 256; + } + + return byteArray; +}; + +function addDays(date, days) { + var result = new Date(date); + result.setDate(result.getDate() + days); + return result; +} + +/* +sleep(2000).then(() => { + // Do something after the sleep! + + +}); +*/ + +function sleep(time) { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +function isEmptyObject(obj) { + for (var name in obj) { + return false; + } + return true; +} + +function convertUTCDateToLocalDate(date) { + var newDate = new Date(date); + newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset()); + return newDate; +} + +function addZeroBefore(n) { + return (n < 10 ? '0' : '') + n; +} + +var convertBase = function() { + + function convertBase(baseFrom, baseTo) { + return function(num) { + return parseInt(num, baseFrom).toString(baseTo); + + }; + } + + // binary to decimal + convertBase.bin2dec = convertBase(2, 10); + + // binary to hexadecimal + convertBase.bin2hex = convertBase(2, 16); + + // decimal to binary + convertBase.dec2bin = convertBase(10, 2); + + // decimal to hexadecimal + convertBase.dec2hex = convertBase(10, 16); + + // hexadecimal to binary + convertBase.hex2bin = convertBase(16, 2); + + // hexadecimal to decimal + convertBase.hex2dec = convertBase(16, 10); + + return convertBase; +}(); + +module.exports = { + bytesToInt, + longToByteArray, + addDays, + addZeroBefore, + resizeArray, + isEmptyObject, + sleep, + convertUTCDateToLocalDate +} diff --git a/flow/modbus_reader.js b/flow/modbus_reader.js index 5852ff3..d6d9dad 100644 --- a/flow/modbus_reader.js +++ b/flow/modbus_reader.js @@ -16,7 +16,7 @@ exports.readme = ` `; const modbus = require('jsmodbus'); -const {SerialPort} = require('serialport'); +const SerialPort = require('serialport'); const { timeoutInterval, deviceConfig } = require("../databases/modbus_config"); const { sendNotification } = require('./helper/notification_reporter'); @@ -36,6 +36,7 @@ let mainSocket; let phases; //phases where voltage is 0 (set) let noVoltage; +let energyToSwitchLamps; exports.install = function(instance) { @@ -76,8 +77,13 @@ exports.install = function(instance) { let obj = this; - this.socket = new SerialPort({path: "/dev/ttymxc0", - baudRate: 9600 + if (this.socket) { + this.socket.removeAllListeners(); + this.socket = null; + } + + this.socket = new SerialPort("/dev/ttymxc0", { + baudRate: 9600, }) // we create a client for every deviceAddress ( = address) in list and push them into dictionary @@ -86,15 +92,11 @@ exports.install = function(instance) { } this.socket.on('error', function(e) { - console.log('socket connection error', e); - if (e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') { - console.log(exports.title + ' Waiting 10 seconds before trying to connect again'); - setTimeout(obj.startSocket, 10000); - } + console.log('Modbus_reader: Socket connection error', e); //'ECONNREFUSED' or 'ECONNRESET' ?? }); this.socket.on('close', function() { - console.log('Socket connection closed ' + exports.title + ' Waiting 10 seconds before trying to connect again'); + console.log('Modbus_reader: Socket connection closed - Waiting 10 seconds before connecting again'); setTimeout(obj.startSocket, 10000); }); @@ -115,7 +117,8 @@ exports.install = function(instance) { this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number this.device = dev.device; //em340, twilight_sensor - if (this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval); + //if we just start to loop devices from the beginning, or there is just 1 device in config, we wait whole timeoutInterval + if (this.indexInDeviceConfig == 0 || deviceConfig.length === 1) setTimeout(this.readRegisters, this.timeoutInterval); else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES); } @@ -304,15 +307,12 @@ exports.install = function(instance) { const actualTotalPower = values.total_power; - const numberOfNodes = Object.keys(FLOW.GLOBALS.nodesData).length; - if (numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300 - - if (actualTotalPower > numberOfNodes * 15 && this.onNotificationSent == false) { + if (actualTotalPower > energyToSwitchLamps && this.onNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance); this.onNotificationSent = true; this.offNotificationSent = false; } - else if (actualTotalPower <= numberOfNodes * 15 && this.offNotificationSent == false) { + else if (actualTotalPower <= energyToSwitchLamps && this.offNotificationSent == false) { sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance); this.onNotificationSent = false; this.offNotificationSent = true; @@ -330,9 +330,9 @@ exports.install = function(instance) { phases = FLOW.GLOBALS.settings.phases; tbName = FLOW.GLOBALS.settings.rvoTbName; noVoltage = FLOW.GLOBALS.settings.no_voltage; - mainSocket = new SocketWithClients(); - - console.log("novoltage: ", noVoltage, typeof noVoltage); + energyToSwitchLamps = FLOW.GLOBALS.settings.energy_to_switch_lamps / 2.5; //half value is enought to show if lamps are turned on or off + if (deviceConfig.length) mainSocket = new SocketWithClients(); + else console.log("Modbus_reader: no modbus device in configuration"); // this notification is to show, that flow (unipi) has been restarted sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance); diff --git a/flow/variables.txt b/flow/variables.txt deleted file mode 100644 index e69de29..0000000 diff --git a/flow/wsmqttpublish.js b/flow/wsmqttpublish.js index acc3c33..0eba804 100644 --- a/flow/wsmqttpublish.js +++ b/flow/wsmqttpublish.js @@ -44,9 +44,9 @@ const fs = require('fs'); const mqtt = require('mqtt'); const SEND_TO = { - debug: 0, - rpcCall: 1, - services: 2 + debug: 0, + rpcCall: 1, + services: 2 } //CONFIG @@ -72,13 +72,13 @@ let sendClientError = true; process.on('uncaughtException', function(err) { - errLogger.error('uncaughtException:', err.message) - errLogger.error(err.stack); + errLogger.error('uncaughtException:', err.message) + errLogger.error(err.stack); - //TODO - //send to service + //TODO + //send to service - //process.exit(1); + //process.exit(1); }) const nosql = NOSQL('tbdata'); @@ -87,364 +87,362 @@ const nosqlBackup = NOSQL('/backup/tbdata'); exports.install = function(instance) { - var client; - var opts; - var clientReady = false; - - // wsmqtt status for notification purposes on projects.worksys.io database - let wsmqttName = null; - let sendWsStatusVar = null; - let wsmqtt_status = 'disconnected'; - - function getWsmqttName(host) { - if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; - else if (host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; - else if (host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; - } - - function sendWsStatus() { - instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status }); - } - - - function main() { - if (!FLOW.dbLoaded) return; - - loadSettings(); - clearInterval(sendWsStatus); - sendWsStatusVar = setInterval(sendWsStatus, 180000); - } - - //set opts according to db settings - function loadSettings() { - - if (instance.options.host !== "") { - //override settings from database - var o = instance.options; - opts = { - host: o.host, - port: o.port, - clientId: o.clientid, - username: o.username, - rejectUnauthorized: false, - resubscribe: false - }; - - wsmqttName = getWsmqttName(o.host); - - console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); - } - else { - - const SETTINGS = FLOW.GLOBALS.settings; - backup_on_failure = SETTINGS.backup_on_failure; - saveTelemetryOnError = backup_on_failure; - - restore_from_backup = SETTINGS.restore_from_backup; - restore_backup_wait = SETTINGS.restore_backup_wait; - - let mqtt_host = SETTINGS.mqtt_host; - let mqtt_clientid = SETTINGS.mqtt_clientid; - let mqtt_username = SETTINGS.mqtt_username; - let mqtt_port = SETTINGS.mqtt_port; - - opts = { - host: mqtt_host, - port: mqtt_port, - keepalive: 10, - clientId: mqtt_clientid, - username: mqtt_username, - rejectUnauthorized: false, - resubscribe: false, - }; - - wsmqttName = getWsmqttName(mqtt_host); - } - - connectToTbServer(); - } - - function connectToTbServer() { - var url = "mqtt://" + opts.host + ":" + opts.port; - console.log("MQTT URL: ", url); - - client = mqtt.connect(url, opts); - - client.on('connect', function() { - instance.status("Connected", "green"); - //monitor.info("MQTT client connected"); - - sendClientError = true; - clientReady = true; - wsmqtt_status = 'connected'; - }); - - client.on('reconnect', function() { - instance.status("Reconnecting", "yellow"); - clientReady = false; - }); - - client.on('message', function(topic, message) { - // message is type of buffer - message = message.toString(); - if (message[0] === '{') { - - try { - message = JSON.parse(message); - if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { - client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); - instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); - } - - } catch (e) { - console.log('MQTT: Error parsing data', e); - } - } - - instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message }); - }); - - client.on('close', function() { - clientReady = false; - wsmqtt_status = 'disconnected'; - - instance.status("Disconnected", "red"); - instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); - }); - - client.on('error', function(err) { - instance.status("Err: " + err.code, "red"); - instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); - if (sendClientError) { - monitor.info('MQTT client error', err); - sendClientError = false; - } - clientReady = false; - wsmqtt_status = 'disconnected'; - }); - - } - - - instance.on("0", _ => { - main(); - }) + var client; + var opts; + var clientReady = false; + + // wsmqtt status for notification purposes on projects.worksys.io database + let wsmqttName = null; + let sendWsStatusVar = null; + let wsmqtt_status = 'disconnected'; + + function getWsmqttName(host) { + if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo'; + else if (host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01'; + else if (host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01'; + } + + function sendWsStatus() { + instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status }); + } + + + function main() { + if (!FLOW.dbLoaded) return; + + loadSettings(); + clearInterval(sendWsStatus); + sendWsStatusVar = setInterval(sendWsStatus, 180000); + } + + //set opts according to db settings + function loadSettings() { + + if (instance.options.host !== "") { + //override settings from database + var o = instance.options; + opts = { + host: o.host, + port: o.port, + clientId: o.clientid, + username: o.username, + rejectUnauthorized: false, + resubscribe: false + }; + + wsmqttName = getWsmqttName(o.host); + + console.log("wsmqttpublich -> loadSettings from instance.options", instance.options); + } + else { + + const SETTINGS = FLOW.GLOBALS.settings; + backup_on_failure = SETTINGS.backup_on_failure; + saveTelemetryOnError = backup_on_failure; + + restore_from_backup = SETTINGS.restore_from_backup; + restore_backup_wait = SETTINGS.restore_backup_wait; + + let mqtt_host = SETTINGS.mqtt_host; + let mqtt_clientid = SETTINGS.mqtt_clientid; + let mqtt_username = SETTINGS.mqtt_username; + let mqtt_port = SETTINGS.mqtt_port; + + opts = { + host: mqtt_host, + port: mqtt_port, + keepalive: 10, + clientId: mqtt_clientid, + username: mqtt_username, + rejectUnauthorized: false, + resubscribe: false + }; + + wsmqttName = getWsmqttName(mqtt_host); + } + + connectToTbServer(); + } + + function connectToTbServer() { + var url = "mqtt://" + opts.host + ":" + opts.port; + console.log("MQTT URL: ", url); + + client = mqtt.connect(url, opts); + + client.on('connect', function() { + instance.status("Connected", "green"); + //monitor.info("MQTT client connected"); + + sendClientError = true; + clientReady = true; + wsmqtt_status = 'connected'; + }); + + client.on('reconnect', function() { + instance.status("Reconnecting", "yellow"); + clientReady = false; + }); + + client.on('message', function(topic, message) { + // message is type of buffer + message = message.toString(); + if (message[0] === '{') { + TRY(function() { + + message = JSON.parse(message); + if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) { + client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 }); + instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } }); + } + + }, () => instance.debug('MQTT: Error parsing data', message)); + } + + instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message }); + }); + + client.on('close', function() { + clientReady = false; + wsmqtt_status = 'disconnected'; + + instance.status("Disconnected", "red"); + instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" }); + }); + + client.on('error', function(err) { + instance.status("Err: " + err.code, "red"); + instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts }); + if (sendClientError) { + monitor.info('MQTT client error', err); + sendClientError = false; + } + clientReady = false; + wsmqtt_status = 'disconnected'; + }); + + } + + + instance.on("0", _ => { + main(); + }) - instance.on('1', function(data) { + instance.on('1', function(data) { - if (clientReady) { - //do we have some data in backup file? if any, process data from database - if (saveTelemetryOnError) { - //read telemetry data and send back to server - if (!processingData) processDataFromDatabase(); - } + if (clientReady) { + //do we have some data in backup file? if any, process data from database + if (saveTelemetryOnError) { + //read telemetry data and send back to server + if (!processingData) processDataFromDatabase(); + } - let stringifiedJson = JSON.stringify(data.data); - client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 }); + let stringifiedJson = JSON.stringify(data.data); + client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 }); - //backup telemetry - if (createTelemetryBackup) { - data.data.id = UID(); - nosqlBackup.insert(data.data); + //backup telemetry + if (createTelemetryBackup) { + data.data.id = UID(); + nosqlBackup.insert(data.data); - insertBackupNoSqlCounter++; - if (insertBackupNoSqlCounter > 150) { - let options = { compress: true }; - let path = __dirname + "/../databases/backup/tbdata.nosql"; - var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options); - stream.write(""); - stream.end(); + insertBackupNoSqlCounter++; + if (insertBackupNoSqlCounter > 150) { + let options = { compress: true }; + let path = __dirname + "/../databases/backup/tbdata.nosql"; + var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options); + stream.write(""); + stream.end(); - insertBackupNoSqlCounter = 0; - } - } + insertBackupNoSqlCounter = 0; + } + } - } - else { - //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); - instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); + } + else { + //logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data)); + instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data }); - if (saveTelemetryOnError) { - //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql - makeBackupFromDbFile(); + if (saveTelemetryOnError) { + //create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql + makeBackupFromDbFile(); - //write to tb - data.data.id = UID(); - nosql.insert(data.data); - } - } - }); + //write to tb + data.data.id = UID(); + nosql.insert(data.data); + } + } + }); - instance.close = function(done) { - if (clientReady) { - client.end(); - clearInterval(sendWsStatusVar); - } - }; + instance.close = function(done) { + if (clientReady) { + client.end(); + clearInterval(sendWsStatusVar); + } + }; - function getDbBackupFileCounter(type) { - var files = fs.readdirSync(__dirname + "/../databases"); + function getDbBackupFileCounter(type) { + var files = fs.readdirSync(__dirname + "/../databases"); - let counter = 0; - for (var i = 0; i < files.length; i++) { + let counter = 0; + for (var i = 0; i < files.length; i++) { - if (files[i] == "tbdata.nosql") continue; + if (files[i] == "tbdata.nosql") continue; - if (files[i].endsWith(".nosql")) { + if (files[i].endsWith(".nosql")) { - let pos = files[i].indexOf("."); - if (pos > -1) { + let pos = files[i].indexOf("."); + if (pos > -1) { - let fileCounter = counter; - let firstDigit = files[i].slice(0, pos); + let fileCounter = counter; + let firstDigit = files[i].slice(0, pos); - fileCounter = parseInt(firstDigit); - if (isNaN(fileCounter)) fileCounter = 0; - //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); + fileCounter = parseInt(firstDigit); + if (isNaN(fileCounter)) fileCounter = 0; + //console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type); - if (type == "max") { - if (fileCounter > counter) { - counter = fileCounter; - } - } - else if (type == "min") { - if (counter == 0) counter = fileCounter; + if (type == "max") { + if (fileCounter > counter) { + counter = fileCounter; + } + } + else if (type == "min") { + if (counter == 0) counter = fileCounter; - if (fileCounter < counter) { - counter = fileCounter; - } - } - } - } + if (fileCounter < counter) { + counter = fileCounter; + } + } + } + } - } + } - if (type == "max") counter++; + if (type == "max") counter++; - return counter; - } + return counter; + } - const makeBackupFromDbFile = async () => { + const makeBackupFromDbFile = async () => { - if (!saveTelemetryOnError) return; + if (!saveTelemetryOnError) return; - //to avoid large file: tbdata.nosql + //to avoid large file: tbdata.nosql - //init value is 0! - if (insertNoSqlCounter > 0) { - --insertNoSqlCounter; - return; - } + //init value is 0! + if (insertNoSqlCounter > 0) { + --insertNoSqlCounter; + return; + } - insertNoSqlCounter = 100; + insertNoSqlCounter = 100; - let source = __dirname + "/../databases/tbdata.nosql"; + let source = __dirname + "/../databases/tbdata.nosql"; - var stats = fs.statSync(source); - var fileSizeInBytes = stats.size; + var stats = fs.statSync(source); + var fileSizeInBytes = stats.size; - if (fileSizeInBytes > noSqlFileSizeLimit) { + if (fileSizeInBytes > noSqlFileSizeLimit) { - let counter = 1; - counter = getDbBackupFileCounter("max"); + let counter = 1; + counter = getDbBackupFileCounter("max"); - let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql"; + let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql"; - //make backup file - fs.copyFileSync(source, destination); - //fs.renameSync(p, p + "." + counter); + //make backup file + fs.copyFileSync(source, destination); + //fs.renameSync(p, p + "." + counter); - //clear tbdata.nosql - fs.writeFileSync(source, ""); - fs.truncateSync(source, 0); + //clear tbdata.nosql + fs.writeFileSync(source, ""); + fs.truncateSync(source, 0); - } - } + } + } - const processDataFromDatabase = async () => { + const processDataFromDatabase = async () => { - if (restore_from_backup <= 0) return; + if (restore_from_backup <= 0) return; - //calculate diff - const now = new Date(); - let currentTime = now.getTime(); - let diff = currentTime - lastRestoreTime; + //calculate diff + const now = new Date(); + let currentTime = now.getTime(); + let diff = currentTime - lastRestoreTime; - if ((diff / 1000) < restore_backup_wait) { - //console.log("*********restore_backup_wait", diff, restore_backup_wait); - return; - } + if ((diff / 1000) < restore_backup_wait) { + //console.log("*********restore_backup_wait", diff, restore_backup_wait); + return; + } - processingData = true; + processingData = true; - //get filename to process - let counter = getDbBackupFileCounter("min"); + //get filename to process + let counter = getDbBackupFileCounter("min"); - //we have some backup files - let dataBase = 'tbdata'; + //we have some backup files + let dataBase = 'tbdata'; - var nosql; - if (counter == 0) dataBase = 'tbdata'; - else dataBase = counter + "." + 'tbdata'; + var nosql; + if (counter == 0) dataBase = 'tbdata'; + else dataBase = counter + "." + 'tbdata'; - nosql = NOSQL(dataBase); + nosql = NOSQL(dataBase); - //select all data - use limit restore_from_backup - let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); + //select all data - use limit restore_from_backup + let records = await promisifyBuilder(nosql.find().take(restore_from_backup)); - for (let i = 0; i < records.length; i++) { - if (clientReady) { + for (let i = 0; i < records.length; i++) { + if (clientReady) { - let item = records[i]; - let id = item.id; + let item = records[i]; + let id = item.id; - if (id !== undefined) { - //console.log("------------processDataFromDatabase - remove", id, dataBase, i); + if (id !== undefined) { + //console.log("------------processDataFromDatabase - remove", id, dataBase, i); - try { + try { - let message = JSON.parse(JSON.stringify(item)); - delete message.id; + let message = JSON.parse(JSON.stringify(item)); + delete message.id; - client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 }); + client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 }); - //remove from database - await promisifyBuilder(nosql.remove().where("id", id)); + //remove from database + await promisifyBuilder(nosql.remove().where("id", id)); - } catch (error) { - //process error - console.log("processDataFromDatabase", error); - } + } catch (error) { + //process error + console.log("processDataFromDatabase", error); + } - } + } - } - else { - processingData = false; - return; - } - } + } + else { + processingData = false; + return; + } + } - if (records.length > 0) { - //clean backup file - if (counter > 0) nosql.clean(); - } + if (records.length > 0) { + //clean backup file + if (counter > 0) nosql.clean(); + } - //no data in db, remove - if (records.length == 0) { - if (counter > 0) nosql.drop(); - } + //no data in db, remove + if (records.length == 0) { + if (counter > 0) nosql.drop(); + } - const d = new Date(); - lastRestoreTime = d.getTime(); + const d = new Date(); + lastRestoreTime = d.getTime(); - processingData = false; + processingData = false; - } + } - instance.on('options', main); - //instance.reconfigure(); + instance.on('options', main); + //instance.reconfigure(); };