citysys-flowserver/flow/dido_controller.js

1934 lines
56 KiB
JavaScript

exports.id = 'dido_controller';
exports.title = 'DIDO_Controller';
exports.version = '2.0.0';
exports.group = 'Worksys';
exports.color = '#2134B0';
exports.input = 3;
exports.output = ["red", "white", "yellow"];
exports.click = false;
exports.author = 'Daniel Segeš';
exports.icon = 'bolt';
exports.options = { edge: "undefined" };
exports.html = `<div class="padding">
<div class="row">
<div class="col-md-6">
<div data-jc="textbox" data-jc-path="edge" data-jc-config="placeholder:undefined;required:true" class="m">Edge TB Name</div>
</div>
</div>
</div>`;
exports.readme = `# Sets RS232 port and all digital pins on device. Then it starts to receive data from sensors.
It receives:
rotary_switch_state,
rotary_switch_state,
door_condition,
state_of_breaker,
state_of_contactor,
twilight_sensor
`;
/*
we open rsPort "/dev/ttymxc0" and set digital input and output pins with "setRSPortData"
Currently we are interested in pins no. 1,2,3,6,8,9,10,16
pins number 11, 12, 13 (we receive 10,11,12 in rsPortReceivedData) are "stykace"
When port receives data, it must be exactly 4 bytes long. Second byte is pin, that changed its value, fourth byte is value itself.
After that, we set this value to "previousValues[allPins[whichpin]]" variable
*/
/*
RVO objekt:
state_of_main_switch - sem sa bude reportovať stav hlavného ističa : 0-> off 1-> on (toto nie je na platforme, ale Rado to už do entity type doplnil)
rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky:
Manual = 1 a Auto = 0 -> vyreportuje Manual
Manual = 0 a Auto = 0 -> vyreportuje Off
Manual = 0 a Auto = 1 -> vyreportuje Automatic
door_condition - tuto ide pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open
twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy
zjavne nám v jsone chýba stav hlavného ističa. Musíme to potom doplniť
Na každú líniu:
state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off 1-> on
state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off 1-> on
momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme
*/
//globals
//FIRMWARE version
FLOW.OMS_edge_fw_version = "2024-07-08";//rok-mesiac-den
FLOW.OMS_edgeName = "";
FLOW.OMS_maintenance_mode = false;
//dynamic values
FLOW.OMS_masterNodeIsResponding = true; //cmd_manager
//FLOW.OMS_brokerready = false //wsmqttpublish
FLOW.OMS_no_voltage = new Set();//modbus_citysys - elektromer
//see loadSettings() in cmd_manager
FLOW.OMS_language = "en";//cmd_manager
FLOW.OMS_rvo_name = "";//cmd_manager
FLOW.OMS_rvo_tbname = "";//relaysData
FLOW.OMS_temperature_adress = "";//cmd_manager
//-----------------------------------------------
let alarmStatus = "OFF";
const SEND_TO = {
debug: 0,
tb: 1,
cmd_manager: 2
}
var log4js = require("log4js");
var path = require('path');
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");
//console.log(path.join(__dirname, 'err.txt', "-----------------------------"));
/*
process.on('uncaughtException', function (err) {
errLogger.error('uncaughtException:', err.message)
errLogger.error(err.stack);
//process.exit(1);
})
*/
//USAGE
//logger.debug("text")
//monitor.info('info');
//errLogger.error("some error");
exports.install = function(instance) {
process.on('uncaughtException', function (err) {
//TODO send to service
errLogger.error('uncaughtException:', err.message)
errLogger.error(err.stack);
errorHandler.sendMessageToService(err.message + "\n" + err.stack, 0, "js_error");
//process.exit(1);
})
let previousValues = {temperature: 0};
let rsPortReceivedData = [];
let twilight_sensor_interval = 5;//minutes
let twilight_sensor = [];
const twilight_sensor_array = [];
let twilightError = false;
let edgeName = "";
monitor.info("DI_DO_Relay_Controller installed");
//key is PIN number , line: 0 = RVO
/*
let conversionTable = {
"1": {tbname: "", type: "state_of_main_switch", "line": 0}, //state_of_main_switch pin1
"2": {tbname: "", type: "rotary_switch_state", "line": 0}, //rotary_switch_state - poloha manual = pin2
"3": {tbname: "", type: "rotary_switch_state", "line": 0}, //rotary_switch_state - poloha auto = pin3
"4": {tbname: "", type: "power_supply", "line": 0},
"5": {tbname: "", type: "battery", "line": 0},
"6": {tbname: "", type: "door_condition", "line": 0}, // door_condition = pin6, 1 -> vyreportuje Closed, 0 -> vyreportuje Open
"8": {tbname: "", type: "state_of_breaker", "line": 1}, // state_of_breaker linia 1 0=off, 1=on
"9": {tbname: "", type: "state_of_breaker", "line": 2}, // state_of_breaker linia 2 0=off, 1=on
"10": {tbname: "", type: "state_of_breaker", "line": 3}, // state_of_breaker linia 3 0=off, 1=on
"11": {tbname: "", type: "state_of_contactor", "line": 1}, // state_of_contactor linia 1 0=off, 1=on
"12": {tbname: "", type: "state_of_contactor", "line": 2}, // state_of_contactor linia 2 0=off, 1=on
"13": {tbname: "", type: "state_of_contactor", "line": 3}, // state_of_contactor linia 3 0=off, 1=on
"16": {tbname: "", type: "twilight_sensor", "line": 0}, // twilight_sensor = pin16
};
*/
const dbPins = TABLE("pins");
let pinsData = {};//key is pin
const dbRelays = TABLE("relays");
let relaysData = {};//key is line
//status for calculating Statecodes
let deviceStatuses = {};//key is device name: temperature,....
deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič
deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód
deviceStatuses["door_condition"] = "closed";//Dverový kontakt
deviceStatuses["em"] = "OK";//elektromer rvo
deviceStatuses["temperature"] = "OK";//templomer
deviceStatuses["battery"] = "OK";//Batéria
deviceStatuses["power_supply"] = "OK";//Zdroj
deviceStatuses["master_node"] = "OK";//MN - FLOW.OMS_masterNodeIsResponding
deviceStatuses["no_voltage"] = "OK";//FLOW.OMS_no_voltage - výpadok napätia na fáze
deviceStatuses["state_of_breaker"] = {};//"Off";//Istič
deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač
deviceStatuses["twilight_sensor"] = "OK"; //lux sensor
/*
dbRelays.on('change', function(doc, old) {
console.log("'DI_DO_Controller - dbRelays.on('change'");
instance.send(SEND_TO.cmd_manager, "reload_relays");
});
*/
const SerialPort = require('serialport');
const WebSocket = require('ws');
let ws = null;
let rsPort = null;
//const { exec } = require('child_process');
const { openPort, runSyncExec, writeData } = require('./helper/serialport_helper.js');
const { bytesToInt, resizeArray } = require('./helper/utils');
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js');
const bitwise = require('bitwise');
const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js');
const errorHandler = new ErrorToServiceHandler();
//let useTurnOffCounter = false;
//let turnOffCounter = 0;
let controller_type = FLOW.OMS_controller_type //"lm" or "unipi" //logicMachine
if(controller_type == "") controller_type = "lm";
console.log(exports.title, "controller type: ", controller_type);
async function loadAllDb()
{
let responsePins = await promisifyBuilder(dbPins.find());
pinsData = makeMapFromDbResult(responsePins, "pin");
let responseRelays = await promisifyBuilder(dbRelays.find());
relaysData = makeMapFromDbResult(responseRelays, "line");
FLOW.OMS_rvo_tbname = relaysData[0].tbname;
if(controller_type === "lm")
{
handleRsPort();
}
else if(controller_type === "unipi")
{
handleWebSocket();
}
else {
errLogger.debug("UNKNOWN controller_type:", controller_type);
}
}
function initialSetting()
{
//force turn off relays & set tbname
let keys = Object.keys(pinsData);
for(let i = 0; i < keys.length; i++)
{
let key = keys[i];
let line = pinsData[key].line;
if(line != undefined)
{
if(relaysData[line] != undefined)
{
pinsData[key].tbname = relaysData[line].tbname;
relaysData[line].contactor = 0;
}
else
{
errLogger.error("CRITICAL!!! undefined relay", relaysData[line], line);
//sendNotification("set port ", edgeName, ERRWEIGHT.CRITICAL, "local database is corrupted", "", SEND_TO.tb, instance, null );
sendNotification("set port ", edgeName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance );
}
}
if(pinsData[key].type == "state_of_contactor")
{
let pin = key - 1;
if(controller_type === "unipi") pin = key;
//this will modify database
let forceTurnOff = true;
turnOffLine(line, pin, forceTurnOff, "turn off on startup");
}
}
//report RVO version relaysData[0].tbname;
let values = {};
values["edge_fw_version"] = FLOW.OMS_edge_fw_version;
values["maintenance_mode"] = FLOW.OMS_maintenance_mode;
values["status"] = "OK";
edgeName = relaysData[0].tbname;
FLOW.OMS_edgeName = edgeName;
dataToTb = {
[edgeName]: [
{
"ts": Date.now(),
"values": values
}
]
}
instance.send(SEND_TO.tb, dataToTb);
let time = 3*1000;
setTimeout(function(){
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"});
sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", SEND_TO.tb, instance );
monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version);
}, time);
}
function handleRsPort()
{
//TODO build according to pins!!!
//! rsPort to open are the same for lm and unipi and electromer ("/dev/ttymxc0")
const setRSPortData = [0xAA,6,6,6,6,6,6,0,6,6,6,1,1,1,1,0,0,10,10,10,10,10,10,0,10,10,10,0,0,0,0,0,0,5,0,0,0,15,15,15,15,15,15,0,15,15,15,0,0,0,0,0,0,30,0,0,0];
rsPort = new SerialPort("/dev/ttymxc0", { autoOpen: false });
rsPort.on('error', function(err) {
logger.debug("rsPort opened error - failed", err.message);
instance.send(SEND_TO.debug, err.message);
errorHandler.sendMessageToService( exports.title + " rsPort opened error - failed: " + err.message);
})
rsPort.on('open', async function() {
await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function (status) {
//set port
rsPort.write(Buffer.from(setRSPortData), function(err) {
monitor.info(exports.title + "--->Digital in_out has been set (runSyncExec was sucessfull)");
turnOffAlarm();
//useTurnOffCounter = true;
//turnOffCounter = relaysData.length - 1;
initialSetting();
})
}).catch(function (reason) {
//instance.send(SEND_TO.debug, exports.title + " runSyncExec - promise rejected:" + reason);
errLogger.error( exports.title + " runSyncExec - promise rejected:" + reason);
errorHandler.sendMessageToService( exports.title + " runSyncExec - promise rejected:" + reason);
});
});
rsPort.on('data', function (data){
rsPortReceivedData = [...rsPortReceivedData, ...data];
if (rsPortReceivedData[0] != 85) {
rsPortReceivedData = [];
return;
}
let l = rsPortReceivedData.length;
if (l < 4 ) return;
if (l > 4 ) {
// if array length is greater than 4, we take first 4 byte and do the logic, second 4 bytes, do the logic and so on
let i, j, temparray, chunk = 4;
for ( i = 0, j = l; i < j; i += chunk ) {
temparray = rsPortReceivedData.slice(i, i + chunk);
if ( temparray.length < 4 ){
rsPortReceivedData = [...temparray];
return;
}
switchLogic(temparray);
}
rsPortReceivedData = [];
return;
}
switchLogic(rsPortReceivedData);
rsPortReceivedData = [];
});
rsPort.on("close", () => {
rsPort.close();
})
rsPort.open();
}
function handleWebSocket() {
//to keep websocket opened, we send request every 150 seconds
let startRequests = null;
console.log("handleWebSocket function called");
ws = new WebSocket('ws:/0.0.0.0:1234/ws');
ws.onopen = function open() {
instance.send(0, exports.title + " running");
turnOffAlarm();
// useTurnOffCounter = true;
// turnOffCounter = relaysData.length - 1;
initialSetting();
ws.send(JSON.stringify({"cmd":"all"}));
// we request dev info about neuron device from evok to keep websocket connection alive
// for some reason this request returns no data, but connection keeps alive
// https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket
startRequests = setInterval(() => {
// console.log(" *** data from evok requested");
ws.send(JSON.stringify({"cmd":"filter", "dev": ["neuron"]}));
}, 150000)
};
// SAMPLE DATA FROM WEBSOCKET
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_07',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_08',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
ws.onmessage = function(data) {
data = JSON.parse(data.data);
// data comes in array except of "temperature" ==> it comes as an object
if(!Array.isArray(data))
{
let value = data['value'];
// temperature value comes very often. To handle it, we check if it change for more than 0.2 degrees, if yes, we send to TB
if(Math.abs(previousValues["temperature"] - value) > 0.21 )
{
const dataToTb = {
[FLOW.OMS_rvo_tbname]: [
{
"ts": Date.now(),
"values": {temperature: value}
}
]
};
deviceStatuses["temperature"] = "OK";
previousValues["temperature"] = value;
instance.send(SEND_TO.tb, dataToTb);
}
return;
}
data.map(item => {
let value = item['value'];
let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01"
if (pin == undefined) return;
switchLogic(pin, value);
})
}
ws.on('error', (err) => {
monitor.info('websocket error, reconnect')
instance.send(SEND_TO.debug, err.message);
clearInterval(startRequests);
ws = null;
setTimeout(handleWebSocket, 1000);
})
ws.onclose = function(){
// connection closed, discard old websocket and create a new one in 5s
// stopRequests();
monitor.info('websocket onclose, reconnect')
clearInterval(startRequests);
ws = null;
console.log("ws is null now, reconnecting...");
setTimeout(handleWebSocket, 1000);
}
}
// ! do we need requests every minute ???
// const startRequests = () => {
// console.log("startRequest function called");
// start = setInterval(() => {
// // console.log("data from evok requested");
// ws.send(JSON.stringify({"cmd":"filter", "devices": "neuron"}));
// // ws.send(JSON.stringify({"cmd":"filter", "devices":["input", "relay"]}));
// }, 150000)
// }
instance.on("close", () => {
if(rsPort) rsPort.close();
if(ws) ws.close();
clearInterval(sendRebuildTasksAt11);
})
loadAllDb();
function getPin(line)
{
//conversionTable
let keys = Object.keys(pinsData);
for(let i = 0; i < keys.length; i++)
{
let key = keys[i];
if(pinsData[key].type == "state_of_contactor" && pinsData[key].line == line)
{
if(rsPort) return key - 1;
if(ws) return key;
}
}
logger.debug("no pin detected");
return null;
}
function turnOnAlarm()
{
if(FLOW.OMS_maintenance_mode) return;
alarmStatus = "ON";
if(rsPort)
{
let arr = [0x55];
arr.push( 13 );
arr.push( 0 );
arr.push( 1 );
rsPort.write(Buffer.from(arr), function(err) {
logger.debug("sirena zapnuta");
});
}
else if(ws)
{
let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": 1};
ws.send(JSON.stringify(cmd));
logger.debug("sirena zapnuta");
}
}
function turnOffAlarm()
{
alarmStatus = "OFF";
if(rsPort)
{
let arr = [0x55];
arr.push( 13 );
arr.push( 0 );
arr.push( 0 );
rsPort.write(Buffer.from(arr), function(err) {
logger.debug("sirena vypnuta");
});
}
else if(ws)
{
let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": 0};
ws.send(JSON.stringify(cmd));
logger.debug("sirena vypnuta");
}
}
function reportLineStatus(line)
{
//Tá hodnota by mala fungovať tak že LSB bit číslo je stav ističa (1 - On, 0 - Off) a druhý bit je stav stýkača (1 - true, 0 - false).
let tbname = relaysData[line].tbname;
let bits = [];
if(deviceStatuses["state_of_breaker"][line] == "On")
{
bits.push(0);
}
else bits.push(1);
if(deviceStatuses["state_of_contactor"][line] == "On")
{
bits.push(0);
}
else bits.push(1);
resizeArray(bits, 8, 0);
let byte = bitwise.byte.write(bits.reverse());
//console.log("line", line, bits, byte);
let values = {
statecode: byte
}
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
//console.log(values);
instance.send(SEND_TO.tb, dataToTb);
}
function turnOnLine(line, pin, force, info)
{
instance.send(SEND_TO.debug, "turn on line " + line );
if(force == undefined) force = false;
if(line == 0)
{
if(alarmStatus == "ON") turnOffAlarm();
FLOW.OMS_maintenance_mode = true;
let values = {};
values["statecode"] = calculateStateCode();
values["power_mode"] = "maintenance";
let tbname = relaysData[line].tbname;
sendTelemetry(values, tbname);
monitor.info("turnOnLine (line, FLOW.OMS_maintenance_mode)", line, FLOW.OMS_maintenance_mode, info);
return;
}
if( pin === undefined) pin = getPin(line);
monitor.info("turnOnLine (line, pin, force)", line, pin, force, info);
if( pin === undefined)
{
monitor.info("pin is undefined!", line);
return;
}
if(!force)
{
if(relaysData[line].contactor == 1)
{
instance.send(SEND_TO.debug, "line is already on " + line );
logger.debug("turnOnLine: line is already on: ", line);
return;
}
}
// if(!rsPort.isOpen && !ws)
if(!rsPort && !ws)
{
errLogger.error("dido controller - port or websocket is not opened");
return;
}
if(rsPort)
{
let arr = [0x55];
arr.push( pin );
arr.push( 0 );
arr.push( 1 );
rsPort.write(Buffer.from(arr), function(err) {
if(err === undefined)
{
console.log("turnONLine zapisal do rsPortu", line, arr);
switchLogic(arr);
}
else
{
monitor.info("turnOnLine WRITE error", err);
}
});
}
else if(ws)
{
console.log("turnONLine pin (relay)", pin);
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1};
ws.send(JSON.stringify(cmd));
//switchLogic(pin, 1)
}
}
function turnOffLine(line, pin, force, info)
{
if(force == undefined) force = false;
if(line == 0)
{
FLOW.OMS_maintenance_mode = false;
let values = {};
values["statecode"] = calculateStateCode();
values["power_mode"] = "automatic";
let tbname = relaysData[line].tbname;
sendTelemetry(values, tbname);
return;
}
if( pin === undefined) pin = getPin(line);
monitor.info("turnOffLine (line, pin, force)", line, pin, force, info);
if( pin === undefined)
{
errLogger.error("pin is undefined!", line);
return;
}
if(!force)
{
if(relaysData[line].contactor == 0)
{
instance.send(SEND_TO.debug, "line is already off " + line );
logger.debug("turnOffLine: line already off:", line);
return;
}
}
// if(!rsPort.isOpen && !ws)
if(!rsPort && !ws)
{
errLogger.error("di do controller - port or websocket is not opened");
return;
}
if(rsPort)
{
let arr = [0x55];
arr.push( pin );
arr.push( 0 );
arr.push( 0 );
rsPort.write(Buffer.from(arr), function(err) {
if(err === undefined)
{
console.log("turnOffLine zapisal do rsPort-u", line, arr);
switchLogic(arr);
}
else
{
monitor.info("turnOffLine WRITE error", err);
}
});
}
else if(ws)
{
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
//monitor.info("turnOffLine pin (relay)", pin);
let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0};
ws.send(JSON.stringify(cmd));
//switchLogic(pin, 0)
}
}
//data from modbus_reader or temperature sensor or twilight sensor or other modbus device
instance.on("0", flowdata => {
if(!flowdata.data instanceof Object) return;
// console.log('***********************', flowdata.data)
instance.send(SEND_TO.debug, flowdata.data);
// we handle nok status from modbus_reader component and thermometer
if(flowdata.data?.status)
{
const status = flowdata.data.status;
if(status == "NOK-twilight_sensor")
{
deviceStatuses["twilight_sensor"] = "NOK";
}
else if(status == "NOK-em340" || status == "NOK-em111")
{
deviceStatuses["em"] = "NOK";
}
else if(status == "NOK-thermometer")
{
deviceStatuses["temperature"] = "NOK";
}
}
else if(flowdata.data?.values)
{
const values = flowdata.data.values;
if(values.hasOwnProperty("twilight_sensor"))
{
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]});
deviceStatuses["twilight_sensor"] = "OK"
}
else if(values.hasOwnProperty("temperature"))
{
deviceStatuses["temperature"] = "OK";
}
// EM
else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current"))
{
deviceStatuses["em"] = "OK";
}
else
{
return;
}
const updateStatus = checkFinalRVOStatus();
if(updateStatus) values.status = "OK";
sendTelemetry(values, FLOW.OMS_rvo_tbname);
}
})
// we expect array as flowdata.data
instance.on("1", flowdata => {
console.log(flowdata.data);
if(!flowdata.data instanceof Object) return;
let obj = flowdata.data;
let line = obj.line;
let force = obj.force;
let info = obj.info;
if(obj.command == "turnOn") turnOnLine(line, undefined, force, info);
else if(obj.command == "turnOff") turnOffLine(line, undefined, force, info);
else if(obj.command == "turnOnAlarm") turnOnAlarm();
else if(obj.command == "turnOffAlarm") turnOffAlarm();
//! ake data prichadzaju z cmd_manager.js ???
//TODO transform to websocket
if (Array.isArray(obj)){
rsPort.write(Buffer.from(obj), function(err) {
switchLogic(obj);
instance.send(SEND_TO.debug, {"WRITE":obj} );
});
}
})
function calculateStateCode()
{
let bytes = [];
let bits = [];
//Hlavný istič - state_of_main_switch
if(deviceStatuses["state_of_main_switch"] == "On")
{
bits.push(0);
}
else if(deviceStatuses["state_of_main_switch"] == "Off")
{
bits.push(1);
}
//Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY
if(!FLOW.OMS_maintenance_mode)
{
if(deviceStatuses["rotary_switch_state"] == "Manual")
{
bits.push(0);
bits.push(1);
}
if(deviceStatuses["rotary_switch_state"] == "Automatic")
{
bits.push(0);
bits.push(0);
}
if(deviceStatuses["rotary_switch_state"] == "Off")
{
bits.push(1);
bits.push(0);
}
}
else{
bits.push(1);
bits.push(1);
}
//Dverový kontakt
if(deviceStatuses["door_condition"] == "closed")
{
bits.push(0);
}
else
{
bits.push(1);
}
//EM
if(deviceStatuses["em"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
//Teplomer
if(deviceStatuses["temperature"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
//Batéria
if(deviceStatuses["battery"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
//Zdroj
if(deviceStatuses["power_supply"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
//MN
if(deviceStatuses["master_node"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
//výpadok napätia na fáze
if(deviceStatuses["no_voltage"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
if(deviceStatuses["twilight_sensor"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
// doplnime do 16 bitov (2 byty)
for(let i = bits.length; i < 16; i++)
{
bits.push(0);
}
// console.log("calculateStateCode - deviceStatuses", deviceStatuses);
// console.log("calculateStateCode", bits);
let byte0 = bitwise.byte.write(bits.slice(0,8).reverse());
let byte1 = bitwise.byte.write(bits.slice(8).reverse());
let byte = bytesToInt([byte1, byte0]);
//console.log("calculateStateCode -------------------", byte);
return byte;
}
function checkFinalRVOStatus() {
// we check if any of these pins values are 0 --> it means status RVO is "NOK"
// pinIndex 6 is door_condition - if open in maintenance mode - status = OK
//set RVO state
let status = "OK";
if(deviceStatuses["em"] == "NOK")
{
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: EM status is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: EM status is NOK");
status = "NOK";
}
if(deviceStatuses["twilight_sensor"] == "NOK")
{
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: twilight_sensor is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: twilight sensor is NOK");
status = "NOK";
}
//ak teplomer NOK, rvo nok
if(deviceStatuses["temperature"] == "NOK")
{
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: temperature status is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: temperature status is NOK");
status = "NOK";
}
if(status == "OK")
{
let pinIndexes = [1, 4, 6];
if(controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05'];
//console.log('-------- previousValues', previousValues);
for (const pinIndex of pinIndexes) {
if (previousValues[pinIndex] === 0) {
if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue;
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0");
if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type);
status = "NOK";
break;
}
}
}
// battery status. If value is 1 - battery is NOK
if (previousValues[5] === 1)
{
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery");
if(writeToFile) errLogger.error("checkFinalRVOStatus: NOK status generated by battery");
status = "NOK";
}
//console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding);
if(!FLOW.OMS_masterNodeIsResponding)
{
//errLogger.error("Master node is not responding");
errorHandler.sendMessageToService("Master node is not responding");
status = "NOK";
deviceStatuses["master_node"] = "NOK";
}
else deviceStatuses["master_node"] = "OK";
//console.log("checkFinalRVOStatus", status);
if(FLOW.OMS_no_voltage.size > 0)
{
let writeToFile = errorHandler.processMessage("no voltage detected");
if(writeToFile) errLogger.error("no voltage detected", FLOW.OMS_no_voltage);
status = "NOK";
deviceStatuses["no_voltage"] = "NOK";
}
else deviceStatuses["no_voltage"] = "OK";
if(status == "NOK")
{
sendTelemetry({status: "NOK"}, FLOW.OMS_rvo_tbname);
return false;
}
return true;
}
// we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]]
// we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1]
const switchLogic = (...args) => {
let values = {status: "OK"};
let dataToTb, pinIndex, newPinValue, twilight;
//data from rsPort
if(args.length == 1)
{
pinIndex = args[0][1] + 1;
if (pinIndex === 17) pinIndex--;
newPinValue = args[0][3];
twilight = args[0][2];
}
//data from websocket
else
{
pinIndex = args[0];
newPinValue = args[1];
}
let obj = pinsData[pinIndex];
if(obj == undefined)
{
previousValues[pinIndex] = newPinValue;
return;
}
let type = obj.type;
let line = obj.line;
let tbname = obj.tbname;
//default value
let value = "On";
if(newPinValue === 0) value = "Off";
//Hlavný istič
//! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri
//! takze ked pride z evoku signal pre 'input1_05', handlujeme ho ako 'door_condition'
if(type === "!!!state_of_main_switch")
{
if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
{
sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch");
values["status"] = "NOK";
deviceStatuses["state_of_main_switch"] = "Off";
}
else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{
sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch");
deviceStatuses["state_of_main_switch"] = "On";
}
}
//Prevádzkový mód
if(type == "rotary_switch_state")
{
// combination of these two pins required to get result
let pin2, pin3;
if(pinIndex == 2 || pinIndex == "input1_02")
{
pin2 = newPinValue;
pin3 = previousValues[3] || previousValues["input1_03"];
if(pin3 == undefined)
{
previousValues[pinIndex] = newPinValue;
return;
}
}
else if(pinIndex == 3 || pinIndex == "input1_03")
{
pin3 = newPinValue;
pin2 = previousValues[2] || previousValues["input1_02"];
if(pin2 == undefined)
{
previousValues[pinIndex] = newPinValue;
return;
}
}
//console.log('***********************', pin2, pin3)
if (pin2 == 1 && pin3 == 0) value = "Manual";
if (pin2 == 0 && pin3 == 0) value = "Off";
if (pin2 == 0 && pin3 == 1) value = "Automatic";
deviceStatuses["rotary_switch_state"] = value;
//automatic - profilu pre nody sa vykonavaju
//ak je spracovany, a automatic - tak ho zapnem
//ak nie je spracovany, iba profil zapisem
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value});
//console.log("rotary_switch_state pin", pin2, pin3, value);
}
//Zdroj - pin 4
else if (type === "power_supply")
{
if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply");
values["status"] = "NOK";
deviceStatuses["power_supply"] = "NOK";
}
else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply");
deviceStatuses["power_supply"] = "OK";
}
}
//Batéria - pin 5
else if (type === "battery")
{
if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level");
values["status"] = "NOK";
deviceStatuses["battery"] = "NOK";
}
else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level");
deviceStatuses["battery"] = "OK";
}
}
//Dverový kontakt - pin 6
//! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch"
//! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition'
else if(type == "door_condition" || type === "state_of_main_switch")
{
newPinValue === 0 ? value = "open" : value = "closed";
if (newPinValue != previousValues[pinIndex])
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, `RVO door ${value}`, "", SEND_TO.tb, instance, "rvo_door");
//TODO ? sendNotification("switchLogic", edgeName, "door_value", {value: value}, "", SEND_TO.tb, instance, "rvo_door");
}
if (value === "open" && FLOW.OMS_maintenance_mode)
{
sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", SEND_TO.tb, instance, "rvo_door");
}
if (value === "open" && !FLOW.OMS_maintenance_mode)
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.WARNING, "RVO open door out of maintenance mode", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "door_has_been_open_without_permision_alarm_is_on", {}, "", SEND_TO.tb, instance, "rvo_door");
values["status"] = "NOK";
//console.log(door_has_been_open_without_permision_alarm_is_on);
// zapneme sirenu
// ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition")
if(type === "door_condition") turnOnAlarm();
}
if (value === "closed")
{
if(alarmStatus == "ON") turnOffAlarm();
//turnOffAlarm();
sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", SEND_TO.tb, instance, "rvo_door");
}
deviceStatuses["door_condition"] = value;
}
//lux sensor
else if(type == "twilight_sensor")
{
//! TODO - to show nok status, if lux value is not changing more then 10 times. From unipi for example comes value from 0-1000.
//Daylight is far more than 1000. So most of the day, when it is sunshine comes just value 1000. But lux sensor is not NOK.
//This is not the case in LM. If value from LM is the same 10x, there is 99% possibility, that sensor is NOK.
values["status"] = "OK";
value = newPinValue;
if(controller_type === 'lm')
{
value = parseFloat(newPinValue + (256*twilight));
let now = new Date();
//new Date(dusk.getTime()
let obj = {timestamp: now.getTime(), value: value};
//test
//twilight_sensor_interval = 1;
twilight_sensor.push(obj);
//twilight_sensor_array.push(value);
//check if we receive just 1 constant value from lux sensor ==> error
if(twilight_sensor_array.length > 10) {
let set = new Set(twilight_sensor_array);
if(set.size === 1 && !twilightError)
{
twilightError = true;
values["status"] = "NOK";
let value = twilight_sensor_array.shift();
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, SEND_TO.tb, instance );
newPinValue = 0;
}
else if (set.size !== 1 && twilightError)
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance );
twilightError = false;
twilight_sensor_array.shift();
newPinValue = value;
}
else if (set.size === 1 && twilightError)
{
values["status"] = "NOK";
twilight_sensor_array.shift();
newPinValue = 0;
}
}
let diff = twilight_sensor[ twilight_sensor.length - 1 ].timestamp - twilight_sensor[0].timestamp;
if(diff >= twilight_sensor_interval * 60 * 1000)
{
const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length;
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: average});
twilight_sensor = [];
//console.log("lux_sensor send", average);
}
//else console.log("lux_sensor", value, diff);
}
}
else if(type == "state_of_contactor")
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance );
if(!(deviceStatuses["state_of_contactor"][line] == value))
{
sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", SEND_TO.tb, instance );
}
else
{
deviceStatuses["state_of_contactor"][line] = value;
}
//true, false
if(value === "On") value = true;
else if(value === "Off") value = false;
//modify table relays
dbRelays.modify({ contactor: newPinValue }).where("line", line).make(function(builder) {
builder.callback(function(err, response) {
/*
if(useTurnOffCounter)
{
turnOffCounter--;
if(turnOffCounter <= 0)
{
useTurnOffCounter = false;
}
}
*/
if(err == undefined)
{
let time = 0;
if(value) time = 1000 * 10;//10 sekund
let dataChanged = false;
if(relaysData[line].contactor != value) dataChanged = true;
relaysData[line].contactor = value;
//ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles
//a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor
setTimeout(function(){
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged});
}, time);
reportLineStatus(line);
}
else
{
errLogger.error("modify table relays failed", err);
}
});
});
}
if(type === "state_of_breaker")
{
let valueChanged = false;
if(newPinValue != previousValues[pinIndex]) valueChanged = true;
if(valueChanged)
{
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line});
//mame iba 3 istice. vyreportujeme a ohandlujeme liniu na tom istom istici ako paralelna linia (napr linia 1, paralelna s nou je linia 4, key je string "4")
// ak je 7 linii, na 1 istici je linia 1,4,7
if(line == 1)
{
const lineOnSameBraker = [4,7];
for (var i = 0; i < lineOnSameBraker.length; i++)
{
if(!relaysData.hasOwnProperty(lineOnSameBraker[i])) continue;
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: lineOnSameBraker[i]});
deviceStatuses["state_of_breaker"][lineOnSameBraker[i]] = value;
reportLineStatus(lineOnSameBraker[i]);
values[type] = value;
const tbname = relaysData[lineOnSameBraker[i]].tbname;
sendTelemetry(values, tbname);
delete values[type];
}
}
else
{
const lineOnSameBraker = line + 3 + "";
if(relaysData.hasOwnProperty(lineOnSameBraker))
{
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line + 3});
deviceStatuses["state_of_breaker"][line + 3] = value;
reportLineStatus(line + 3);
values[type] = value;
const tbname = relaysData[lineOnSameBraker].tbname;
sendTelemetry(values, tbname);
delete values[type];
}
}
}
if(value == "Off") values["status"] = "NOK";
deviceStatuses["state_of_breaker"][line] = value;
reportLineStatus(line);
}
values[type] = value;
//if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode();
if(pinsData.hasOwnProperty(pinIndex))
{
let valueChanged = false;
if(newPinValue != previousValues[pinIndex])
{
valueChanged = true;
//pin was changed
previousValues[pinIndex] = newPinValue;
}
let result = checkFinalRVOStatus();
if(!result && line == 0)
{
values["status"] = "NOK";
}
if(type == "state_of_contactor") valueChanged = true;
if(type == "rotary_switch_state") valueChanged = true;
if(type === "state_of_breaker")
{
//console.log(type, values, valueChanged);
}
if(FLOW.OMS_rvo_tbname == "")
{
console.log("FLOW.OMS_rvo_tbname is EMPTY");
}
if(FLOW.OMS_rvo_tbname == tbname)
{
values["statecode"] = calculateStateCode();
//console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname);
}
if(valueChanged)
{
sendTelemetry(values, tbname);
}
if(type == "rotary_switch_state")
{
if(FLOW.OMS_maintenance_mode) value = "maintenance";
value = value.toLowerCase();
let values = {};
values["power_mode"] = value;
sendTelemetry(values, tbname);
}
}
else
{
logger.debug("no pinIndex", pinsData[pinIndex], pinsData);
}
}
function sendTelemetry(values, tbname)
{
let dataToTb = {
[tbname]: [
{
"ts": Date.now(),
"values": values
}
]
}
instance.send(SEND_TO.tb, dataToTb);
}
}
//! incomming data to websocket
// [
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_08',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_01',
// alias: 'al_lights_kitchen',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_02',
// alias: 'al_lights_bedroom',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_03',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_04',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_05',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_06',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_07',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_08',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_mode: 'Enabled',
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// dev: 'input',
// modes: [ 'Simple', 'DirectSwitch' ],
// debounce: 50,
// counter: 1,
// value: 1,
// alias: 'al_main_switch',
// mode: 'Simple',
// circuit: '1_01'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 1,
// circuit: '1_02',
// debounce: 50,
// counter: 2,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 1,
// circuit: '1_03',
// debounce: 50,
// counter: 2,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_04',
// debounce: 50,
// counter: 1,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_05',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_06',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_07',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// interval: 3,
// value: 24.5,
// circuit: '28744F7791180257',
// address: '28744F7791180257',
// time: 1631873896.48797,
// typ: 'DS18B20',
// lost: false,
// dev: 'temp'
// },
// {
// bus: '/dev/i2c-2',
// interval: 3,
// dev: 'owbus',
// scan_interval: 300,
// circuit: '1',
// do_scan: false,
// do_reset: false
// },
// {
// glob_dev_id: 1,
// last_comm: 0.014672994613647461,
// ver2: '0.1',
// sn: 42,
// circuit: '1',
// model: 'S207',
// dev: 'neuron',
// board_count: 1
// },
// {
// circuit: '1_01',
// value: 0,
// glob_dev_id: 1,
// dev: 'wd',
// timeout: 5000,
// was_wd_reset: 0,
// nv_save: 0
// }
// ]
//! loaded pins_data --> from LM
// {
// '1': { pin: 1, type: 'state_of_main_switch', line: 0 },
// '2': { pin: 2, type: 'rotary_switch_state', line: 0 },
// '3': { pin: 3, type: 'rotary_switch_state', line: 0 },
// '4': { pin: 4, type: 'power_supply', line: 0 },
// '5': { pin: 5, type: 'battery', line: 0 },
// '6': { pin: 6, type: 'door_condition', line: 0 },
// '8': { pin: 8, type: 'state_of_breaker', line: 1 },
// '9': { pin: 9, type: 'state_of_breaker', line: 2 },
// '10': { pin: 10, type: 'state_of_breaker', line: 3 },
// '11': { pin: 11, type: 'state_of_contactor', line: 1 },
// '12': { pin: 12, type: 'state_of_contactor', line: 2 },
// '13': { pin: 13, type: 'state_of_contactor', line: 3 },
// '16': { pin: 16, type: 'twilight_sensor', line: 0 }
// }
//! pins.table --> from LM
// pin:number|type:string|line:number
// *|1|state_of_main_switch|0|...........
// *|2|rotary_switch_state|0|...........
// *|3|rotary_switch_state|0|...........
// *|4|power_supply|0|...........
// *|5|battery|0|...........
// *|6|door_condition|0|...........
// *|8|state_of_breaker|1|...........
// *|9|state_of_breaker|2|...........
// *|10|state_of_breaker|3|...........
// *|11|state_of_contactor|1|...........
// *|12|state_of_contactor|2|...........
// *|13|state_of_contactor|3|...........
// *|16|twilight_sensor|0|...........
//! pins.table --> from UNIPI
// pin:string|type:string|line:number
// *|input1_01|state_of_main_switch|0|...........
// *|input1_02|rotary_switch_state|0|...........
// *|input1_03|rotary_switch_state|0|...........
// *|intut1_04|power_supply|0|...........
// *|input1_05|door_condition|0|...........
// *|input1_06|state_of_breaker|1|...........
// *|input1_07|state_of_breaker|2|...........
// *|input1_08|state_of_breaker|3|...........
// *|relay1_02|state_of_contactor|1|...........
// *|relay1_03|state_of_contactor|2|...........
// *|relay1_04|state_of_contactor|3|...........
// *|287D8776E0013CE9|temperature|0|...........
//! pins_data --> from UNIPI
// {
// '16': { pin: '16', type: 'twilight_sensor', line: 0 },
// al_mswitch: { pin: 'al_mswitch', type: 'state_of_main_switch', line: 0 },
// al_rswitch1: { pin: 'al_rswitch1', type: 'rotary_switch_state', line: 0 },
// al_rswitch2: { pin: 'al_rswitch2', type: 'rotary_switch_state', line: 0 },
// al_power: { pin: 'al_power', type: 'power_supply', line: 0 },
// al_battery: { pin: 'al_battery', type: 'battery', line: 0 },
// al_door: { pin: 'al_door', type: 'door_condition', line: 0 },
// al_breaker1: { pin: 'al_breaker1', type: 'state_of_breaker', line: 1 },
// al_breaker2: { pin: 'al_breaker2', type: 'state_of_breaker', line: 2 },
// al_breaker3: { pin: 'al_breaker3', type: 'state_of_breaker', line: 3 },
// al_breaker4: { pin: 'al_breaker4', type: 'state_of_breaker', line: 4 },
// al_relay_1: { pin: 'al_relay_1', type: 'state_of_contactor', line: 1 },
// al_relay_2: { pin: 'al_relay_2', type: 'state_of_contactor', line: 2 },
// al_relay_3: { pin: 'al_relay_3', type: 'state_of_contactor', line: 3 },
// al_relay_4: { pin: 'al_relay_4', type: 'state_of_contactor', line: 4 },
// '28744F7791180257': { pin: '28744F7791180257', type: 'temperature', line: 0 }
// }
//! relays_data
// {
// '0': {
// line: 0,
// tbname: 'KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV',
// contactor: 1,
// profile: ''
// },
// '1': {
// line: 1,
// tbname: 'RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O',
// contactor: 0,
// profile: '{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"10:00","start_time":"20:00"},{"value":0,"end_time":"10:20","start_time":"10:00"},{"value":1,"end_time":"10:40","start_time":"10:20"},{"value":0,"end_time":"11:00","start_time":"10:40"},{"value":1,"end_time":"11:30","start_time":"11:00"},{"value":0,"end_time":"11:50","start_time":"11:30"},{"value":1,"end_time":"13:00","start_time":"11:50"}],"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: 'dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7',
// contactor: 0,
// profile: '{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"10:00","start_time":"20:00"},{"value":0,"end_time":"10:20","start_time":"10:00"},{"value":1,"end_time":"10:40","start_time":"10:20"},{"value":0,"end_time":"11:00","start_time":"10:40"},{"value":1,"end_time":"11:30","start_time":"11:00"},{"value":0,"end_time":"11:50","start_time":"11:30"},{"value":1,"end_time":"13:00","start_time":"11:50"}],"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: 'vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V',
// contactor: 0,
// profile: '{"intervals":[{"value":0,"end_time":"20:30","start_time":"13:00"},{"value":1,"end_time":"00:10","start_time":"20:30"},{"value":0,"end_time":"13:00","start_time":"05:40"},{"value":1,"end_time":"05:40","start_time":"00:10"}],"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}'
// }
// }
// {
// "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV": [
// {
// "ts": 1700409326353,
// "values": {
// "_event": {
// "type": "notice",
// "status": "new",
// "source": {
// "func": "rsPort.open()",
// "component": "1700343402190",
// "component_name": "DIDO_Controller",
// "edge": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV"
// },
// "message": "al_shariff_10.0.0.38: FLOW has been started ",
// "message_data": ""
// }
// }
// }
// ]
// }