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