citysys-flowserver/flow/cmd_manager.js

3793 lines
110 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

exports.id = 'cmd_manager';
exports.title = 'CMD Manager';
exports.group = 'Worksys';
exports.color = '#5D9CEC';
exports.version = '0.0.3';
exports.output = ['red', 'blue', 'yellow', 'blue', 'white'];
//blue - send message to relays
exports.input = true;
exports.author = 'Daniel Segeš';
exports.icon = 'cloud-upload';
//exports.npm = ['serialport' , 'child_process'];
exports.html = `
<div class="padding">
<div class="row">
<div class="col-md-12">
<div>RPC - run RPC calls</div><br>
</div>
</div>
<div data-jc="textbox" data-jc-path="username" class="m" data-jc-config="required:true">@(User)</div>
<div class="row">
<div class="col-md-6 m">
<div data-jc="textbox" data-jc-path="userpassword" data-jc-config="required:true">@(Password)</div>
</div>
<div class="col-md-6 m">
<div data-jc="textbox" data-jc-path="edge" data-jc-config="required:true">@(My edge)</div>
</div>
</div>
</div>
`;
exports.readme = `Manager for CMD calls`;
const SerialPort = require('serialport');
const { exec } = require('child_process');
const { crc8, crc16, crc32 } = require('easy-crc');
const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js');
const { bytesToInt, longToByteArray, addZeroBefore, isEmptyObject, convertUTCDateToLocalDate } = require('./helper/utils');
const bitwise = require('bitwise');
var SunCalc = require('./helper/suncalc.js');
const DataToTbHandler = require('./helper/DataToTbHandler.js');
const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js');
const { promisifyBuilder, makeMapFromDbResult} = require('./helper/db_helper.js');
const { sendNotification, initNotifications, ERRWEIGHT } = require('./helper/notification_reporter.js');
const dbNodes = TABLE("nodes");
const dbRelays = TABLE("relays");
const dbSettings = TABLE("settings");
//https://github.com/log4js-node/log4js-node/blob/master/examples/example.js
//file: { type: 'file', filename: path.join(__dirname, 'log/file.log') }
var path = require('path');
var log4js = require("log4js");
const process = require('process');
//TODO - to remove?
// runTasks intervals
const SHORT_INTERVAL = 30;
const LONG_INTERVAL = 300;
//send data to following instances:
const SEND_TO = {
debug: 0,
tb: 1,
http_response: 2,
dido_controller: 3,
infoSender: 4
}
const PRIORITY_TYPES = {
terminal: 0,
fw_detection: 1,//reserved only for FW detection - FLOW.OMS_masterNodeIsResponding
high_priority: 2,//reserverd only for: read dimming / brightness (after set dimming from platform)
relay_profile: 3,
node_broadcast: 4,
node_profile: 5,
node_cmd: 6
}
//list of command calls to process. Processing in runTasks function
let tasks = [];
let interval = null;//timeout for procesing tasks
let refFlowdata = null;//holds reference to httprequest flowdata
let refFlowdataObj = {};
//load from settings
let latitude = 48.70826502;//48.682255758;
let longitude = 17.28455203;//17.278910807;
const gmtOffset = 0;
//ak nie je nastaveny
//https://www.tecmint.com/set-time-timezone-and-synchronize-time-using-timedatectl-command/
//https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates
//priorities for registers
let priorities = [];
let minutes = 1;
priorities["0"] = minutes;
priorities["1"] = minutes;
minutes = 5;
priorities["74"] = minutes;
priorities["75"] = minutes;
priorities["76"] = minutes;
priorities["77"] = minutes;
priorities["78"] = minutes;
priorities["79"] = minutes;
priorities["84"] = minutes;
minutes = 10;
priorities["87"] = minutes;
priorities["6"] = minutes;
priorities["7"] = minutes;
priorities["80"] = minutes;
priorities["8"] = minutes;
priorities["3"] = minutes;
priorities["89"] = minutes;
//prikazy kt sa budu spustat na dany node - see config.js in terminal-oms.app. (1 - dimming)
let listOfCommands = [0,1,3,6,7,8,74,75,76,77,78,79,80,84,87,89];
const errorHandler = new ErrorToServiceHandler();
let rotary_switch_state = "Off";
let lux_sensor;
let state_of_breaker = {};//key is line, value is On/Off
let disconnectedReport = {};//key is tbname, value true/false
let relaysData = {};//key is line, value is data from db
let nodesData = {};//key is node, value data from db
//helper container for counting resolved group of commands (commands related to set profile)
let cmdCounter = {};//key is node, value is counter
let cmdNOKNodeCounter = {};//key is node, value is counter
//END OF VARIABLE SETTINGS
//--------------------------------
log4js.configure({
appenders: {
errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'err.txt') },
monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') },
console: { type: 'console' }
},
categories: {
errLogs: { appenders: ['console', 'errLogs'], level: 'error' },
monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' },
//another: { appenders: ['console'], level: 'trace' },
default: { appenders: ['console'], level: 'trace' }
}
});
const errLogger = log4js.getLogger("errLogs");
const logger = log4js.getLogger();
const monitor = log4js.getLogger("monitorLogs");
//USAGE
//logger.debug("text")
//monitor.info('info');
//errLogger.error("some error");
function cmdCounterResolve(address)
{
if(cmdCounter.hasOwnProperty(address))
{
cmdCounter[address] = cmdCounter[address] - 1;
let result = cmdCounter[address];
if(result == 0) delete cmdCounter[address];
return result;
}
return -1;
}
function getParams(priority)
{
let params = {};
//core rpc values
params.address = 0;//if(recipient === 0) address = 0;
params.byte1 = 0;//msb, podla dokumentacie data3
params.byte2 = 0;//podla dokumentacie data2
params.byte3 = 0;//podla dokumentacie data1
params.byte4 = 0;//lsb, podla dokumentacie data0
params.recipient = 0;//0: Master, 1: Slave, 2: Broadcast
params.register = -1;//register number
params.rw = 0;//0: read, 1: write
//other values
//params.type = "cmd"; "relay" "cmd-terminal" "set_node_profile" "process_profiles" "edge_date_time" "number_of_luminaires"
//params.tbname = tbname;
params.priority = PRIORITY_TYPES.node_cmd; //default priority - if more tasks with the same timestamp, we sort them based on priority
params.timestamp = 0; //execution time - if timestamp < Date.now(), the task is processed
if(priority != undefined )
{
params.timestamp = priority;
params.priority = priority;
}
params.addMinutesToTimestamp = 0;//repeat task if value is > 0,
//params.timePointName = "luxOff" // "luxOn", "dusk", "dawn", "profileTimepoint"
//params.info = "";
return params;
}
async function loadSettings()
{
let responseSettings = await promisifyBuilder(dbSettings.find());
latitude = responseSettings[0]["latitude"];
longitude = responseSettings[0]["longitude"];
//globals
FLOW.OMS_language = responseSettings[0]["lang"];
FLOW.OMS_rvo_name = responseSettings[0]["rvo_name"];
FLOW.OMS_projects_id = responseSettings[0]["projects_id"];
//FLOW.OMS_rvo_tbname = responseSettings[0]["tbname"];
FLOW.OMS_temperature_adress = responseSettings[0]["temperature_adress"];
FLOW.OMS_controller_type = responseSettings[0]["controller_type"];
FLOW.OMS_serial_port = responseSettings[0]["serial_port"];
//logger.debug('settings', responseSettings[0]);
initNotifications();
}
loadSettings();
async function loadNodes()
{
const responseNodes = await promisifyBuilder(dbNodes.find());
nodesData = makeMapFromDbResult(responseNodes, "node");
}
loadNodes();
//nastav profil nodu
function processNodeProfile(node)
{
if(rotary_switch_state != "Automatic")
{
logger.debug("unable to process profile for node", node, "rotary_switch_state != Automatic");
return;
}
let nodeObj = nodesData[node];
let line = nodeObj.line;
if(relaysData[line].contactor == 0)
{
logger.debug("line line is off", line, node);
return;
}
if(nodeObj.processed == 1)
{
logger.debug("node was already processed", node);
return;
}
let profile = nodeObj.profile;
logger.debug("processNodeProfile: start - set profile for ", node, profile);
let nodeProfile;
try{
nodeProfile = JSON.parse( profile );
if(Object.keys(nodeProfile).length === 0) throw ("profile is not defined");
} catch (error) {
logger.debug("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});
cmdNOKNodeCounter[node] = 0;
//co ked sa prave spracovava?
//if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false;
if(nodeProfile === undefined)
{
//vypneme profil nodu, posleme cmd
//Pokiaľ je hodnota rovná 1 Profil sa zapne, ostatné bity sa nezmenia.
//Pokiaľ sa hodnota rovná 2 profil sa vypne, ostatné bity sa nezmenia
logger.debug("turn off profile");
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.byte1 = 0;
params.byte2 = 0;
params.byte3 = 0;
params.byte4 = 96;
params.recipient = 1;
params.register = 8;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = 'turn off/reset node profile';
cmdCounter[node] = 1;
tasks.push(params);
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", SEND_TO.tb, instance );
}
else
{
let tasksProfile = [];
//cmdCounter[node] = tasksProfile.length;
//tasks.push(tasksProfile);
//let timestamp = PRIORITY_TYPES.node_cmd;
//vypneme profil - Zapísať hodnotu 32 do registra Time Schedule Settings reset profilu
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.byte1 = 0;
params.byte2 = 0;
params.byte3 = 0;
params.byte4 = 96;
params.recipient = 1;
params.register = 8;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = 'turn off node profile';
tasksProfile.push(params);
timestamp++;
logger.debug("processNodeProfile: TS1 Time point a TS1 Time Point Levels ", node);
//TS1 Time point a TS1 Time Point Levels
let register = 9;
for(let i = 0; i < nodeProfile.intervals.length; i++)
{
let obj = nodeProfile.intervals[i];
//let timePoint = obj.time_point;
let dim_value = obj.value;
//Reg 9 až Reg 40
/*
Samotný profil sa zapisuje do max. 16 párov časový bod a úroveň.
Prázdny profil je vtedy keď časový bod obsahuje hodnotu 0xFFFFFFFF (táto hodnota sa zapíše do registrov keď sa aktivuje reset profilu do registru 8).
Páry sa prechádzajú časovo zoradené takže teoreticky je jedno v akom poradí sa zapisujú ale je lepšie ich zapisovať v chronologickom poradí od 13:00.
Časový bod má formát:
Byte 3: hodiny Byte 2: minúty Byte 1: sekundy Byte 0 rezervované
Register úrovne má rovnaký formát ako dimming register (Reg 1).
*/
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//params.byte1 = 0;//msb, podla dokumentacie data3
//params.byte2 = 0;//podla dokumentacie data2
//params.byte3 = 0;//podla dokumentacie data1
//params.byte4 = 0;//lsb, podla dokumentacie data0
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let start_time = obj.start_time;
let t = start_time.split(":");
//if(timePoint != undefined) t = timePoint.split(":");
//else t = [0,0];
logger.debug("processNodeProfile: TS1 Time point ", (i + 1), node);
params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.byte1 = parseInt(t[0]);//hh
params.byte2 = parseInt(t[1]);//mm
params.byte3 = 0;//ss
params.byte4 = 0;//
params.recipient = 1;
params.register = register;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = 'TS1 Time point ' + (i + 1);
tasksProfile.push(params);
register++;
timestamp++;
params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.byte1 = 0;
params.byte2 = 0;
params.byte3 = 0;//ss
params.byte4 = parseInt(dim_value) + 128;//
params.recipient = 1;
params.register = register;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = 'TS1 Time point Levels ' + (i + 1);
tasksProfile.push(params);
register++;
timestamp++;
}
//Threshold lux level for DUSK/DAWN
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//params.byte1 = 0;//msb, podla dokumentacie data3
//params.byte2 = 0;//podla dokumentacie data2
//params.byte3 = 0;//podla dokumentacie data1
//params.byte4 = 0;//lsb, podla dokumentacie data0
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//Time schedule settings na koniec
//if(nodeProfile.dusk_lux_sensor || nodeProfile.dawn_lux_sensor)
{
logger.debug("processNodeProfile: Threshold lux level for DUSK/DAWN", node);
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.register = 96;
params.recipient = 1;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = "Threshold lux level for DUSK/DAWN";
if(nodeProfile.dusk_lux_sensor)
{
let v = nodeProfile.dusk_lux_sensor_value;
let ba = longToByteArray(v);
params.byte1 = ba[1];//msb
params.byte2 = ba[0];
}
if(nodeProfile.dawn_lux_sensor)
{
let v = nodeProfile.dawn_lux_sensor_value;
let ba = longToByteArray(v);
params.byte3 = ba[1];//msb
params.byte4 = ba[0];
}
tasksProfile.push(params);
timestamp++;
}
//DUSK/DAWN max. adjust period
{
logger.debug("processNodeProfile: DUSK/DAWN max. adjust period", node);
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.register = 97;
params.recipient = 1;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = "DUSK/DAWN max. adjust period";
if(nodeProfile.astro_clock)
{
let v = nodeProfile.dusk_lux_sensor_time_window;
let ba = longToByteArray(v);
params.byte1 = ba[1];//msb
params.byte2 = ba[0];
}
if(nodeProfile.astro_clock)
{
let v = nodeProfile.dawn_lux_sensor_time_window;
let ba = longToByteArray(v);
params.byte3 = ba[1];//msb
params.byte4 = ba[0];
}
tasksProfile.push(params);
timestamp++;
}
//Static offset
{
//Statický offset pre časy úsvitu a súmraku. Byte 1 je pre DUSK, Byte 0 je pre DAWN. Formát:
//Bity 0 6: hodnota v minútach
//Bit 7: znamienko (1 mínus)
logger.debug("processNodeProfile: Static offset", node);
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.register = 98;
params.recipient = 1;
params.rw = 1;//write
params.timestamp = timestamp;
params.addMinutesToTimestamp = 0;
params.info = "Static offset";
if(nodeProfile.astro_clock)
{
let dusk_astro_clock_offset = parseInt(nodeProfile.dusk_astro_clock_offset);
let dawn_astro_clock_offset = parseInt(nodeProfile.dawn_astro_clock_offset);
if(dusk_astro_clock_offset < 0)
{
params.byte3 = (dusk_astro_clock_offset * -1) + 128;
}
else
{
params.byte3 = dusk_astro_clock_offset;
}
if(dawn_astro_clock_offset < 0)
{
params.byte4 = (dawn_astro_clock_offset * -1) + 128;
}
else
{
params.byte4 = dawn_astro_clock_offset;
}
}
tasksProfile.push(params);
timestamp++;
}
logger.debug("Time schedule settings - turn on", node);
params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "set_node_profile";
params.address = node;
params.register = 8;
params.recipient = 1;
params.rw = 1;//write
//Time schedule settings
let bits = [];
//Byte 0 (LSB):
//Bit 0 (LSB) zapnutie/vypnutie profilov ako takých (1 zapnuté).
bits.push(1);
//Bit 1 3 - zatiaľ nepoužité (zapisovať 0)
bits.push(0);
bits.push(0);
bits.push(0);
if(nodeProfile.astro_clock == true)
{
//Bit 4 ak je nastavený profil sa riadi podľa astrohodín, a je 0 tak profil je jednoduchý
bits.push(1);
}
else bits.push(0);
//Bit 5 zápis 1 spôsobí reset nastavení profilu (nastavenie prázdneho profilu)
bits.push(0);
//Bity 6-7 - zatiaľ nepoužité
bits.push(0);
bits.push(0);
params.byte4 = bitwise.byte.write(bits.reverse());
//Byte 2 nastavenie pre lux senzor:
bits = [];
//Bit 0 (LSB) riadenie súmraku podľa lux senzoru (1 zapnuté). Súmrak sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia
if(nodeProfile.dusk_lux_sensor == true)//sumrak
{
bits.push(1);
}
else bits.push(0);
//Bit 1 - riadenie úsvitu podľa lux senzoru (1 zapnuté). Úsvit sa môže posúvať v rámci času v registri 97 podľa intenzity osvetlenia
if(nodeProfile.dawn_lux_sensor == true)//usvit
{
bits.push(1);
}
else bits.push(0);
//Bit 2 zdroj pre hodnotu luxov 0 RVO posiela hodnoty zo svojho luxmetra, 1 node má pripojený svoj vlastný lux meter.
bits.push(0);//zatial neimplementovane
//Bit 3 7 - nepoužité
bits.push(0);
bits.push(0);
bits.push(0);
bits.push(0);
bits.push(0);
params.byte2 = bitwise.byte.write(bits.reverse());
params.timestamp = timestamp;
params.info = "Time schedule settings - turn on";
tasksProfile.push(params);
//zaver
cmdCounter[node] = tasksProfile.length;
//tasks.push(tasksProfile);
tasks = tasks.concat(tasksProfile);
}
logger.debug("finished set profile for ", node);
}
function cleanUpRefFlowdataObj()
{
let now = new Date();
let timestamp = now.getTime();
//clear old refFlowdata references
let keys = Object.keys(refFlowdataObj);
for(let i = 0; i < keys.length; i++)
{
let timestampKey = keys[i];
if((timestamp - timestampKey) > 60*1000 )
{
console.log("cleanUpRefFlowdataObj delete", timestampKey);
delete refFlowdataObj[ timestampKey ];
}
}
}
function removeTask(obj)
{
let keys = Object.keys(obj);
tasks = tasks.filter((task) => {
let counter = 0;
for(let i = 0; i < keys.length; i++)
{
let key = keys[i];
if(task.hasOwnProperty(key) && obj.hasOwnProperty(key))
{
if(task[key] == obj[key]) counter++;
}
}
if(counter == keys.length) return false;
return true;
});
}
exports.install = function(instance) {
let now = new Date();
console.log("CMD Manager installed", now.toLocaleString("sk-SK"));
const tbHandler = new DataToTbHandler(SEND_TO.tb);
tbHandler.setSender(exports.title);
//FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name
//const errorHandler = new ErrorToServiceHandler(instance, SEND_TO.infoSender);
errorHandler.setProjectsId(FLOW.OMS_projects_id);
//const errorHandler = new ErrorToServiceHandler(instance);
//errorHandler.sendMessageToService("ahoj", 0);
let sunCalcResult = calculateDuskDawn();
let reportDuskDawn = {
dusk_time: sunCalcResult.dusk_time,
dawn_time: sunCalcResult.dawn_time,
dusk_time_reported: undefined,
dawn_time_reported: undefined
};
process.on('uncaughtException', function (err) {
//TODO send to service
errLogger.error('uncaughtException:', err.message)
errLogger.error(err.stack);
errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error");
//process.exit(1);
})
//te();//force error
function processAllNodeProfilesOnLine(line)
{
for (let k in nodesData) {
//node:number|tbname:string|line:number|profile:string|processed:boolean
if(line == nodesData[k].line)
{
let node = nodesData[k].node;
let processed = nodesData[k].processed;
if(!processed)
{
processNodeProfile(node);
}
else
{
logger.debug( `Node ${node} profile for line ${nodesData[k].line} was already processed`);
}
}
}
}
async function loadRelaysData(line) {
relaysData = await promisifyBuilder(dbRelays.find());
relaysData = makeMapFromDbResult(relaysData, "line");
for (const [key, value] of Object.entries(relaysData))
{
if(key == "0") continue;
if(line != undefined)
{
//ak sa jedna o update profilu linie - pozor dido_controller posiela command pre loadRelaysData
if(line != value.line ) continue;
}
if(value.contactor == 1) processAllNodeProfilesOnLine(value.line);
}
// console.log('.........', relaysData);
}
function reportOnlineNodeStatus(line)
{
//broadcast cas, o 3 sek neskor - status, brightness
//Po zapnutí línie broadcastovo aktualizovať predtým čas.
logger.debug("--->reportOnlineNodeStatus for line", line);
//return;
//run broadcast //Actual time
addMinutesToTimestamp = 0;
let params = {};
let recipient = 2;//2 broadcast, address = 0
let address = 0;//0
if(recipient === 2)
{
address = 0xffffffff;//Broadcast
}
var d = new Date();
let hours = d.getHours();
let minutes = d.getMinutes();
let seconds = d.getSeconds();
params.address = address;//broadcast
params.byte1 = hours;//h
params.byte2 = minutes;//m
params.byte3 = seconds;//s
params.byte4 = 0;
params.recipient = recipient;
params.register = 87;//Actual time
params.rw = 1;//write
let timestampStart = PRIORITY_TYPES.node_broadcast;
//other values
params.type = "cmd";
//params.tbname = tbname;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = addMinutesToTimestamp;
params.info = "run broadcast: Actual time";
tasks.push(params);
let sec = 3;
setTimeout(function(){
//Po zapnutí línie - spraviť hromadný refresh stavu práve zapnutých svietidiel
for (let k in nodesData) {
//potrebujem nody k danej linii
if(line == nodesData[k].line || line == undefined)
{
let tbname = nodesData[k].tbname;
let node = nodesData[k].node;
//prud, vykon - current, input power pre liniu pre vsetky nody
//a pridame aj vyreportovanie dimmingu
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 1;//dimming
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read dimming';
//params.debug = true;
tasks.push(params);
}
//Prúd
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 75;//prud
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read current';
//params.debug = true;
tasks.push(params);
}
//výkon
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 76;//výkon
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read power';
//params.debug = true;
tasks.push(params);
}
}
}
},sec*1000);
}
function reportOfflineNodeStatus(line)
{
logger.debug("--->reportOfflineNodeStatus for line", line);
values = {};
values["dimming"] = 0;//brightness
values["power"] = 0;//výkon
values["current"] = 0;//prúd
values["status"] = "OFFLINE";//prúd
for (let k in nodesData) {
//potrebujem nody k danej linii
if(line == nodesData[k].line || line == undefined)
{
let tbname = nodesData[k].tbname;
//logger.debug("node:", tbname);
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
}
}
//report OFFLINE for line
//relaysData[line].tbname;
//values = {};
//values["status"] = "OFFLINE";//prúd
}
function turnOnLine(line, info)
{
let obj = {
line: line,
command: "turnOn",
info: info
};
logger.debug("linia", line, obj);
instance.send(SEND_TO.dido_controller, obj);
}
function turnOffLine(line, info)
{
let obj = {
line: line,
command: "turnOff",
info: info
};
logger.debug("linia", line, obj);
instance.send(SEND_TO.dido_controller, obj);
}
function detectIfResponseIsValid(bytes)
{
//ak sa odpoved zacina 0 - je to v poriadku, inak je NOK
let type = "RESPONSE";
if(bytes[4] == 0) type = "RESPONSE";
else if(bytes[4] == 1) type = "ERROR";
else if(bytes[4] == 2) type = "EVENT";
else type = "UNKNOWN";
let crc = crc16('ARC', bytes.slice(0, 9));
let c1 = (crc >> 8) & 0xFF;
let c2 = crc & 0xFF;
let message = "OK";
let error = "";
if(c1 != bytes[9])
{
//CRC_ERROR
message = "NOK";
error = "CRC_ERROR c1";
instance.send(SEND_TO.debug, "CRC_ERROR c1");
}
if(c2 != bytes[10])
{
//CRC_ERROR
message = "NOK";
error = "CRC_ERROR c2";
instance.send(SEND_TO.debug, "CRC_ERROR c2");
}
//crc error
if(type != "RESPONSE")
{
instance.send(SEND_TO.debug, bytes);
instance.send(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4]);
//logger.debug(SEND_TO.debug, "RESPONSE " + type + " - " + bytes[4], bytes);
error = "type is: " + type;
message = "NOK";
}
return {message: message, type: type, error: error};
}
//BUILD TASKS//
function buildTasks(params)
{
//report FLOW.OMS_edge_fw_version as fw_version
//report date as startdate
//return;
monitor.info("buildTasks - params", params);
let processLine; //defined line
let init = false;
let processLineProfiles = true;
let processBroadcast = true;
let processNodes = true;
if(params == undefined)
{
init = true;
tasks = [];
logger.debug("-->buildTasks clear tasks");
}
else
{
processLineProfiles = false;
processBroadcast = false;
processNodes = false;
processLineProfiles = params.processLineProfiles;
processLine = params.line;
}
//load profiles pre linie
//relaysData[ record["line"] ]
let now = new Date();
if(processLineProfiles)
{
//process line profiles
let keys = Object.keys(relaysData);
for(let i = 0; i < keys.length; i++)
{
let line = parseInt(keys[i]); //line is turned off by default
let profilestr = relaysData[line].profile;
if(processLine != undefined)
{
if(processLine != line) continue;
}
try {
/**
* we process line profiles: timepoints, astro clock, lux_sensor, offsets ...
*/
if(profilestr === "") throw ("Profile is not defined");
let profile = JSON.parse(profilestr);
if(Object.keys(profile).length === 0) throw ("Profile is empty");
monitor.info("buildTasks: profile for line", line);
monitor.info("profile:", profile);
let time_points = profile.time_points;
if(time_points == undefined) time_points = profile.intervals;
// add name to regular profile timepoint and delete unused end_time key:
time_points.forEach(point => {
point.name = "profileTimepoint"
delete point.end_time;
});
//monitor.info("buildTasks: time_points", time_points);
let currentValue = 0;
if(time_points.length > 0) currentValue = time_points[time_points.length - 1].value;
/**
* if astro_clock is true, we create timepoints, that switch on/off relays accordingly.
* we need to manage, astro clock timepoints has the greatest priority - normal timepoints will not switch off/on lines before dusk or dawn
* if dawn/dusk_lux_sensor is true, it has higher priority than astro_clock switching
*/
if(profile.astro_clock == true)
{
// if astro clock true, we remove all regular profile points
time_points = [];
let sunCalcResult = calculateDuskDawn(new Date(), line);
// adding dusk dawn to timpoints
if(profile.dawn_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dawn"], "value": 0, "name":"dawn"});
if(profile.dusk_lux_sensor == false) time_points.push({"start_time": sunCalcResult["dusk"], "value": 1, "name":"dusk"});
//if dusk/dawn is true, lines will switch on/off according to lux_sensor value. In case it fails, we create lux_timepoints, to make sure lines will switch on/off (aby nam to nezostalo svietit)
//force to turn off after timestamp: dawn + dawn_lux_sensor_time_window
if(profile.dawn_lux_sensor == true)
{
let [ahours, aminutes, aseconds] = sunCalcResult["dawn"].split(':');
let ad = new Date();
ad.setHours(parseInt(ahours));
ad.setMinutes(parseInt(aminutes) + profile.dawn_lux_sensor_time_window);
ad.setSeconds(0);
let strDate = ad.getHours() + ":" + ad.getMinutes();
time_points.push({"value": 0, "start_time": strDate, "name": "luxOff"});
}
if(profile.dusk_lux_sensor == true)
{
let [ahours, aminutes, aseconds] = sunCalcResult["dusk"].split(':');
let ad = new Date();
ad.setHours(parseInt(ahours));
ad.setMinutes(parseInt(aminutes) + profile.dusk_lux_sensor_time_window);
ad.setSeconds(0);
let strDate = ad.getHours() + ":" + ad.getMinutes();
time_points.push({"value": 1, "start_time": strDate, "name": "luxOn"});
//time_points.push({"value": 1, "start_time": "15:19", "name": "luxOn"}); //testing
}
}
//sort time_points
time_points.sort(function (a, b) {
let [ahours, aminutes, aseconds] = a.start_time.split(':');
let [bhours, bminutes, bseconds] = b.start_time.split(':');
let ad = new Date();
ad.setHours( parseInt(ahours) );
ad.setMinutes( parseInt(aminutes) );
ad.setSeconds(0);
let bd = new Date();
bd.setHours( parseInt(bhours) );
bd.setMinutes( parseInt(bminutes) );
ad.setSeconds(0);
return ad.getTime() - bd.getTime();
});
console.log("line timepoints ........", time_points);
monitor.info("-->comming events turn on/off lines:");
for(let t = 0; t < time_points.length; t++)
{
let start_time = new Date();
let [hours, minutes, seconds] = time_points[t].start_time.split(':');
start_time.setHours( parseInt(hours) );
start_time.setMinutes( parseInt(minutes) );
start_time.setSeconds(0);
//task is the past
if(now.getTime() > start_time.getTime())
{
currentValue = time_points[t].value;
//timepoint is in past, we add 24 hours
start_time.setDate(start_time.getDate() + 1);
}
let params = getParams(PRIORITY_TYPES.relay_profile);
params.type = "relay";
params.line = parseInt(line);
params.value = time_points[t].value;
params.tbname = relaysData[line].tbname;
params.timestamp = start_time.getTime();
params.addMinutesToTimestamp = 0;
// it timepoints are not calculated (dawn, dusk, lux_timepoint), but static points in line profile, we just repeat the task every day
if(time_points[t].name == "profileTimepoint") params.addMinutesToTimestamp = 24*60;
//astro timepoints will be recalculated dynamically:
params.timePointName = time_points[t].name;
// if astro timepoint, we save time window:
if(['luxOn', 'luxOff', 'dusk','dawn'].includes(params.timePointName))
{
params.dawn_lux_sensor_time_window = profile.dawn_lux_sensor_time_window;
params.dusk_lux_sensor_time_window = profile.dusk_lux_sensor_time_window;
}
if(params.value == 0) params.info = `${params.timePointName}: turn off line: ` + line;
else if(params.value == 1) params.info = `${params.timePointName}: turn on line: ` + line;
params.debug = true;
//turn on/off line
tasks.push(params);
monitor.info(params.info, start_time);
}
monitor.info("-->time_points final", line, time_points);
//ensure to turn on/off according to calculated value
let params = getParams(PRIORITY_TYPES.terminal);
params.type = "relay";
params.line = parseInt(line);
params.tbname = relaysData[line].tbname;
params.value = currentValue;
params.timestamp = PRIORITY_TYPES.terminal;
params.addMinutesToTimestamp = 0;
params.debug = true;
//logger.debug(now.toLocaleString("sk-SK"));
monitor.info("-->currentValue for relay", line, currentValue);
//turn on/off line
if(params.value == 0) params.info = "turn off line on startup: " + line;
else if(params.value == 1) params.info = "turn on line on startup: " + line;
tasks.push(params);
} catch (error) {
if(profilestr !=="" )
{
//errLogger.error(profilestr, error);
errorHandler.sendMessageToService(profilestr + "-" + error, 0, "js_error");
}
}
}
//logger.debug("tasks:");
//logger.debug(tasks);
}
//PROCESS DEFAULT BROADCASTS
//RPC pre nody / broadcast
//Time of dusk, Time of dawn
//Actual Time
if(processBroadcast)
{
let addMinutesToTimestamp = 5;
{
//run broadcast Time of dusk
addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dusk
let params = getParams(PRIORITY_TYPES.node_broadcast);
let sunCalcResult = calculateDuskDawn();
let dusk_hours = sunCalcResult["dusk_hours"];
let dusk_minutes = sunCalcResult["dusk_minutes"];
params.address = 0xffffffff;//broadcast
params.byte1 = dusk_hours;//h
params.byte2 = dusk_minutes;//m
params.byte3 = 0;//s
params.byte4 = 0;
params.recipient = 2;//2 broadcast,
params.register = 6;//Time of dusk - Reg 6
params.rw = 1;//write
let timestampStart = PRIORITY_TYPES.node_broadcast;
//other values
params.type = "cmd";
//params.tbname = tbname;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = addMinutesToTimestamp;
params.info = "Broadcast-duskTime";
tasks.push(params);
}
{
//run broadcast Time of dawn
// addMinutesToTimestamp = 60*5;
addMinutesToTimestamp = 60 * 3; //kazde 3 hodiny zisti novy dawn
let params = getParams(PRIORITY_TYPES.node_broadcast);
let sunCalcResult = calculateDuskDawn();
let dawn_hours = sunCalcResult["dawn_hours"];
let dawn_minutes = sunCalcResult["dawn_minutes"];
params.address = 0xffffffff;//broadcast
params.byte1 = dawn_hours;//h
params.byte2 = dawn_minutes;//m
params.byte3 = 0;//s
params.byte4 = 0;
params.recipient = 2; //2 broadcast
params.register = 7;//Time of dawn - Reg 6
params.rw = 1;//write
let timestampStart = PRIORITY_TYPES.node_broadcast;
//other values
params.type = "cmd";
//params.tbname = tbname;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = addMinutesToTimestamp;
params.info = "Broadcast-dawnTime";
tasks.push(params);
}
{
//run broadcast //Actual time
addMinutesToTimestamp = 5;
let params = getParams(PRIORITY_TYPES.node_broadcast);
var d = new Date();
let hours = d.getHours();
let minutes = d.getMinutes();
let seconds = d.getSeconds();
params.address = 0xffffffff;//broadcast
params.byte1 = hours;//h
params.byte2 = minutes;//m
params.byte3 = seconds;//s
params.byte4 = 0;
params.recipient = 2; //2 broadcast
params.register = 87;//Actual time
params.rw = 1;//write
let timestampStart = PRIORITY_TYPES.node_broadcast;
//other values
params.type = "cmd";
//params.tbname = tbname;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = addMinutesToTimestamp;
params.info = "run broadcast: Actual time";
tasks.push(params);
}
}
//process nodes & tasks
//reportovanie pre platformu
if(processNodes)
{
for (let k in nodesData) {
let address = parseInt(k);
let tbname = nodesData[k].tbname;
let register = 0;
//logger.debug("generated cmd - buildTasks for node:", address);
//listOfCommands - READ
for(let i = 0; i < listOfCommands.length; i++)
{
register = listOfCommands[i];
let params = getParams(PRIORITY_TYPES.node_cmd);
//core rpc values
params.address = address;
params.byte1 = 0;
params.byte2 = 0;
params.byte3 = 0;
params.byte4 = 0;
params.recipient = 1;
params.register = register;
params.rw = 0;
let addMinutesToTimestamp = priorities[register];
let timestampStart = PRIORITY_TYPES.node_cmd; //run imediatelly in function runTasks
if(addMinutesToTimestamp > 1)
{
timestampStart = timestampStart + addMinutesToTimestamp * 60000;
}
//other values
params.type = "cmd";
params.tbname = tbname;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = addMinutesToTimestamp;
params.info = "generated cmd - buildTasks (node)";
//monitor last node && last command
/*
if(register == listOfCommands[ listOfCommands.length - 1 ])
{
//if(k == 632) params.debug = true;
if(k == 698) params.debug = true;
}
*/
tasks.push(params);
}
}
}
//niektore ulohy sa vygeneruju iba 1x pri starte!!!
if(!init) return;
//Priebežne (raz za cca 5 minút) je potrebné vyčítať z Master nodu verziu jeho FW.
//Jedná sa o register 10. Rovnaká interpretácia ako pri FW verzii nodu.
//Adresa mastera je 0. V prípade že kedykoľvek nastane situácia že Master Node neodpovedá (napríklad pri vyčítaní telemetrie z nodu nevráti žiadne dáta),
//tak treba vyreportovať string "NOK".
{
let params = getParams(PRIORITY_TYPES.fw_detection);
params.type = "cmd";
params.register = 4;
params.address = 0;
let timestampStart = PRIORITY_TYPES.fw_detection;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = 5;
params.tbname = FLOW.OMS_edgeName;
params.info = "Master node FW verzia";
//params.debug = true;
//this will set FLOW.OMS_masterNodeIsResponding
tasks.push(params);
}
//kazdu hodinu skontrolovat nastavenie profilov
{
let params = getParams(PRIORITY_TYPES.fw_detection);
params.type = "process_profiles";
let timestampStart = PRIORITY_TYPES.relay_profile;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = 60;//60 = every hour
params.info = "detekcia nespracovaných profilov linie a nodov";
//params.debug = true;
tasks.push(params);
}
{
//edge_date_time
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "edge_date_time";
let timestampStart = PRIORITY_TYPES.node_cmd;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = 1;
params.tbname = FLOW.OMS_edgeName;
params.info = "reportovanie aktuálneho času na LM - EDGE-Date Time";
//logger.debug("BUILD Master node FW verzia");
tasks.push(params);
}
{
//edge_date_time
let params = getParams(PRIORITY_TYPES.node_cmd);
params.type = "number_of_luminaires";
let timestampStart = PRIORITY_TYPES.node_cmd + 1;
params.timestamp = timestampStart;
params.addMinutesToTimestamp = 1;
params.tbname = FLOW.OMS_edgeName;
params.info = "reportovanie number_of_luminaires";
tasks.push(params);
}
monitor.info("tasks created:", tasks.length);
}
/**
* We process line profile, where "astro_clock": true
* example profile:
*
"dawn_lux_sensor": true,
"dusk_lux_sensor": true,
"dawn_lux_sensor_value": 5,
"dusk_lux_sensor_value": 5,
"dawn_astro_clock_offset": 0,
"dusk_astro_clock_offset": 10,
"dawn_lux_sensor_time_window": 30,
"dusk_lux_sensor_time_window": 30,
"dawn_astro_clock_time_window": 60,
"dusk_astro_clock_time_window": 60
* if dawn: if currentTimestamp is in timewindow "dawnTime + and - dawn_lux_sensor_time_window" and lux value >= lux_sensor_value, we switch off the line.
* if dusk: we do oposite
*
* dawn: usvit - lux je nad hranicou - vypnem
* dusk: sumrak - lux je pod hranicou - zapnem
*/
function turnOnOffLinesAccordingToLuxSensor(lux_sensor_value)
{
let now = new Date();
let currentTimestamp = now.getTime();
let keys = Object.keys(relaysData);
for(let i = 0; i < keys.length; i++)
{
let line = keys[i]; //line is turned off by default
let profilestr = relaysData[line].profile;
const contactor = relaysData[line].contactor;
try {
let profile = JSON.parse(profilestr);
if(Object.keys(profile).length === 0) throw ("turnOnOffLinesAccordingToLuxSensor - profile is not defined");
if(profile.astro_clock == true)
{
let sunCalcResult = calculateDuskDawn(now, line);
//usvit
if(profile.dawn_lux_sensor == true)
{
let lux_sensor_time_window1 = sunCalcResult.dawn_time - (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60); // LUX_SENSOR_TIME_WINDOW x 1000 x 60 --> dostaneme odpocet/pripocitanie minut
let lux_sensor_time_window2 = sunCalcResult.dawn_time + (parseInt( profile.dawn_lux_sensor_time_window ) * 1000 * 60);
if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2)
{
if(lux_sensor_value > profile.dawn_lux_sensor_value)
{
if(contactor) turnOffLine(line, "Profile: dawn - turnOff line according to lux sensor");
}
}
}
//sumrak
if(profile.dusk_lux_sensor == true)
{
let lux_sensor_time_window1 = sunCalcResult.dusk_time - (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60);
let lux_sensor_time_window2 = sunCalcResult.dusk_time + (parseInt( profile.dusk_lux_sensor_time_window ) * 1000 * 60);
if(currentTimestamp >= lux_sensor_time_window1 && currentTimestamp <= lux_sensor_time_window2)
{
if(lux_sensor_value < profile.dusk_lux_sensor_value)
{
if(!contactor) turnOnLine(line, "Profile: dusk - turnOn line according to lux sensor");
}
}
}
}
} catch (error) {
if(profilestr !== "" ) monitor.info('Error parsing profile in turnOnOffLinesAccordingToLuxSensor', error);
}
}
}
async function upateNodeStatus(node, status)
{
//MASTER
if(node == 0) return;
let nodeObj = nodesData[node];
if(nodeObj == undefined) return;
if(status)
{
cmdNOKNodeCounter[node] = 0;
}
else cmdNOKNodeCounter[node]++;
if(nodeObj.status !== status)
{
await dbNodes.modify({ status: status }).where("node", node).make(function(builder) {
builder.callback(function(err, response) {
if(err == null) nodesData[node].status = status;
});
});
}
}
async function runTasks() {
clearInterval(interval);
let currentTimestamp = Date.now();
//report dusk, dawn---------------------------------
if(reportDuskDawn.dusk_time < currentTimestamp)
{
//vyreportuj iba ak nie je velky rozdiel napr. 60 sekund
if( (currentTimestamp - reportDuskDawn.dusk_time) < 60 * 1000)
{
//reportovali sme?
if(reportDuskDawn.dusk_time_reported != sunCalcResult.dusk_time)
{
sendNotification("CMD Manager: calculated Time of dusk", FLOW.OMS_edgeName, "dusk_has_occured", {value: sunCalcResult["dusk"]}, "", SEND_TO.tb, instance);
reportDuskDawn.dusk_time_reported = sunCalcResult.dusk_time;
}
}
var nextDay = new Date();
nextDay.setDate(nextDay.getDate() + 1);
sunCalcResult = calculateDuskDawn(nextDay);
reportDuskDawn.dusk_time = sunCalcResult.dusk_time;
}
if(reportDuskDawn.dawn_time < currentTimestamp)
{
//vyreportuj iba ak nie je velky rozdiel napr. 60 sekund
if( (currentTimestamp - reportDuskDawn.dawn_time) < 60 * 1000)
{
//reportovali sme?
if(reportDuskDawn.dawn_time_reported != sunCalcResult.dawn_time)
{
sendNotification("CMD Manager: calculated Time of dawn", FLOW.OMS_edgeName, "dawn_has_occured", {value: sunCalcResult["dawn"]}, "", SEND_TO.tb, instance);
reportDuskDawn.dawn_time_reported = sunCalcResult.dawn_time;
}
}
var nextDay = new Date();
nextDay.setDate(nextDay.getDate() + 1);
sunCalcResult = calculateDuskDawn(nextDay);
reportDuskDawn.dawn_time = sunCalcResult.dawn_time;
}
//--------------------------------------------------------
//sort tasks based on timestamp
tasks.sort(function (a, b) {
if(a.timestamp <= currentTimestamp && b.timestamp <= currentTimestamp)
{
return a.priority - b.priority;
}
return a.timestamp - b.timestamp;
});
if(tasks.length == 0 )
{
instance.send(SEND_TO.debug, "no tasks created");
interval = setInterval(runTasks, LONG_INTERVAL);
return;
}
if(!rsPort.isOpen)
{
instance.send(SEND_TO.debug, "!rsPort.isOpen");
//await rsPort.open();
}
let currentTask = tasks[0];
if(currentTask.debug)
{
//logger.debug("--->task to process", currentTask);
}
if(currentTask.timestamp <= currentTimestamp)
{
let params = {...tasks[0]};
//allow terminal commands
if(FLOW.OMS_maintenance_mode && params.type !== "cmd-terminal")
{
interval = setInterval(runTasks, LONG_INTERVAL);
return;
}
let type = params.type;
let tbname = params.tbname;
let nodeKey = params.address;
let line = null;
//rpc related
if(nodesData[nodeKey] !== undefined) line = nodesData[nodeKey].line;
if(params.line !== undefined) line = params.line;
let repeatTask = false;
if(params.addMinutesToTimestamp > 0 || params.timePointName) repeatTask = true;
if(repeatTask)
{
if(type === "cmd")
{
//set next start time automatically
tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000;
}
}
else
{
tasks.shift();
}
//custom tasks
if(type == "number_of_luminaires")
{
tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000;
//treba reportovat node status
{
//number_of_luminaires
//number_of_ok_luminaires
//number_of_nok_luminaires
let keys = Object.keys(nodesData);
let number_of_luminaires = keys.length;
let number_of_ok_luminaires = 0;
let number_of_nok_luminaires = 0;
for(let i = 0; i < keys.length; i++)
{
let key = keys[i];
let nodeObj = nodesData[key];
if(nodeObj.tbname == undefined) continue;
if(nodeObj.status) number_of_ok_luminaires++;
else number_of_nok_luminaires++;
}
let values = {
number_of_luminaires: number_of_luminaires,
number_of_ok_luminaires: number_of_ok_luminaires,
number_of_nok_luminaires: number_of_nok_luminaires
};
let dataToTb = {
[FLOW.OMS_edgeName]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
interval = setInterval(runTasks, SHORT_INTERVAL);
return;
}
}
//kontrola nespracovanych profilov nodov
if(type == "process_profiles")
{
tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000;
//select nespracovane nody
//node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
//buildTasks({processLineProfiles: true, line: line});
/*
let keys = Object.keys(nodesData);
for(let i = 0; i < keys.length; i++)
{
let node = keys[i];
let line = node.line;
if(node.processed) continue;
if(relaysData[line] != undefined)
{
let relayStatus = relaysData[line].contactor;
if(relayStatus == 1)
{
//linia je zapnuta
//await loadRelaysData(flowdata.data.line);
}
}
}
*/
//vsetky linie kt. su zapnute, a spracuju sa nespracovane profily nodov
loadRelaysData();
interval = setInterval(runTasks, SHORT_INTERVAL);
return;
}
if(type == "edge_date_time")
{
const ts = Date.now();
let values = {"edge_date_time": ts};
let dataToTb = {
[tbname]: [
{
"ts": ts,
"values": values
}
]
}
tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000;
instance.send(SEND_TO.tb, dataToTb);
interval = setInterval(runTasks, SHORT_INTERVAL);
return;
}
//relay
if(type == "relay")
{
const timePointName = params.timePointName;
const value = params.value;
let date = new Date();
date.setDate(date.getDate() + 1);//next day
let sunCalcResult;
sunCalcResult = calculateDuskDawn(date, params.line);
if(timePointName == "dawn")
{
tasks[0].timestamp = sunCalcResult.dawn_time;
}
else if(timePointName == "dusk")
{
tasks[0].timestamp = sunCalcResult.dusk_time;
}
else if(timePointName == "luxOn")
{
tasks[0].timestamp = sunCalcResult.dusk_time + params.dusk_lux_sensor_time_window * 60000;
}
else if(timePointName == "luxOff")
{
tasks[0].timestamp = sunCalcResult.dawn_time + params.dawn_lux_sensor_time_window * 60000;
}
else if(timePointName == "profileTimepoint")
{
tasks[0].timestamp = currentTimestamp + tasks[0].addMinutesToTimestamp * 60000;
}
let info = "aplikovany bod profilu";
let message = "";
if(value == 1)
{
turnOnLine(params.line, info);
message = "on";
}
else if(value == 0)
{
turnOffLine(params.line, info);
message = "off";
}
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.INFO, "aplikovaný bod profilu línie " + params.line + " - stav: " + message, "", SEND_TO.tb, instance, null );
sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "switching_profile_point_applied_to_line", {line: params.line, value: message}, "", SEND_TO.tb, instance );
interval = setInterval(runTasks, SHORT_INTERVAL);
return;
}
//zhodeny hlavny istic
let disconnected = false;
//if(rotary_switch_state == "Off") disconnected = true;
//state_of_breaker[line] - alebo istic linie
if(state_of_breaker.hasOwnProperty(line))
{
//if(state_of_breaker[line] == "Off") disconnected = true;
}
//toto sa reportuje po prijati dat z dido_controlera
if(disconnected)
{
let values = {"status": "OFFLINE"};
logger.debug("disconnected", values);
logger.debug("rotary_switch_state", rotary_switch_state);
logger.debug("state_of_breaker", state_of_breaker[line]);
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//report only once!
if(!disconnectedReport.hasOwnProperty(tbname)) disconnectedReport[tbname] = false;
if(!disconnectedReport[tbname])
{
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
}
interval = setInterval(runTasks, SHORT_INTERVAL);
return;
}
disconnectedReport[tbname] = false;
//high_priority
if(!FLOW.OMS_masterNodeIsResponding)
{
//ak neodpoveda, nebudeme vykonavat ziadne commands, okrem cmd-terminal, a fw version
errorHandler.sendMessageToService("Master node is not responding");
let stop = true;
if(params.type == "cmd-terminal") stop = false;
//fw version - register == 4
if(params.type == "cmd" && params.register == 4 && params.address == 0) stop = false;
if(stop)
{
interval = setInterval(runTasks, LONG_INTERVAL);
return;
}
}
let relayStatus = 1;
if(relaysData[line] != undefined)
{
relayStatus = relaysData[line].contactor;
}
if(line == 0) relayStatus = 0;
if(params.type == "cmd-terminal") relayStatus = 1;
//check if rotary_switch_state == "Off"
if(relayStatus == 0)
{
//console.log("------------------------------------relayStatus", relayStatus, line);
let values = {"status": "OFFLINE"};
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
interval = setInterval(runTasks, SHORT_INTERVAL);
return;
}
if(!rsPort.isOpen)
{
interval = setInterval(runTasks, LONG_INTERVAL);
return;
}
//RE-CALCULATE VALUES
//set actual time for broadcast
if(params.register == 87 && params.recipient === 2)
{
var d = new Date();
let hours = d.getHours();
let minutes = d.getMinutes();
let seconds = d.getSeconds();
params.byte1 = hours;//h
params.byte2 = minutes;//m
params.byte3 = seconds;//s
params.byte4 = 0;
}
//SET DUSK/DAWN FOR BROADCAST
//Time of dusk
if(params.register == 6 && params.recipient === 2)
{
if(params.type != "cmd-terminal")
{
let sunCalcResult = calculateDuskDawn();
let dusk_hours = sunCalcResult["dusk_hours"];
let dusk_minutes = sunCalcResult["dusk_minutes"];
params.byte1 = dusk_hours;//h
params.byte2 = dusk_minutes;//m
params.byte3 = 0;//s
params.byte4 = 0;
//TODO astrohodiny
let dusk = "Time of dusk: " + sunCalcResult["dusk"];
//sendNotification("CMD Manager: calculated Time of dusk", relaysData[0].tbname, ERRWEIGHT.INFO, dusk, "", SEND_TO.tb, instance, null );
}
}
//Time of dawn
if(params.register == 7 && params.recipient === 2)
{
if(params.type != "cmd-terminal")
{
let sunCalcResult = calculateDuskDawn();
let dawn_hours = sunCalcResult["dawn_hours"];
let dawn_minutes = sunCalcResult["dawn_minutes"];
params.byte1 = dawn_hours;//h
params.byte2 = dawn_minutes;//m
params.byte3 = 0;//s
params.byte4 = 0;
//TODO astrohodiny
let dawn = "Time of dawn: " + sunCalcResult["dawn"];
//sendNotification("CMD Manager: calculated Time of dusk", relaysData[0].tbname, ERRWEIGHT.INFO, dawn, "", SEND_TO.tb, instance, null );
}
}
//-----------------------
let register = params.register;
instance.send(SEND_TO.debug, "address: " + params.address + " register:" + params.register + "type: " + params.type);
var startTime, endTime;
startTime = new Date();
let resp = com_generic(params.address, params.recipient, params.rw, params.register, params.name, params.byte1, params.byte2, params.byte3, params.byte4);
let readBytes = 11;
let timeout = 5000;
await writeData(rsPort, resp, readBytes, timeout).then(function (data) {
endTime = new Date();
var timeDiff = endTime - startTime;
//--1-4 adresa, 5 status ak je status 0 - ok, nasleduju 4 byty data
//let bytes = data.slice(0);
let bytes = data;
let dataBytes = data.slice(5,9);
let result = detectIfResponseIsValid(bytes);
let message = result.message;
let type = result.type;
let error = result.error;
//ak sa odpoved zacina 0 - je to v poriadku, inak je NOK
if(params.debug != "generated cmd")
{
//debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug, params);
}
if(params.hasOwnProperty("debug"))
{
if(params.debug)
{
console.log("detected response:", result);
logger.debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug, params, result);
}
}
//debug("writeData: done " + type + " duration: " + timeDiff + " type: " + params.debug);
//debug("writeData done", type, "duration", timeDiff, "type", params.debug, result);
let tbname = params.tbname;
let saveToTb = true;
if(tbname == null || tbname == undefined || tbname == "") saveToTb = false;
//--
//CMD FINISHED
if(message == "OK")
{
upateNodeStatus(params.address, true);
//write
if(params.type == "set_node_profile")
{
let result = cmdCounterResolve(params.address);
if(result == 0)
{
dbNodes.modify({ processed: true }).where("node", params.address).make(function(builder) {
builder.callback(function(err, response) {
sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "dimming_profile_was_successfully_received_by_node", {node: params.address}, "", SEND_TO.tb, instance );
logger.debug( "--> profil úspešne odoslaný na node č. " + params.address);
nodesData[params.address].processed = true;
});
});
}
}
//parse read response
let values = {};
if(params.rw == 0) {
values = processResponse(register, dataBytes);//read
}
if(params.rw == 1)
{ //write command
//set command dimming
if(params.register == 1) values = {"comm_status": message};
}
if(params.register == 0) values["status"] = message;
//fw version - register == 4
if(params.register == 4) values["edge_fw_version"] = FLOW.OMS_edge_fw_version;
if(params.address == 0)
{
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.NOTICE, "Master node is working again", "", SEND_TO.tb, instance, "rvo_status" );
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" );
sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_responding_again", {}, "", SEND_TO.tb, instance, "rvo_status" );
FLOW.OMS_masterNodeIsResponding = true;
}
//odoslanie príkazu z terminálu - dáta
if(params.type == "cmd-terminal")
{
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.DEBUG, "odoslanie príkazu z terminálu", params, SEND_TO.tb, instance, null );
sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "command_was_sent_from_terminal_interface", {}, params, SEND_TO.tb, instance );
}
if(params.debug)
{
logger.debug("saveToTb", saveToTb, tbname, values);
}
if(saveToTb)
{
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
}
else
{
if(params.type == "cmd-terminal")
{
if(params.refFlowdataKey != undefined)
{
logger.debug("cmd-terminal SUCCESS");
logger.debug(currentTask);
//make http response
let responseObj = {};
responseObj["type"] = "SUCESS";
responseObj["bytes"] = data;
//params.refFlowdata.data = responseObj;
//instance.send(SEND_TO.http_response, params.refFlowdata);
let refFlowdata = refFlowdataObj[ params.refFlowdataKey ];
refFlowdata.data = responseObj;
instance.send(SEND_TO.http_response, refFlowdata);
}
else
{
console.log("params.refFlowdataKey is undefined", params);
}
}
}
}
else
{
upateNodeStatus(params.address, false);
if(params.refFlowdataKey != undefined)
{
logger.debug("cmd-terminal FAILED");
logger.debug(currentTask);
//make http response
let responseObj = {};
responseObj["type"] = "ERROR";
responseObj["bytes"] = data;
//params.refFlowdata.data = responseObj;
//instance.send(SEND_TO.http_response, params.refFlowdata);
let refFlowdata = refFlowdataObj[ params.refFlowdataKey ];
if(refFlowdata !== undefined)
{
refFlowdata.data = responseObj;
instance.send(SEND_TO.http_response, refFlowdata);
}
}
/*
if(params.type == "cmd-terminal")
{
if(params.refFlowdata != undefined)
{
logger.debug("cmd-terminal FAILED");
logger.debug(currentTask);
//make http response
let responseObj = {};
responseObj["type"] = "ERROR";
responseObj["bytes"] = data;
params.refFlowdata.data = responseObj;
instance.send(SEND_TO.http_response, params.refFlowdata);
}
}
*/
if(params.address == 0)
{
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", SEND_TO.tb, instance, "rvo_status");
sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status");
logger.debug("master_node_is_not_responding", params);
FLOW.OMS_masterNodeIsResponding = false;
}
if(params.type == "set_node_profile")
{
delete cmdCounter[params.address];
let tbname = nodesData[ params.address ].tbname;
logger.debug( "profil nebol úspešne odoslaný na node č. ", params, result, resp);
//sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "profil nebol úspešne odoslaný na node č. " + params.address, "", SEND_TO.tb, instance, null );
sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", SEND_TO.tb, instance );
}
//is it node?
if(nodesData.hasOwnProperty(params.address))
{
if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false;
}
//Master node version
//if(params.register == 4 && saveToTb)
if(saveToTb)
{
let values = {
"status": "NOK"
};
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
}
//instance.send(SEND_TO.debug, result);
if(params.hasOwnProperty("debug"))
{
if(params.debug)
{
logger.debug("writeData err: ", error, result, params);
}
}
//logger.debug(error, result, params);
}
}).catch(function (reason) {
console.log("writeData catch exception", reason);
logger.debug(currentTask);
if(params.refFlowdataKey != undefined)
{
logger.debug("catch: cmd-terminal FAILED");
logger.debug(currentTask);
//make http response
let responseObj = {};
responseObj["type"] = "ERROR";//
responseObj["message"] = "ERROR WRITE FAILED: " + reason;//
//params.refFlowdata.data = responseObj;
//instance.send(SEND_TO.http_response, params.refFlowdata);
let refFlowdata = refFlowdataObj[ params.refFlowdataKey ];
if(refFlowdata !== undefined)
{
refFlowdata.data = responseObj;
instance.send(SEND_TO.http_response, refFlowdata);
}
}
/*
if(params.type == "cmd-terminal")
{
if(params.refFlowdata != undefined)
{
logger.debug("cmd-terminal FAILED");
logger.debug(currentTask);
//make http response
let responseObj = {};
responseObj["type"] = "ERROR WRITE FAILED: " + reason;
//responseObj["bytes"] = data;
params.refFlowdata.data = responseObj;
instance.send(SEND_TO.http_response, params.refFlowdata);
//refFlowdata = undefined;
}
}
*/
if(params.hasOwnProperty("debug"))
{
if(params.debug)
{
logger.debug("-->WRITE FAILED: " + reason, params.debug, params);
}
}
upateNodeStatus(params.address, false);
let tbname = params.tbname;
let saveToTb = true;
if(tbname == null || tbname == undefined || tbname == "") saveToTb = false;
if(params.address == 0)
{
//sendNotification("CMD Manager: process cmd", relaysData[0].tbname, ERRWEIGHT.ALERT, "Master node not responding", "", SEND_TO.tb, instance, "rvo_status");
sendNotification("CMD Manager: process cmd", FLOW.OMS_edgeName, "master_node_is_not_responding", {}, "", SEND_TO.tb, instance, "rvo_status");
logger.debug("master_node_is_not_responding", params);
FLOW.OMS_masterNodeIsResponding = false;
}
if(params.type == "set_node_profile")
{
delete cmdCounter[params.address];
let tbname = nodesData[ params.address ].tbname;
logger.debug( "profil nebol úspešne odoslaný na node č. ", params, resp);
//sendNotification("CMD Manager: process cmd", tbname, ERRWEIGHT.ALERT, "odosielanie profilu na node č. " + params.address + " zlyhalo", "", SEND_TO.tb, instance, null );
sendNotification("CMD Manager: process cmd", tbname, "configuration_of_dimming_profile_to_node_failed", {node: params.address}, "", SEND_TO.tb, instance );
}
//is it node?
if(nodesData.hasOwnProperty(params.address))
{
if(cmdNOKNodeCounter[params.address] < 5) saveToTb = false;
}
//Master node version
if(params.register == 4 && saveToTb)
{
let values = {
"status": "NOK",
"master_node_version": "NOK"
};
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
FLOW.OMS_masterNodeIsResponding = false;
}
//treba?
/*
else if(saveToTb)
{
let values = {
"comm_status": "no_comm"
};
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
instance.send(SEND_TO.tb, dataToTb);
}
*/
instance.send(SEND_TO.debug, reason);
});
}
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);
}
//! rsPort LM = "/dev/ttymxc4", rsPort UNIPI = "/dev/ttyUSB0"
// const rsPort = new SerialPort("/dev/ttymxc4", { autoOpen: false }); //LM
// const rsPort = new SerialPort("/dev/ttyUSB0", { autoOpen: false }); // UNIPI
if(FLOW.OMS_serial_port == "" || FLOW.OMS_serial_port == undefined || FLOW.OMS_serial_port.length === 1) FLOW.OMS_serial_port = "ttymxc4";
const rsPort = new SerialPort(`/dev/${FLOW.OMS_serial_port}`, { autoOpen: false });
//(node:16372) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 13 data listeners added to [SerialPort]. Use emitter.setMaxListeners() to increase limit
//rsPort.setMaxListeners(0);
rsPort.on('open', async function() {
logger.debug("CMD manager - rsPort opened sucess");
loadRelaysData();
await runSyncExec(`stty -F /dev/${FLOW.OMS_serial_port} 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke`).then(function (status) {
instance.send(SEND_TO.debug, "RPC runSyncExec - Promise Resolved:" + status);
logger.debug(0, "RPC runSyncExec - Promise Resolved:" + status);
//APP START
let dataToInfoSender = {id: FLOW.OMS_projects_id, name: FLOW.OMS_rvo_name};
dataToInfoSender.fw_version = FLOW.OMS_edge_fw_version;
dataToInfoSender.startdate = new Date().toISOString().slice(0, 19).replace('T', ' ');
dataToInfoSender.__force__ = true;
instance.send(SEND_TO.infoSender, dataToInfoSender);
logger.debug(0, "---------------------------->START message send to service", dataToInfoSender);
}).catch(function (reason) {
instance.send(SEND_TO.debug, "CMD manager - RPC runSyncExec - promise rejected:" + reason);
});
});
rsPort.on('error', function(err) {
//TODO report to service!!!
//errLogger.error(exports.title, "unable to open port", FLOW.OMS_serial_port, err.message);
errorHandler.sendMessageToService([exports.title, "unable to open port", FLOW.OMS_serial_port, err.message], 0);
instance.send(SEND_TO.debug, err.message);
});
rsPort.on("close", () => {
rsPort.close();
});
//loadRelaysData();
rsPort.open();
instance.on("close", () => {
clearInterval(interval);
rsPort.close();
});
instance.on("data", async function(flowdata) {
//instance.send(SEND_TO.debug, "on Data");
//instance.send(SEND_TO.debug, flowdata);
//logger.debug(flowdata.data);
//just testing functions
if(flowdata.data == "open")
{
if(!rsPort.isOpen) rsPort.open();
return;
}
else if(flowdata.data == "close")
{
rsPort.close();
return;
}
else if(flowdata.data == "clean")
{
tasks = [];
return;
}
else if(flowdata.data == "buildtasks")
{
//build & run
return;
}
else if(flowdata.data == "run")
{
//durations = [];
if(tasks.length == 0)
{
buildTasks();
if(rsPort.isOpen)
{
interval = setInterval(runTasks, 100);
}
else
{
instance.send(SEND_TO.debug, "port is not opened!!!");
}
}
}
else
{
//terminal data - object
//logger.debug("flowdata", flowdata.data);
if(typeof flowdata.data === 'object')
{
//logger.debug("dido", flowdata.data);
if(flowdata.data.hasOwnProperty("sender"))
{
//data from dido_controller
if(flowdata.data.sender == "dido_controller")
{
if(flowdata.data.hasOwnProperty("cmd"))
{
let cmd = flowdata.data.cmd;
if(cmd == "buildTasks")
{
clearInterval(interval);
logger.debug("-->CMD MANAGER - BUILD TASKS");
buildTasks();
//logger.debug("tasks:");
//logger.debug(tasks);
logger.debug("-->CMD MANAGER - RUN TASKS");
interval = setInterval(runTasks, LONG_INTERVAL);
}
else if(cmd == "reload_relays")
{
loadRelaysData(flowdata.data.line);
if(flowdata.data.dataChanged)
{
if(!flowdata.data.value)
{
reportOfflineNodeStatus(flowdata.data.line);
}
else
{
reportOnlineNodeStatus(flowdata.data.line);
}
}
}
else if(cmd == "rotary_switch_state")
{
//state was changed
if(rotary_switch_state != flowdata.data.value)
{
if(rotary_switch_state == "Off")
{
//vyreportovat vsetky svietdla
reportOfflineNodeStatus();
}
else reportOnlineNodeStatus();
}
rotary_switch_state = flowdata.data.value;
}
else if(cmd == "lux_sensor")
{
lux_sensor = parseInt(flowdata.data.value);
// POSSIBLE SOURCE OF PROBLEMS, IF USER SETS LUX TRESHOLD LEVEL GREATER THAN 100 - WE SHOULD BE CHECKING "DUSK/DAWN_LUX_SENSOR_VALUE" IN PROFILE MAYBE ??
if(lux_sensor < 100)
{
// we send lux_sensor value to all nodes:
let params = getParams(PRIORITY_TYPES.node_broadcast);
params.recipient = 2;//2 broadcast, address = 0
params.address = 0xffffffff;//Broadcast
let ba = longToByteArray(lux_sensor);
params.byte3 = ba[1];//msb
params.byte4 = ba[0];
params.timestamp = PRIORITY_TYPES.node_broadcast;
params.info = "run broadcast: Actual Lux level from cabinet";
params.register = 95;//Actual Lux level from cabinet
params.rw = 1;//write
tasks.push(params);
//process profiles
turnOnOffLinesAccordingToLuxSensor(lux_sensor);
}
}
else if(cmd == "state_of_breaker")
{
//istic linie
let value = flowdata.data.value;
let line = parseInt(flowdata.data.line);
let dataChanged = false;
if(state_of_breaker[line] != value) dataChanged = true;
state_of_breaker[line] = value;
let status = "OK";
let weight = ERRWEIGHT.NOTICE;
let message = `zapnutý istič línie č. ${line}`;
if(value == "Off")
{
weight = ERRWEIGHT.ERROR;
message = `vypnutý istič línie č. ${line}`;
status = "NOK";
}
if(dataChanged) {
if(relaysData.hasOwnProperty(line))
{
let tbname = relaysData[line].tbname;
if(value == "Off") sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_off_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker");
else sendNotification("CMD Manager: onData", tbname, "circuit_breaker_was_turned_on_line", {line: line}, "", SEND_TO.tb, instance, "circuit_breaker");
//report status liniu
let values = {
"status": status
};
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//instance.send(SEND_TO.tb, dataToTb);
tbHandler.sendToTb(dataToTb, instance);
//current value
if(value == "Off")
{
//vyreportovat vsetky svietdla na linii
reportOfflineNodeStatus(line);
}
else reportOnlineNodeStatus(line);
}
}
}
else{
logger.debug("undefined cmd", cmd);
}
}
}
return;
}
//data from worksys
if(flowdata.data.hasOwnProperty("topic"))
{
let data = flowdata.data.content.data;
let command = data.params.command;
let method = data.method;
let profile = data.params.payload;
if(profile == undefined) profile = "";
let entity = data.params.entities[0];
let entity_type = entity.entity_type;
let tbname = entity.tb_name;
instance.send(SEND_TO.debug, flowdata.data);
logger.debug("--->worksys", flowdata.data, data.params, entity, entity_type, command, method);
logger.debug("----------------------------");
if(entity_type == "street_luminaire"|| entity_type === "street_luminaire_v4_1" || entity_type === "street_luminaire_v4_1cez" || entity_type === "street_luminaire_v4")
{
if(method == "set_command")
{
//let command = data.params.command;
let value = data.params.payload.value;
if(command == "dimming")
{
let nodeWasFound = false;
let keys = Object.keys(nodesData);
//logger.debug("-----", keys);
for(let i = 0; i < keys.length; i++)
{
let node = keys[i];
//logger.debug( node, nodesData[node], tbname);
if(tbname == nodesData[node].tbname.trim())
{
let params = getParams(PRIORITY_TYPES.high_priority);
value = parseInt(value);
if(value > 0) value = value + 128;
//set dimming - LUM1_13 - 647 je node linie 1 kt. dobre vidime
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 1;//dimming
params.recipient = 1;//slave
params.byte4 = value;
params.rw = 1;//write
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'set dimming from platform';
//params.debug = true;
//ak linia je
//debug(params);
logger.debug("dimming", params);
tasks.push(params);
setTimeout(function(){
//spustime o 4 sekundy neskor, s prioritou PRIORITY_TYPES.high_priority
//a pridame aj vyreportovanie dimmingu
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 1;//dimming
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read dimming (after set dimming from platform)';
params.debug = true;
tasks.push(params);
}
//pridame aj vyreportovanie - vykon
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 76;
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read Input Power (after set dimming from platform)';
params.debug = true;
tasks.push(params);
}
//pridame aj vyreportovanie - prud svietidla
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 75;
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read Input Current (after set dimming from platform)';
params.debug = true;
tasks.push(params);
}
//pridame aj vyreportovanie - power faktor - ucinnik
{
let params = getParams(PRIORITY_TYPES.high_priority);
params.type = "cmd";
params.tbname = tbname;
params.address = node;
params.register = 77;
params.recipient = 1;//slave
params.rw = 0;//read
params.timestamp = PRIORITY_TYPES.high_priority;
params.info = 'read power factor - Cos phi (after set dimming from platform)';
params.debug = true;
tasks.push(params);
}
},4000);
nodeWasFound = true;
break;
}
}
if(!nodeWasFound)
{
logger.debug("set dimming from platform", "unable to find tbname", tbname);
}
}
else
{
instance.send(SEND_TO.debug, "undefined command " + command);
logger.debug("undefined command", command);
}
return;
}
else if(method == "set_profile")
{
//nastav profil nodu
logger.debug("-->set_profile for node", data.params);
logger.debug("------profile data", profile);
//instance.send(SEND_TO.debug, "set_profile" + command);
let keys = Object.keys(nodesData);
for(let i = 0; i < keys.length; i++)
{
let node = keys[i];
if(tbname == nodesData[node].tbname.trim())
{
if(profile != "") profile = JSON.stringify(profile);
dbNodes.modify({ processed: false, profile: profile }).where("node", node).make(function(builder) {
builder.callback(function(err, response) {
logger.debug("worksys - update node profile done", profile);
if(profile === "") logger.debug("worksys - update node profile done - profile is empty");
//profil úspešne prijatý pre node č. xx
//sendNotification("CMD manager", tbname, ERRWEIGHT.INFO, `profil úspešne poslaný z platformy na RVO pre node č. ${node}`, profile, SEND_TO.tb, instance, null );
sendNotification("CMD manager", tbname, "dimming_profile_was_processed_for_node", {node: node}, profile, SEND_TO.tb, instance );
nodesData[node].processed = false;
nodesData[node].profile = profile;
let line = nodesData[node].line;
processNodeProfile(node);
});
});
}
}
}
else
{
instance.send(SEND_TO.debug, "unknown method " + method);
logger.debug("unknown method", method);
return;
}
}
//nastav profil linie z platformy
else if(entity_type == "edb_line" || entity_type == "edb" || entity_type == "edb_line_ver4" || entity_type == "edb_ver4_se")
{
//profil linie
//relays.table line:number|tbname:string|contactor:number|profile:string
//najdeme line relaysData
if(method == "set_profile")
{
logger.debug("-->set_profile for line", data.params);
logger.debug("profile data:", profile);
let keys = Object.keys(relaysData);
for(let i = 0; i < keys.length; i++)
{
let line = keys[i];
if(tbname == relaysData[line].tbname)
{
//zmazeme tasky
removeTask({type: "relay", line: line});
if(profile != "") profile = JSON.stringify(profile);
dbRelays.modify({ profile: profile }).where("line", line).make(function(builder) {
builder.callback(function(err, response) {
//update profile
logger.debug("worksys - update relay profile done:", profile);
instance.send(SEND_TO.debug, "worksys - update relay profile done");
loadRelaysData(line).then(function (data) {
logger.debug("loadRelaysData DONE for line", line);
buildTasks({processLineProfiles: true, line: line});
});
sendNotification("CMD manager - set profile from worksys", tbname, "switching_profile_was_processed_for_line", {line: line}, profile, SEND_TO.tb, instance );
});
});
break;
}
}
}
else if(method == "set_command")
{
let value = data.params.payload.value;
if(command === "switch")
{
// if we receive rpc from platform, to switch maintenance mode, we set OMS_maintenance_mode flow variable to value;
if(entity_type === "edb" || entity_type === "edb_ver4_se") FLOW.variables.OMS_maintenance_mode = value;
let responseRelays = await promisifyBuilder(dbRelays.find().where("tbname", tbname));
let line = 0;
if(responseRelays.length == 1) line = responseRelays[0].line;
if(value == false) turnOffLine(line, "command received form platform");
else turnOnLine(line, "command received form platform");
}
}
else
{
instance.send(SEND_TO.debug, "undefined method " + method);
logger.debug("undefined method", method);
}
return;
}
else
{
instance.send(SEND_TO.debug, "UNKNOW entity_type " + entity_type);
logger.debug("UNKNOW entity_type", entity_type);
}
return;
}
//terminal
if(!rsPort.isOpen) await rsPort.open();
let params = flowdata.data.body;
if(params == undefined)
{
//logger.debug("CMD manager flowdata.data.body is undefined");
return;
}
params.priority = PRIORITY_TYPES.terminal;
params.type = "cmd-terminal";
params.tbname = "";
params.timestamp = PRIORITY_TYPES.terminal;
params.addMinutesToTimestamp = 0;// do not repeat task!!!
params.debug = true;
let timestamp = Date.now();
params.refFlowdataKey = timestamp;
//params.refFlowdata = flowdata;
//refFlowdata = flowdata;
//console.log("flowdata", flowdata);
cleanUpRefFlowdataObj();
refFlowdataObj[ timestamp ] = flowdata;
//fix
//params.address = params.adress;
logger.debug("received from terminal", params);
logger.debug("date/time:", new Date());
logger.debug("tasks length:", tasks.length);
//tasks = [];
//add to tasks
tasks.push(params);
}
}
})
} // end of instance.export
/**
* setCorrectTime function runs once per hour
* If it is 3 o'clock, it sets actual time, which is got from services
* https://service-prod01.worksys.io/gettime
* If also detects Read Only Filesystem once a day
*/
function setCorrectPlcTimeOnceADay()
{
const currentTime = new Date();
if(currentTime.getHours() != 3) return;
RESTBuilder.make(function(builder) {
if(!builder) return;
builder.method('GET');
builder.url('http://192.168.252.2:8004/gettime?projects_id=1');
builder.callback(function(err, response, output) {
if (err) {
console.log(err);
return;
}
const res = output.response;
try {
const obj = JSON.parse(res);
let d = new Date(obj.date);
const now = new Date();
let diffInMinutes = now.getTimezoneOffset();
console.log("---->TimezoneOffset", diffInMinutes);
if(d instanceof Date)
{
// monitor.info("----------->setCorrectPlcTimeOnceADay() current js date:", d, d.getHours());
let year = d.getFullYear();
let month = addZeroBefore(d.getMonth() + 1);
let day = addZeroBefore(d.getDate());
let hours = addZeroBefore(d.getHours());
let minutes = addZeroBefore(d.getMinutes() );
let seconds = addZeroBefore(d.getSeconds());
let dateStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
exec(`sudo timedatectl set-time "${dateStr}"`, (err, stdout, stderr) => {
if (err || stderr) {
console.error(err);
console.log(stderr);
console.log(dateStr);
monitor.info("failed timedatectl set-time", err, stderr);
}
else
{
monitor.info("setCorrectPlcTimeOnceADay() --> Nastaveny cas na: ", dateStr);
}
});
}
} catch (error) {
logger.debug("setCorrectPlcTimeOnceADay - function error", error, res);
monitor.info("setCorrectPlcTimeOnceADay - function error", error, res);
}
// we detect readOnlyFileSystem once an hour as well
detectReadOnlyFilesystem();
});
});
}
function detectReadOnlyFilesystem()
{
exec(`sudo egrep " ro,|,ro " /proc/mounts`, (err, stdout, stderr) => {
if (err || stderr) {
console.error(err);
console.log(stderr);
} else {
//console.log("Read-only", stdout);
let lines = stdout + "";
lines = lines.split("\n");
let readOnlyDetected = "";
for(let i = 0; i < lines.length; i++)
{
if(lines[i].startsWith("/dev/mmcblk0p2"))
{
readOnlyDetected = lines[i];
}
}
if(readOnlyDetected !== "")
{
errorHandler.sendMessageToService("Detected: Read-only file system: " + readOnlyDetected);
monitor.info("Read only filesystem detected");
}
}
});
}
let setCorrectTime = setInterval(setCorrectPlcTimeOnceADay, 60000 * 60); // 1 hour
setCorrectPlcTimeOnceADay();
///helper functions
function calculateDuskDawn(date, line, duskOffset = 0, dawnOffset = 0)
{
if(date === undefined) date = new Date();
//if(duskOffset === undefined) duskOffset = 0;
//if(dawnOffset === undefined) dawnOffset = 0;
//let line = keys[i];
let profilestr = "";
if(relaysData[line] != undefined) profilestr = relaysData[line].profile;
let result = {};
var times = SunCalc.getTimes(date, latitude, longitude);
let dawn = new Date(times.sunrise);//usvit
let dusk = new Date(times.sunset);//sumrak
//http://suncalc.net/#/48.5598,18.169,11/2021.04.07/11:06
//https://mapa.zoznam.sk/zisti-gps-suradnice-m6
let dusk_astro_clock_offset = duskOffset;//minutes
let dawn_astro_clock_offset = dawnOffset;//minutes
try {
let profile = JSON.parse(profilestr);
if(Object.keys(profile).length === 0) throw ("profile is not defined");
//Jednoduchý režim
if(profile.astro_clock == false && profile.dusk_lux_sensor == false && profile.dawn_lux_sensor == false)
{
}
//Režim astrohodín
if(profile.astro_clock == true)
{
//if(profile.dusk_lux_sensor == false)
{
if(profile.hasOwnProperty("dusk_astro_clock_offset")) dusk_astro_clock_offset = parseInt( profile.dusk_astro_clock_offset );
}
//if(profile.dawn_lux_sensor == false)
{
if(profile.hasOwnProperty("dawn_astro_clock_offset")) dawn_astro_clock_offset = parseInt( profile.dawn_astro_clock_offset );
}
}
//dusk - súmrak
//down, sunrise - svitanie
} catch (error) {
if(profilestr != "")
{
logger.debug(profilestr);
logger.debug(error);
}
}
result.dusk_no_offset = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes());
result.dawn_no_offset = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes());
dusk = new Date(dusk.getTime() + gmtOffset + dusk_astro_clock_offset*60000);
dawn = new Date(dawn.getTime() + gmtOffset + dawn_astro_clock_offset*60000);
result.dusk = addZeroBefore(dusk.getHours()) + ":" + addZeroBefore(dusk.getMinutes());
result.dusk_hours = dusk.getHours();
result.dusk_minutes = dusk.getMinutes();
result.dawn = addZeroBefore(dawn.getHours()) + ":" + addZeroBefore(dawn.getMinutes());
result.dawn_hours = dawn.getHours();
result.dawn_minutes = dawn.getMinutes();
result.dusk_time = dusk.getTime();
result.dawn_time = dawn.getTime();
result.dusk_astro_clock_offset = dusk_astro_clock_offset;
result.dawn_astro_clock_offset = dawn_astro_clock_offset;
return result;
}
function processResponse(register, bytes)
{
let values = {};
let byte3 = bytes[0];
let byte2 = bytes[1];
let byte1 = bytes[2];
let byte0 = bytes[3];
//status
if(register == 0)
{
let statecode = bytesToInt(bytes);
values = {"statecode": statecode};
return values;
}
//Dimming, CCT
if(register == 1)
{
let brightness = 0;
let dimming = byte0;
if(dimming > 128) {
//dimming = -128;
brightness = dimming - 128;
}
//cct
//Ak Byte3 == 1: CCT = (Byte2*256)+Byte1
let cct;
if(byte3 == 1) cct = byte2*256 + byte1;
else cct = bytesToInt(bytes.slice(0, 3));
//cct podla auditu
values["dimming"] = brightness;
return values;
}
//
if(register == 4)
{
values["master_node_version"] = bytes[1] + "." + bytes[2];
//logger.debug("FW Version", register, bytes);
}
//Napätie
if(register == 74)
{
let voltage = (bytesToInt(bytes) * 0.1).toFixed(1);
values["voltage"] = Number(voltage);
}
//Prúd
if(register == 75)
{
let current = bytesToInt(bytes);
values["current"] = current;
}
//výkon
if(register == 76)
{
let power = (bytesToInt(bytes) * 0.1).toFixed(2);
values["power"] = Number(power);
}
//účinník
if(register == 77)
{
let power_factor = Math.cos(bytesToInt(bytes) * 0.1).toFixed(2);
values["power_factor"] = Number(power_factor);
}
//frekvencia
if(register == 78)
{
let frequency = (bytesToInt(bytes) * 0.1).toFixed(2);
values["frequency"] = Number(frequency);
}
//energia
if(register == 79)
{
let energy = bytesToInt(bytes);
//Energiu treba reportovať v kWh. Teda číslo, ktoré príde treba podeliť 1000. Toto som ti možno zle napísal.
values["energy"] = energy / 1000;
}
//doba života
if(register == 80)
{
let lifetime = ( bytesToInt(bytes) / 60).toFixed(2);
values["lifetime"] = Number(lifetime);
}
//nastavenie profilu
if(register == 8)
{
let time_schedule_settings = bytesToInt(bytes);
values["time_schedule_settings"] = time_schedule_settings;
}
//skupinová adresa 1
if(register == 3)
{
let gr_add_1 = bytesToInt(byte0);
values["gr_add_1"] = gr_add_1;
let gr_add_2 = bytesToInt(byte1);
values["gr_add_2"] = gr_add_2;
let gr_add_3 = bytesToInt(byte2);
values["gr_add_3"] = gr_add_3;
let gr_add_4 = bytesToInt(byte3);
values["gr_add_4"] = gr_add_4;
}
//naklon
if(register == 84)
{
let temp;
if(byte3 >= 128)
{
temp = (byte3 - 128) * (-1);
}
else
{
temp = byte3;
}
let inclination_x;
if(byte2 >= 128)
{
inclination_x = (byte2 - 128) * (-1);
}
else
{
inclination_x = byte2;
}
let inclination_y;
if(byte1 >= 128)
{
inclination_y = (byte1 - 128) * (-1);
}
else
{
inclination_y = byte1;
}
let inclination_z;
if(byte0 >= 128)
{
inclination_z = (byte0 - 128) * (-1);
}
else
{
inclination_z = byte0;
}
values["temperature"] = temp;
//náklon x
values["inclination_x"] = inclination_x;
//náklon y
values["inclination_y"] = inclination_y;
//náklon z
values["inclination_z"] = inclination_z;
}
let h = byte3;
let m = byte2;
let s = byte1;
let timestamp;
if(register == 87 || register == 6 || register == 7 )
{
//if(byte3 < 10) h = "0" + byte3;
//if(byte2 < 10) m = "0" + byte2;
//if(byte1 < 10) s = "0" + byte1;
var d = new Date();
d.setHours(h);
d.setMinutes(m);
d.setSeconds(s);
timestamp = d.getTime();
}
//aktuálny čas
if(register == 87)
{
//Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek.
//values["actual_time"] = h + ":" + m + ":" + s;
values["actual_time"] = timestamp;
}
//čas súmraku
if(register == 6)
{
//Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek.
//values["dusk_time"] = h + ":" + m + ":" + s;
values["dusk_time"] = timestamp;
}
//čas úsvitu
if(register == 7)
{
//Byte3 - hodiny, Byte 2 - minúty, Byte 1 -sek.
//values["dawn_time"] = h + ":" + m + ":" + s;
values["dawn_time"] = timestamp;
}
//FW verzia
if(register == 89)
{
//formát: "Byte3: Byte2.Byte1 (Byte0)"
values["fw_version"] = byte3 + ":" + byte2 + "." + byte1 + "(" + byte0 + ")";
}
return values;
}
//byte1 MSB = data3, byte2 = data2, byte3 = data1, byte4 = data0 LSB
function com_generic(adresa, rec, rw, register, name, byte1, byte2, byte3, byte4) {
let resp = [];
let cmd = register;
if (typeof adresa === 'string') adresa = parseInt(adresa);
if (typeof byte1 === 'string') byte1 = parseInt(byte1);
if (typeof byte2 === 'string') byte2 = parseInt(byte2);
if (typeof byte3 === 'string') byte3 = parseInt(byte3);
if (typeof byte4 === 'string') byte4 = parseInt(byte4);
if (rw === 0)
{
cmd = cmd + 0x8000;
}
//master
if(rec === 0) adresa = 0;
if(rec === 2)
{
adresa = 0xffffffff;//Broadcast
}
//recipient
if (rec === 3)
{
resp.push(0xFF);
resp.push(0xFF);
resp.push(0xFF);
resp.push(0xFF);
resp.push( adresa & 0xFF );//band
}
else
{
resp.push( (adresa >> 24) & 0xFF);//rshift
resp.push( (adresa >> 16) & 0xFF);
resp.push( (adresa >> 8) & 0xFF);
resp.push( adresa & 0xFF );
if (rec === 2)
{
resp.push(0xFF);
}
else resp.push(0);
}
resp.push( (cmd >> 8) & 0xFF);//rshift
resp.push( cmd & 0xFF );//band
resp.push( byte1 & 0xFF );//band
resp.push( byte2 & 0xFF );//band
resp.push( byte3 & 0xFF );//band
resp.push( byte4 & 0xFF );//band
//let data = '12345';
let crc = crc16('ARC', resp);
let c1 = (crc >> 8) & 0xFF;
let c2 = crc & 0xFF;
resp.push(c1);
resp.push(c2);
//logger.debug("checksum", crc);
//logger.debug("resp", resp);
return resp;
}
// SAMPLE DATA
const relaysDataExample =
{
'0': {
line: 0,
tbname: 'jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV',
contactor: 1,
profile: ''
},
'1': {
line: 1,
tbname: 'MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O',
contactor: 1,
profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}'
},
'2': {
line: 2,
tbname: 'jBL12pg63eX4N9P7zy0lJLyEJKmlbkGwZMx0avQV',
contactor: 1,
profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}'
},
'3': {
line: 3,
tbname: 'aAOzENGrvpbe0VoK7D6E1a819PZmdg3nl24JLQMk',
contactor: 1,
profile: '{"intervals":[{"value":1,"end_time":"13:00","start_time":"13:00"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}'
}
}
const rpcSwitchOffLine =
{
"topic": "v1/gateway/rpc",
"content": {
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
"data": {
"id": 8,
"method": "set_command",
"params": {
"entities": [
{
"entity_type": "edb_line",
"tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O"
}
],
"command": "switch",
"payload": {
"value": false
}
}
}
}
}
const rpcSetNodeDimming =
{
"topic": "v1/gateway/rpc",
"content": {
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
"data": {
"id": 10,
"method": "set_command",
"params": {
"entities": [
{
"entity_type": "street_luminaire",
"tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV"
}
],
"command": "dimming",
"payload": {
"value": 5
}
}
}
}
}
const rpcLineProfile =
{
"topic": "v1/gateway/rpc",
"content": {
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
"data": {
"id": 9,
"method": "set_profile",
"params": {
"entities": [
{
"entity_type": "edb_line",
"tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O"
}
],
"payload": {
"intervals": [
{
"value": 0,
"end_time": "20:00",
"start_time": "13:00"
},
{
"value": 1,
"end_time": "05:30",
"start_time": "20:00"
},
{
"value": 0,
"end_time": "13:00",
"start_time": "05:30"
}
],
"astro_clock": true,
"dawn_lux_sensor": false,
"dusk_lux_sensor": false,
"dawn_lux_sensor_value": 5,
"dusk_lux_sensor_value": 5,
"dawn_astro_clock_offset": 0,
"dusk_astro_clock_offset": 0,
"dawn_lux_sensor_time_window": 30,
"dusk_lux_sensor_time_window": 30,
"dawn_astro_clock_time_window": 60,
"dusk_astro_clock_time_window": 60
}
}
}
}
}
const rpcNodeProfile =
{
"topic": "v1/gateway/rpc",
"content": {
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
"data": {
"id": 11,
"method": "set_profile",
"params": {
"entities": [
{
"entity_type": "street_luminaire",
"tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV"
}
],
"payload": {
"intervals": [
{
"cct": 3000,
"value": 0,
"end_time": "17:50",
"start_time": "13:00"
},
{
"cct": 3000,
"value": 100,
"end_time": "21:30",
"start_time": "17:50"
},
{
"cct": 3000,
"value": 0,
"end_time": "13:00",
"start_time": "07:10"
},
{
"cct": 3000,
"value": 50,
"end_time": "00:00",
"start_time": "21:30"
},
{
"cct": 3000,
"value": 10,
"end_time": "04:30",
"start_time": "00:00"
},
{
"cct": 3000,
"value": 100,
"end_time": "07:10",
"start_time": "04:30"
}
],
"astro_clock": true,
"dawn_lux_sensor": false,
"dusk_lux_sensor": false,
"dawn_lux_sensor_value": 5,
"dusk_lux_sensor_value": 5,
"dawn_astro_clock_offset": 30,
"dusk_astro_clock_offset": 20,
"dawn_lux_sensor_time_window": 30,
"dusk_lux_sensor_time_window": 30,
"dawn_astro_clock_time_window": 60,
"dusk_astro_clock_time_window": 60
}
}
}
}
}
const sunCalcExample = {
dusk_no_offset: '20:18',
dawn_no_offset: '05:19',
dusk: '20:18',
dusk_hours: 20,
dusk_minutes: 18,
dawn: '05:19',
dawn_hours: 5,
dawn_minutes: 19,
dusk_time: 1715278688962,
dawn_time: 1715224744357,
dusk_astro_clock_offset: 0,
dawn_astro_clock_offset: 0
}