3793 lines
110 KiB
JavaScript
3793 lines
110 KiB
JavaScript
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
|
||
}
|