Compare commits

..

15 commits

Author SHA1 Message Date
Jakub Klena
f7350c117d Merged in senica-rvo-fix-line-control (pull request #4)
Senica rvo fix line control
2024-09-09 11:47:59 +00:00
Jakub Klena
e3c68f3d86 Merged master into senica-rvo-fix-line-control 2024-09-09 11:47:05 +00:00
Jakub Klena
7e20c7d3f0 Removing duplicate 2024-09-09 13:44:39 +02:00
8929cc3a53 actual working Senica Rvo code 09/2024 2024-09-09 12:32:34 +02:00
Jakub Klena
464e74fe26 Merged in feat/Create-slack-connector (pull request #2)
Feat/Create slack connector

Approved-by: Rastislav Kovac
2024-09-06 13:00:04 +00:00
rasta5man
f4ffef94b9 kovalov part delete 2024-06-20 17:56:49 +02:00
Jakub Klena
1b7aae4590 removing some debug msgs 2024-05-24 08:44:15 +02:00
Jakub Klena
6d468acd6f proper last version 2024-05-24 08:42:21 +02:00
Jakub Klena
9e2ab3ccea api key fix 2024-05-24 01:24:08 +02:00
Jakub Klena
2f659a164b Slack connector component 2024-05-24 01:19:34 +02:00
rasta5man
54038a06f8 major updated line switching version 2024-05-10 2024-05-21 15:49:42 +02:00
rasta5man
d289a99d07 new switching line functionality - in progress 2024-05-14 16:29:11 +02:00
rasta5man
31fe341ef1 refactoring - no change 2024-05-14 16:17:58 +02:00
rasta5man
86c7b3a942 add buildTasks function to dido_controller and handle 7 lines with state_of_braker 2024-05-07 16:28:43 +02:00
rasta5man
ac2ccc1c61 senica add rebuild tasks at 11 on tuesday and saturday 2024-05-07 14:11:40 +02:00
15 changed files with 7230 additions and 8106 deletions

3
config
View file

@ -1,5 +1,4 @@
name : Total.js Flow name : Total.js Flow
default_timezone : Europe/Bratislava default_timezone : Europe/Bratislava
// Packages settings // Packages settings
@ -7,7 +6,7 @@ package#flow (Object) : { url: '/' }
table.relays : line:number|tbname:string|contactor:number|profile:string table.relays : line:number|tbname:string|contactor:number|profile:string
table.nodes : node:number|tbname:string|line:number|dimming:number|profile:string|processed:boolean|status:boolean table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
table.pins : pin:string|type:string|line:number table.pins : pin:string|type:string|line:number
table.notifications : key:string|weight:string|sk:string|en:string table.notifications : key:string|weight:string|sk:string|en:string

114
databases/modbus_config.js Normal file
View file

@ -0,0 +1,114 @@
const timeoutInterval = 150000;
const deviceConfig = [
{
device: "em340",
deviceAddress: 1,
stream: [
{
"tbAttribute": "Phase_1_voltage",
"register": 0,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_2_voltage",
"register": 2,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_3_voltage",
"register": 4,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_1_current",
"register": 12,
"size": 2,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_2_current",
"register": 14,
"size": 2,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_3_current",
"register": 16,
"size": 2,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_1_power",
"register": 18,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_2_power",
"register": 20,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_3_power",
"register": 22,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "total_power",
"register": 40,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "total_energy",
"register": 52,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_1_pow_factor",
"register": 46,
"size": 1,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_2_pow_factor",
"register": 47,
"size": 1,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_3_pow_factor",
"register": 48,
"size": 1,
"multiplier": 0.001
},
{
"tbAttribute": "power_factor",
"register": 49,
"size": 1,
"multiplier": 0.001
}
]
},
{
device: "twilight_sensor",
deviceAddress: 2,
stream: [
{
"tbAttribute": "twilight_sensor",
"register": 60,
"size": 2,
"multiplier": 1
}
]
}
];
module.exports = { timeoutInterval, deviceConfig };

View file

@ -32,3 +32,6 @@ key:string|weight:string|sk:string|en:string
+|flow_start|NOTICE|FLOW bol spustený|FLOW has been started |............... +|flow_start|NOTICE|FLOW bol spustený|FLOW has been started |...............
+|twilight_sensor_nok|ERROR|Sensor súmraku neodpovedá|Twilight sensor is not responding|............... +|twilight_sensor_nok|ERROR|Sensor súmraku neodpovedá|Twilight sensor is not responding|...............
+|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|............... +|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|...............
+|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|...............
+|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|...............
+|flow_restart|NOTICE|Restart flowu|Flow has been restarted|...............

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
exports.id = 'di_do_controller'; exports.id = 'dido_controller';
exports.title = 'DI_DO_Controller'; exports.title = 'DIDO_Controller';
exports.version = '1.0.0'; exports.version = '2.0.0';
exports.group = 'Worksys'; exports.group = 'Worksys';
exports.color = '#2134B0'; exports.color = '#2134B0';
exports.input = 1; exports.input = 3;
exports.output = ["red", "white", "yellow"]; exports.output = ["red", "white", "yellow"];
exports.click = false; exports.click = false;
exports.author = 'Daniel Segeš'; exports.author = 'Daniel Segeš';
@ -60,7 +60,7 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda
//globals //globals
//FIRMWARE version //FIRMWARE version
FLOW.OMS_edge_fw_version = "2022-05-12";//rok-mesiac-den FLOW.OMS_edge_fw_version = "2024-07-08";//rok-mesiac-den
FLOW.OMS_edgeName = ""; FLOW.OMS_edgeName = "";
FLOW.OMS_maintenance_mode = false; FLOW.OMS_maintenance_mode = false;
@ -78,7 +78,7 @@ FLOW.OMS_temperature_adress = "";//cmd_manager
let alarmStatus = "OFF"; let alarmStatus = "OFF";
const instanceSendTo = { const SEND_TO = {
debug: 0, debug: 0,
tb: 1, tb: 1,
cmd_manager: 2 cmd_manager: 2
@ -178,7 +178,7 @@ exports.install = function(instance) {
deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič
deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód
deviceStatuses["door_condition"] = "closed";//Dverový kontakt deviceStatuses["door_condition"] = "closed";//Dverový kontakt
deviceStatuses["rvo"] = {status: "OK"};//elektromer rvo deviceStatuses["em"] = "OK";//elektromer rvo
deviceStatuses["temperature"] = "OK";//templomer deviceStatuses["temperature"] = "OK";//templomer
deviceStatuses["battery"] = "OK";//Batéria deviceStatuses["battery"] = "OK";//Batéria
deviceStatuses["power_supply"] = "OK";//Zdroj deviceStatuses["power_supply"] = "OK";//Zdroj
@ -187,11 +187,12 @@ exports.install = function(instance) {
deviceStatuses["state_of_breaker"] = {};//"Off";//Istič deviceStatuses["state_of_breaker"] = {};//"Off";//Istič
deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač
deviceStatuses["twilight_sensor"] = "OK"; //lux sensor
/* /*
dbRelays.on('change', function(doc, old) { dbRelays.on('change', function(doc, old) {
console.log("'DI_DO_Controller - dbRelays.on('change'"); console.log("'DI_DO_Controller - dbRelays.on('change'");
instance.send(instanceSendTo.cmd_manager, "reload_relays"); instance.send(SEND_TO.cmd_manager, "reload_relays");
}); });
*/ */
@ -263,14 +264,13 @@ exports.install = function(instance) {
{ {
errLogger.error("CRITICAL!!! undefined relay", relaysData[line], line); errLogger.error("CRITICAL!!! undefined relay", relaysData[line], line);
//sendNotification("set port ", edgeName, ERRWEIGHT.CRITICAL, "local database is corrupted", "", instanceSendTo.tb, instance, null ); //sendNotification("set port ", edgeName, ERRWEIGHT.CRITICAL, "local database is corrupted", "", SEND_TO.tb, instance, null );
sendNotification("set port ", edgeName, "local_database_is_corrupted", {}, "", instanceSendTo.tb, instance ); sendNotification("set port ", edgeName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance );
} }
} }
if(pinsData[key].type == "state_of_contactor") if(pinsData[key].type == "state_of_contactor")
{ {
let pin = key - 1; let pin = key - 1;
if(controller_type === "unipi") pin = key; if(controller_type === "unipi") pin = key;
@ -298,13 +298,13 @@ exports.install = function(instance) {
] ]
} }
instance.send(instanceSendTo.tb, dataToTb); instance.send(SEND_TO.tb, dataToTb);
let time = 3*1000; let time = 3*1000;
setTimeout(function(){ setTimeout(function(){
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "buildTasks"}); instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"});
sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", instanceSendTo.tb, instance ); sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", SEND_TO.tb, instance );
monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version); monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version);
}, time); }, time);
@ -320,7 +320,7 @@ exports.install = function(instance) {
rsPort.on('error', function(err) { rsPort.on('error', function(err) {
logger.debug("rsPort opened error - failed", err.message); logger.debug("rsPort opened error - failed", err.message);
instance.send(instanceSendTo.debug, err.message); instance.send(SEND_TO.debug, err.message);
errorHandler.sendMessageToService( exports.title + " rsPort opened error - failed: " + err.message); errorHandler.sendMessageToService( exports.title + " rsPort opened error - failed: " + err.message);
}) })
@ -342,7 +342,7 @@ exports.install = function(instance) {
}) })
}).catch(function (reason) { }).catch(function (reason) {
//instance.send(instanceSendTo.debug, exports.title + " runSyncExec - promise rejected:" + reason); //instance.send(SEND_TO.debug, exports.title + " runSyncExec - promise rejected:" + reason);
errLogger.error( exports.title + " runSyncExec - promise rejected:" + reason); errLogger.error( exports.title + " runSyncExec - promise rejected:" + reason);
errorHandler.sendMessageToService( exports.title + " runSyncExec - promise rejected:" + reason); errorHandler.sendMessageToService( exports.title + " runSyncExec - promise rejected:" + reason);
@ -448,7 +448,6 @@ exports.install = function(instance) {
// dev: 'input', // dev: 'input',
// mode: 'Simple' // mode: 'Simple'
// }, // },
ws.onmessage = function(data) { ws.onmessage = function(data) {
data = JSON.parse(data.data); data = JSON.parse(data.data);
@ -472,7 +471,7 @@ exports.install = function(instance) {
deviceStatuses["temperature"] = "OK"; deviceStatuses["temperature"] = "OK";
previousValues["temperature"] = value; previousValues["temperature"] = value;
instance.send(instanceSendTo.tb, dataToTb); instance.send(SEND_TO.tb, dataToTb);
} }
return; return;
} }
@ -489,15 +488,18 @@ exports.install = function(instance) {
ws.on('error', (err) => { ws.on('error', (err) => {
instance.send(instanceSendTo.debug, err.message); monitor.info('websocket error, reconnect')
instance.send(SEND_TO.debug, err.message);
clearInterval(startRequests); clearInterval(startRequests);
startRequests = null; ws = null;
setTimeout(handleWebSocket, 1000);
}) })
ws.onclose = function(){ ws.onclose = function(){
// connection closed, discard old websocket and create a new one in 5s // connection closed, discard old websocket and create a new one in 5s
// stopRequests(); // stopRequests();
monitor.info('websocket onclose, reconnect')
clearInterval(startRequests); clearInterval(startRequests);
ws = null; ws = null;
console.log("ws is null now, reconnecting..."); console.log("ws is null now, reconnecting...");
@ -519,6 +521,7 @@ exports.install = function(instance) {
instance.on("close", () => { instance.on("close", () => {
if(rsPort) rsPort.close(); if(rsPort) rsPort.close();
if(ws) ws.close(); if(ws) ws.close();
clearInterval(sendRebuildTasksAt11);
}) })
loadAllDb(); loadAllDb();
@ -635,14 +638,14 @@ exports.install = function(instance) {
//console.log(values); //console.log(values);
instance.send(instanceSendTo.tb, dataToTb); instance.send(SEND_TO.tb, dataToTb);
} }
function turnOnLine(line, pin, force, info) function turnOnLine(line, pin, force, info)
{ {
instance.send(instanceSendTo.debug, "turn on line " + line ); instance.send(SEND_TO.debug, "turn on line " + line );
if(force == undefined) force = false; if(force == undefined) force = false;
if(line == 0) if(line == 0)
@ -676,7 +679,7 @@ exports.install = function(instance) {
{ {
if(relaysData[line].contactor == 1) if(relaysData[line].contactor == 1)
{ {
instance.send(instanceSendTo.debug, "line is already on " + line ); instance.send(SEND_TO.debug, "line is already on " + line );
logger.debug("turnOnLine: line is already on: ", line); logger.debug("turnOnLine: line is already on: ", line);
return; return;
@ -717,7 +720,7 @@ exports.install = function(instance) {
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1}; let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1};
ws.send(JSON.stringify(cmd)); ws.send(JSON.stringify(cmd));
switchLogic(pin, 1) //switchLogic(pin, 1)
} }
} }
@ -753,7 +756,7 @@ exports.install = function(instance) {
{ {
if(relaysData[line].contactor == 0) if(relaysData[line].contactor == 0)
{ {
instance.send(instanceSendTo.debug, "line is already off " + line ); instance.send(SEND_TO.debug, "line is already off " + line );
logger.debug("turnOffLine: line already off:", line); logger.debug("turnOffLine: line already off:", line);
return; return;
@ -794,48 +797,76 @@ exports.install = function(instance) {
//monitor.info("turnOffLine pin (relay)", pin); //monitor.info("turnOffLine pin (relay)", pin);
let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0}; let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0};
ws.send(JSON.stringify(cmd)); ws.send(JSON.stringify(cmd));
switchLogic(pin, 0) //switchLogic(pin, 0)
} }
} }
// we expect array as flowdata.data //data from modbus_reader or temperature sensor or twilight sensor or other modbus device
instance.on("data", (flowdata) => { instance.on("0", flowdata => {
console.log(flowdata.data); if(!flowdata.data instanceof Object) return;
if(flowdata.data instanceof Object) // 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(flowdata.data.hasOwnProperty("sender")) if(status == "NOK-twilight_sensor")
{ {
//console.log("sender", flowdata.data); deviceStatuses["twilight_sensor"] = "NOK";
if(flowdata.data.sender == "gettemperature")
{
deviceStatuses["temperature"] = flowdata.data.status;
} }
else if(flowdata.data.sender == "modbus_citysys") else if(status == "NOK-em340" || status == "NOK-em111")
{ {
//elektromer rvo deviceStatuses["em"] = "NOK";
if(flowdata.data.tbdata.hasOwnProperty(edgeName))
{
//rvo
deviceStatuses["rvo"] = {status: flowdata.data.tbdata[edgeName][0]["values"]["status"], tbdata: flowdata.data.tbdata};
} }
else if(flowdata.data.tbdata.hasOwnProperty("twilight_sensor")) else if(status == "NOK-thermometer")
{ {
switchLogic('twilight_sensor', flowdata.data.tbdata["twilight_sensor"]) deviceStatuses["temperature"] = "NOK";
} }
} }
else if(flowdata.data?.values)
instance.send(instanceSendTo.debug, flowdata.data ); {
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; return;
} }
let obj = flowdata.data; 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 line = obj.line;
let force = obj.force; let force = obj.force;
let info = obj.info; let info = obj.info;
@ -845,17 +876,15 @@ exports.install = function(instance) {
else if(obj.command == "turnOnAlarm") turnOnAlarm(); else if(obj.command == "turnOnAlarm") turnOnAlarm();
else if(obj.command == "turnOffAlarm") turnOffAlarm(); else if(obj.command == "turnOffAlarm") turnOffAlarm();
return;
}
//! ake data prichadzaju z cmd_manager.js ??? //! ake data prichadzaju z cmd_manager.js ???
//TODO transform to websocket //TODO transform to websocket
if (Array.isArray(flowdata.data)){ if (Array.isArray(obj)){
rsPort.write(Buffer.from(flowdata.data), function(err) { rsPort.write(Buffer.from(obj), function(err) {
switchLogic(flowdata.data); switchLogic(obj);
instance.send(instanceSendTo.debug, {"WRITE":flowdata.data} ); instance.send(SEND_TO.debug, {"WRITE":obj} );
}); });
} }
}) })
@ -867,12 +896,17 @@ exports.install = function(instance) {
let bytes = []; let bytes = [];
let bits = []; let bits = [];
//Hlavný istič - state_of_main_switch //Hlavný istič - state_of_main_switch
if(deviceStatuses["state_of_main_switch"] == "On") bits.push(0); if(deviceStatuses["state_of_main_switch"] == "On")
else if(deviceStatuses["state_of_main_switch"] == "Off") bits.push(1); {
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 //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY
if(!FLOW.OMS_maintenance_mode) if(!FLOW.OMS_maintenance_mode)
{ {
if(deviceStatuses["rotary_switch_state"] == "Manual") if(deviceStatuses["rotary_switch_state"] == "Manual")
@ -903,38 +937,85 @@ exports.install = function(instance) {
{ {
bits.push(0); bits.push(0);
} }
else bits.push(1); else
{
bits.push(1);
}
//EM //EM
if(deviceStatuses["rvo"].status == "NOK") bits.push(1); if(deviceStatuses["em"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//Teplomer //Teplomer
if(deviceStatuses["temperature"] == "NOK") bits.push(1); if(deviceStatuses["temperature"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//Batéria //Batéria
if(deviceStatuses["battery"] == "NOK") bits.push(1); if(deviceStatuses["battery"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//Zdroj //Zdroj
if(deviceStatuses["power_supply"] == "NOK") bits.push(1); if(deviceStatuses["power_supply"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//MN //MN
if(deviceStatuses["master_node"] == "NOK") bits.push(1); if(deviceStatuses["master_node"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//výpadok napätia na fáze //výpadok napätia na fáze
if(deviceStatuses["no_voltage"] == "NOK") bits.push(1); if(deviceStatuses["no_voltage"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
if(deviceStatuses["twilight_sensor"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0); bits.push(0);
}
// doplnime do 16 bitov (2 byty)
for(let i = bits.length; i < 16; i++)
{
bits.push(0); bits.push(0);
bits.push(0); }
bits.push(0);
bits.push(0);
bits.push(0);
// console.log("calculateStateCode - deviceStatuses", deviceStatuses); // console.log("calculateStateCode - deviceStatuses", deviceStatuses);
// console.log("calculateStateCode", bits); // console.log("calculateStateCode", bits);
@ -959,10 +1040,18 @@ exports.install = function(instance) {
let status = "OK"; let status = "OK";
if(deviceStatuses["rvo"].status == "NOK") if(deviceStatuses["em"] == "NOK")
{ {
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: rvo status is NOK"); let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: EM status is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: rvo status is NOK", deviceStatuses["rvo"].tbdata); 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"; status = "NOK";
} }
@ -979,10 +1068,15 @@ exports.install = function(instance) {
if(status == "OK") if(status == "OK")
{ {
for (const pinIndex of [1, 4, 6]) { 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 (previousValues[pinIndex] === 0) {
if (pinIndex === 6 && FLOW.OMS_maintenance_mode) continue; if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue;
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0"); let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0");
if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type); if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type);
@ -994,7 +1088,7 @@ exports.install = function(instance) {
} }
} }
// battery status. If value is 1 - battery is not ok // battery status. If value is 1 - battery is NOK
if (previousValues[5] === 1) if (previousValues[5] === 1)
{ {
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery"); let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery");
@ -1003,18 +1097,6 @@ exports.install = function(instance) {
status = "NOK"; status = "NOK";
} }
//ak mame telemetriu z elektromeru, posleme
if(deviceStatuses["rvo"].tbdata != undefined)
{
//deviceStatuses["rvo"] = {status: flowdata.data.tbdata[edgeName][0]["values"]["status"], tbdata: flowdata.data.tbdata};
deviceStatuses["rvo"].tbdata[edgeName][0]["values"]["status"] = status;
instance.send(instanceSendTo.tb, deviceStatuses["rvo"].tbdata);
delete deviceStatuses["rvo"].tbdata;
}
//console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding); //console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding);
if(!FLOW.OMS_masterNodeIsResponding) if(!FLOW.OMS_masterNodeIsResponding)
@ -1041,17 +1123,7 @@ exports.install = function(instance) {
if(status == "NOK") if(status == "NOK")
{ {
sendTelemetry({status: "NOK"}, FLOW.OMS_rvo_tbname);
const dataToTb = {
[edgeName]: [
{
"ts": Date.now(),
"values": {status: "NOK"}
}
]
}
instance.send(instanceSendTo.tb, dataToTb);
return false; return false;
} }
@ -1060,8 +1132,8 @@ exports.install = function(instance) {
} }
// we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) // 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) // we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1]
const switchLogic = (...args) => { const switchLogic = (...args) => {
let values = {status: "OK"}; let values = {status: "OK"};
@ -1095,28 +1167,27 @@ exports.install = function(instance) {
let tbname = obj.tbname; let tbname = obj.tbname;
//default value //default value
let value = "Off"; let value = "On";
if(newPinValue === 1) value = "On"; if(newPinValue === 0) value = "Off";
//Hlavný istič //Hlavný istič
if(type === "state_of_main_switch") //! 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])
if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{ {
sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch");
sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", instanceSendTo.tb, instance , "state_of_main_switch");
values["status"] = "NOK"; values["status"] = "NOK";
value = "Off";
deviceStatuses["state_of_main_switch"] = "Off";
} }
else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{ {
sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", instanceSendTo.tb, instance , "state_of_main_switch"); sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch");
value = "On";
deviceStatuses["state_of_main_switch"] = "On";
} }
deviceStatuses["state_of_main_switch"] = value;
} }
//Prevádzkový mód //Prevádzkový mód
@ -1148,9 +1219,9 @@ exports.install = function(instance) {
} }
//console.log('***********************', pin2, pin3) //console.log('***********************', pin2, pin3)
if (pin2 == 0 && pin3 == 1) value = "Manual"; if (pin2 == 1 && pin3 == 0) value = "Manual";
if (pin2 == 0 && pin3 == 0) value = "Off"; if (pin2 == 0 && pin3 == 0) value = "Off";
if (pin2 == 1 && pin3 == 0) value = "Automatic"; if (pin2 == 0 && pin3 == 1) value = "Automatic";
deviceStatuses["rotary_switch_state"] = value; deviceStatuses["rotary_switch_state"] = value;
@ -1158,25 +1229,25 @@ exports.install = function(instance) {
//ak je spracovany, a automatic - tak ho zapnem //ak je spracovany, a automatic - tak ho zapnem
//ak nie je spracovany, iba profil zapisem //ak nie je spracovany, iba profil zapisem
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "rotary_switch_state", value: value}); instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value});
//console.log("rotary_switch_state pin", pin2, pin3, value); //console.log("rotary_switch_state pin", pin2, pin3, value);
} }
//Zdroj - pin 4 //Zdroj - pin 4
else if (type === "power_supply") else if (type === "power_supply")
{ {
if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", instanceSendTo.tb, instance); //sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", instanceSendTo.tb, instance, "power_supply"); sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply");
values["status"] = "NOK"; values["status"] = "NOK";
deviceStatuses["power_supply"] = "NOK"; deviceStatuses["power_supply"] = "NOK";
} }
else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", instanceSendTo.tb, instance); //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", instanceSendTo.tb, instance, "power_supply"); sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply");
deviceStatuses["power_supply"] = "OK"; deviceStatuses["power_supply"] = "OK";
} }
@ -1184,48 +1255,51 @@ exports.install = function(instance) {
//Batéria - pin 5 //Batéria - pin 5
else if (type === "battery") else if (type === "battery")
{ {
if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", instanceSendTo.tb, instance); //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", instanceSendTo.tb, instance, "battery_level"); sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level");
values["status"] = "NOK"; values["status"] = "NOK";
deviceStatuses["battery"] = "NOK"; deviceStatuses["battery"] = "NOK";
} }
else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", instanceSendTo.tb, instance); //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", SEND_TO.tb, instance);
sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", instanceSendTo.tb, instance, "battery_level"); sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level");
deviceStatuses["battery"] = "OK"; deviceStatuses["battery"] = "OK";
} }
} }
//Dverový kontakt - pin 6 //Dverový kontakt - pin 6
else if(type == "door_condition") //! 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 = "closed" : value = "open"; newPinValue === 0 ? value = "open" : value = "closed";
if (newPinValue != previousValues[pinIndex]) if (newPinValue != previousValues[pinIndex])
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, `RVO door ${value}`, "", instanceSendTo.tb, instance, "rvo_door"); //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, `RVO door ${value}`, "", SEND_TO.tb, instance, "rvo_door");
//TODO ? sendNotification("switchLogic", edgeName, "door_value", {value: value}, "", instanceSendTo.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) if (value === "open" && FLOW.OMS_maintenance_mode)
{ {
sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", instanceSendTo.tb, instance, "rvo_door"); sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", SEND_TO.tb, instance, "rvo_door");
} }
if (value === "open" && !FLOW.OMS_maintenance_mode) if (value === "open" && !FLOW.OMS_maintenance_mode)
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.WARNING, "RVO open door out of maintenance mode", "", instanceSendTo.tb, instance); //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", {}, "", instanceSendTo.tb, instance, "rvo_door"); sendNotification("switchLogic", edgeName, "door_has_been_open_without_permision_alarm_is_on", {}, "", SEND_TO.tb, instance, "rvo_door");
values["status"] = "NOK"; values["status"] = "NOK";
//console.log(door_has_been_open_without_permision_alarm_is_on); //console.log(door_has_been_open_without_permision_alarm_is_on);
// zapneme sirenu // zapneme sirenu
turnOnAlarm(); // 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 (value === "closed")
@ -1233,7 +1307,7 @@ exports.install = function(instance) {
if(alarmStatus == "ON") turnOffAlarm(); if(alarmStatus == "ON") turnOffAlarm();
//turnOffAlarm(); //turnOffAlarm();
sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", instanceSendTo.tb, instance, "rvo_door"); sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", SEND_TO.tb, instance, "rvo_door");
} }
deviceStatuses["door_condition"] = value; deviceStatuses["door_condition"] = value;
@ -1272,12 +1346,12 @@ exports.install = function(instance) {
twilightError = true; twilightError = true;
values["status"] = "NOK"; values["status"] = "NOK";
let value = twilight_sensor_array.shift(); let value = twilight_sensor_array.shift();
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, instanceSendTo.tb, instance ); //sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, SEND_TO.tb, instance );
newPinValue = 0; newPinValue = 0;
} }
else if (set.size !== 1 && twilightError) else if (set.size !== 1 && twilightError)
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", instanceSendTo.tb, instance ); //sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance );
twilightError = false; twilightError = false;
twilight_sensor_array.shift(); twilight_sensor_array.shift();
newPinValue = value; newPinValue = value;
@ -1295,7 +1369,7 @@ exports.install = function(instance) {
if(diff >= twilight_sensor_interval * 60 * 1000) if(diff >= twilight_sensor_interval * 60 * 1000)
{ {
const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length; const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length;
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "lux_sensor", value: average}); instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: average});
twilight_sensor = []; twilight_sensor = [];
@ -1303,17 +1377,19 @@ exports.install = function(instance) {
} }
//else console.log("lux_sensor", value, diff); //else console.log("lux_sensor", value, diff);
} }
else
{
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "lux_sensor", value: value});
}
} }
else if(type == "state_of_contactor") else if(type == "state_of_contactor")
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", instanceSendTo.tb, instance ); //sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance );
sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", instanceSendTo.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; deviceStatuses["state_of_contactor"][line] = value;
}
//true, false //true, false
if(value === "On") value = true; if(value === "On") value = true;
@ -1350,7 +1426,7 @@ exports.install = function(instance) {
//a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor
setTimeout(function(){ setTimeout(function(){
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged});
}, time); }, time);
reportLineStatus(line); reportLineStatus(line);
@ -1375,7 +1451,53 @@ exports.install = function(instance) {
if(valueChanged) if(valueChanged)
{ {
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "state_of_breaker", value: value, line: line}); 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"; if(value == "Off") values["status"] = "NOK";
@ -1383,25 +1505,27 @@ exports.install = function(instance) {
deviceStatuses["state_of_breaker"][line] = value; deviceStatuses["state_of_breaker"][line] = value;
reportLineStatus(line); reportLineStatus(line);
} }
values[type] = value; values[type] = value;
let result = checkFinalRVOStatus();
if(!result && line == 0)
{
values["status"] = "NOK";
}
//--
//if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode(); //if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode();
if(pinsData.hasOwnProperty(pinIndex)) if(pinsData.hasOwnProperty(pinIndex))
{ {
let valueChanged = false; let valueChanged = false;
if(newPinValue != previousValues[pinIndex]) valueChanged = true; 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 == "state_of_contactor") valueChanged = true;
if(type == "rotary_switch_state") valueChanged = true; if(type == "rotary_switch_state") valueChanged = true;
@ -1421,7 +1545,6 @@ exports.install = function(instance) {
//console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname); //console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname);
} }
if(valueChanged) if(valueChanged)
{ {
sendTelemetry(values, tbname); sendTelemetry(values, tbname);
@ -1443,10 +1566,6 @@ exports.install = function(instance) {
logger.debug("no pinIndex", pinsData[pinIndex], pinsData); logger.debug("no pinIndex", pinsData[pinIndex], pinsData);
} }
//pin was changed
previousValues[pinIndex] = newPinValue;
} }
@ -1461,7 +1580,7 @@ exports.install = function(instance) {
] ]
} }
instance.send(instanceSendTo.tb, dataToTb); instance.send(SEND_TO.tb, dataToTb);
} }
} }
@ -1792,3 +1911,24 @@ exports.install = function(instance) {
// {
// "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": ""
// }
// }
// }
// ]
// }

File diff suppressed because it is too large Load diff

348
flow/modbus_reader.js Normal file
View file

@ -0,0 +1,348 @@
exports.id = 'modbus_reader';
exports.title = 'Modbus reader';
exports.version = '2.0.0';
exports.group = 'Worksys';
exports.color = '#2134B0';
exports.output = ["red", "white", "yellow"];
exports.click = false;
exports.author = 'Rastislav Kovac';
exports.icon = 'bolt';
exports.readme = `
Modbus requests to modbus devices (electromer, twilight sensor, thermometer.
Component keeps running arround deviceConfig array in "timeoutInterval" intervals. Array items are objects with single modbus devices.
Everything is sent to dido_controller. If requests to device fail (all registers must fail to send NOK status) , we send "NOK-'device'" status to dido_controller.
This device needs to be configured in dido_controller!!! Double check if it is. In dido_controller we calculate final status and all values with status are pushed to tb.
`;
const modbus = require('jsmodbus');
const SerialPort = require('serialport');
const { timeoutInterval, deviceConfig } = require("../databases/modbus_config");
const { sendNotification } = require('./helper/notification_reporter');
const DELAY_BETWEEN_DEVICES = 10000;
const SEND_TO = {
debug: 0,
dido_controller: 1,
tb: 2
};
//to handle NOK and OK sendNotifications s
const numberOfNotResponding = {};
let tbName = null;
let mainSocket;
exports.install = function(instance) {
class SocketWithClients {
constructor () {
this.stream = null;
this.socket = null;
this.clients = {};
this.allValues = {};
this.errors = 0;
this.index = 0;
this.timeoutInterval = 5000;
// we need to go always around for all devices. So we need index value, device address, as well as number of registers for single device
this.deviceAddress = null; // device address (1 - EM340 and 2 for twilight_sensor)
this.indexInDeviceConfig = 0; // first item in deviceConfig
this.lengthOfActualDeviceStream = null;
this.device = null;
// lampSwitchNotification helper variables
this.onNotificationSent = false;
this.offNotificationSent = false;
this.startSocket();
}
startSocket = () => {
let obj = this;
this.socket = new SerialPort("/dev/ttymxc0", {
baudRate: 9600,
})
// we create a client for every deviceAddress ( = address) in list and push them into dictionary
for( let i = 0; i < deviceConfig.length; i++)
{
this.clients[deviceConfig[i].deviceAddress] = new modbus.client.RTU(this.socket, deviceConfig[i].deviceAddress, 2000); // 2000 is timeout in register request, default is 5000, which is too long
}
this.socket.on('error', function(e) {
console.log('socket connection error', e);
if(e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') {
console.log(exports.title + ' Waiting 10 seconds before trying to connect again');
setTimeout(obj.startSocket, 10000);
}
});
this.socket.on('close', function() {
console.log('Socket connection closed ' + exports.title + ' Waiting 10 seconds before trying to connect again');
setTimeout(obj.startSocket, 10000);
});
this.socket.on('open', function () {
console.log("socket connected");
obj.getActualStreamAndDevice();
obj.timeoutInterval = timeoutInterval - DELAY_BETWEEN_DEVICES; // to make sure readout always runs in timeoutinterval we substract DELAY_BETWEEN_DEVICES
})
};
getActualStreamAndDevice = () => {
const dev = deviceConfig[this.indexInDeviceConfig];
this.index = 0;
this.errors = 0;
this.stream = dev.stream;
this.lengthOfActualDeviceStream = dev.stream.length;
this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number
this.device = dev.device; //em340, twilight_sensor
if(this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval);
else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES);
}
readRegisters = () => {
const str = this.stream[this.index];
const register = str.register;
const size = str.size;
const tbAttribute = str.tbAttribute;
let obj = this;
this.clients[this.deviceAddress].readHoldingRegisters(register, size)
.then( function (resp) {
resp = resp.response._body.valuesAsArray; //resp is array of length 1 or 2, f.e. [2360,0]
// console.log(deviceAddress, register, tbAttribute, resp);
//device is responding again after NOK status
if(numberOfNotResponding.hasOwnProperty(obj.device))
{
let message = "";
if(obj.device == "em340")
{
message = "electrometer_ok";
}
else if(obj.device == "twilight_sensor")
{
message = "twilight_sensor_ok";
}
message && sendNotification("modbus_reader: readRegisters", tbName, message, {}, "", SEND_TO.tb, instance);
delete numberOfNotResponding[obj.device];
}
obj.transformResponse(resp, register);
//obj.errors = 0;
obj.index++;
obj.readAnotherRegister();
}).catch (function () {
console.log("errors pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute);
obj.errors++;
if(obj.errors == obj.lengthOfActualDeviceStream)
{
instance.send(SEND_TO.dido_controller, {status: "NOK-" + obj.device}); // NOK-em340, NOK-em111, NOK-twilight_sensor, NOK-thermometer
//todo - neposlalo notification, ked sme vypojili twilight a neposle to do tb, ale do dido ??
if(!numberOfNotResponding.hasOwnProperty(obj.device))
{
let message = "";
if(obj.device == "twilight_sensor")
{
message = "twilight_sensor_nok";
}
else if(obj.device == "em340")
{
message = "electrometer_nok";
}
message && sendNotification("modbus_reader: readingTimeouted", tbName, message, {}, "", SEND_TO.tb, instance);
numberOfNotResponding[obj.device] = 1;
}
obj.errors = 0;
numberOfNotResponding[obj.device] += 1;
}
console.error(require('util').inspect(arguments, {
depth: null
}))
// if reading out of device's last register returns error, we send accumulated allValues to dido_controller (if allValues are not an empty object)
if(obj.index + 1 >= obj.lengthOfActualDeviceStream)
{
if(!isObjectEmpty(obj.allValues)) instance.send(SEND_TO.dido_controller, {values: obj.allValues});
obj.allValues = {};
}
obj.index++;
obj.readAnotherRegister();
})
};
readAnotherRegister = () => {
if(this.index < this.lengthOfActualDeviceStream) setTimeout(this.readRegisters, 0);
else this.setNewStream();
}
transformResponse = (response, register) => {
for (let i = 0; i < this.lengthOfActualDeviceStream; i++) {
let a = this.stream[i];
if (a.register === register)
{
let tbAttribute = a.tbAttribute;
let multiplier = a.multiplier;
let value = this.calculateValue(response, multiplier);
// console.log(register, tbName, tbAttribute, response, a.multiplier, value);
// if(tbName == undefined) return;
if(this.index + 1 < this.lengthOfActualDeviceStream)
{
this.allValues[tbAttribute] = value;
return;
}
const values = {
...this.allValues,
[tbAttribute]: value,
};
this.checkNullVoltage(values);
this.lampSwitchNotification(values);
instance.send(SEND_TO.dido_controller, {values: values});
this.allValues = {};
break;
}
}
}
setNewStream = () =>
{
if(this.lengthOfActualDeviceStream == this.index)
{
if(this.indexInDeviceConfig + 1 == deviceConfig.length)
{
this.indexInDeviceConfig = 0;
}
else
{
this.indexInDeviceConfig += 1;
}
this.getActualStreamAndDevice();
}
}
calculateValue = (response, multiplier) =>
{
let value = 0;
let l = response.length;
if (l === 2)
{
value = (response[1]*(2**16) + response[0]);
if(value >= (2**31)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x80000000), ak vieš robiť logický súčin
{
value = value - "0xFFFFFFFF" + 1;
}
}
else if (l === 1)
{
value = response[0];
if(value >= (2**15)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x8000), ak vieš robiť logický súčin
{
value = value - "0xFFFF" + 1;
}
}
return Math.round(value * multiplier * 10) / 10;
}
checkNullVoltage = (values) => {
if(!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return;
Object.keys(values).map(singleValue => {
if (["Phase_1_voltage", "Phase_2_voltage", "Phase_3_voltage"].includes(singleValue))
{
let l = singleValue.split("_");
let phase = parseInt(l[1]);
if(FLOW.OMS_no_voltage == undefined) FLOW.OMS_no_voltage = new Set();
// console.log(values[singleValue], tbName);
if(values[singleValue] == 0)
{
FLOW.OMS_no_voltage.add(phase);
sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase );
// console.log('no voltage')
}
else
{
FLOW.OMS_no_voltage.delete(phase);
// console.log('voltage detected')
sendNotification("modbus_reader: checkNullVoltage", tbName, "voltage_on_phase_restored", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase);
}
}
})
}
/**
* function sends notification to slack and to tb, if EM total_power value changes more than 500. This should show, that RVO lamps has been switched on or off
*/
lampSwitchNotification = (values) => {
if(!values.hasOwnProperty("total_power")) return;
const actualTotalPower = values.total_power;
if(actualTotalPower > 600 && this.onNotificationSent == false)
{
sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance);
this.onNotificationSent = true;
this.offNotificationSent = false;
}
else if(actualTotalPower <= 600 && this.offNotificationSent == false)
{
sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance);
this.onNotificationSent = false;
this.offNotificationSent = true;
}
}
}
const isObjectEmpty = (objectName) => {
return Object.keys(objectName).length === 0 && objectName.constructor === Object;
}
setTimeout(() => {
mainSocket = new SocketWithClients();
tbName = FLOW.OMS_rvo_tbname;
// this notification is to show, that flow (unipi) has been restarted
sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance);
}, 25000);
}

124
flow/slack_connector.js Normal file
View file

@ -0,0 +1,124 @@
exports.id = 'slack_connector';
exports.title = 'Slack_Connector';
exports.version = '1.0.0';
exports.group = 'Worksys';
exports.color = '#888600';
exports.input = 1;
exports.output = 1;
exports.click = false;
exports.author = 'Jakub Klena';
exports.icon = 'sign-out';
exports.options = { slack_channel: "C071KN2Q8SK", api_key: "", bot_name: "Flow DEMO", bot_icon: ":totaljs:" };
// Slack channel - where to post the messages, can be name like "backend-alerts"
// Bot Name - Name of the "user" that will post these messages, it should be based on which server it is running on.
// Bot Icon - We can use any slack icon (even custom ones uploaded by us) as the "user" profile picture
exports.html = `<div class="padding">
<div class="row">
<div class="col-md-6">
<div data-jc="textbox" data-jc-path="slack_channel" data-jc-config="placeholder:name or id;required:true" class="m">Slack Channel</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div data-jc="textbox" data-jc-path="api_key" data-jc-config="placeholder:api key;required:true" class="m">API Key:</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div data-jc="textbox" data-jc-path="bot_name" data-jc-config="placeholder:Flow DEMO;required:false" class="m">Bot Name</div>
</div>
<div class="col-md-6">
<div data-jc="textbox" data-jc-path="bot_icon" data-jc-config="placeholder:\:totaljs\:;required:true" class="m">Bot Icon</div>
</div>
</div>
</div>`;
exports.readme = `Sends any string received on input to Slack Channel.`;
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");
exports.install = function(instance) {
var can = false;
process.on('uncaughtException', function (err) {
errLogger.error('uncaughtException:', err.message);
errLogger.error(err.stack);
instance.error(err);
});
instance.on('data', function(data) {
if (!can) return;
let str = String(data.data); // Ensuring data get converted to string
let message = {
'channel': instance.options.slack_channel,
'username': instance.options.bot_name,
'icon_emoji': instance.options.bot_icon,
'text': str
};
let headers = {
'Content-type': `application/json`,
'Authorization': `Bearer ${instance.options.api_key}`
};
if (F.is4) {
let opt = {
'method': 'post',
'url': 'https://slack.com/api/chat.postMessage',
'headers': headers,
'body': JSON.stringify(message),
'type': 'json',
'callback': function(err, response) {
if (response && !err) {
var msg = { data: response.body, status: response.status, headers: response.headers, host: response.host, cookies: response.cookies };
instance.send2(msg);
} else if (err) {
errLogger.error('Slack post failed - err:', err, '\n - response was:', response);
instance.error(err, response);
}
}
};
REQUEST(opt);
} else {
U.request('https://slack.com/api/chat.postMessage', ['json', 'post'], JSON.stringify(message), function(err, data, status, headers, host) {
if (response && !err) {
response.data = { data: data, status: status, headers: headers, host: host };
instance.send2(response);
} else if (err) {
errLogger.error('Slack post failed - err:', err, '\n - response was:', response);
instance.error(err, response);
}
}, null, headers);
}
});
instance.reconfigure = function() {
var options = instance.options;
can = options.slack_channel && options.bot_name && options.bot_icon && options.api_key ? true : false;
instance.status(can ? '' : 'Not configured', can ? undefined : 'red');
};
instance.on('options', instance.reconfigure);
instance.reconfigure();
}

187
flow/slack_filter.js Normal file
View file

@ -0,0 +1,187 @@
exports.id = 'slack_filter';
exports.title = 'Slack Filter';
exports.group = 'Citysys';
exports.color = '#30E193';
exports.input = 1;
exports.output = 1;
exports.author = 'Jakub Klena';
exports.icon = 'plug';
exports.version = '1.0.8';
exports.options = { 'name':'', 'types': '["emergency", "critical", "error", "alert"]', 'message_includes':'["is responding again"]', 'tag_on_include':'[{"user_id":"U072JE5JUQG", "includes":["Electrometer", "Twilight sensor"]}]', 'slack_channel':'' };
exports.html = `<div class="padding">
<div class="row">
<div class="col-md-12">
<div data-jc="textbox" data-jc-path="name" data-jc-config="required:true">@(Name of this server)</div>
</div>
<div class="col-md-12">
<div data-jc="textbox" data-jc-path="slack_channel" data-jc-config="required:false">@(Slack channel to receive the alerts)</div>
</div>
<div class="col-md-12">
<div data-jc="textbox" data-jc-path="types" data-jc-config="required:false">@(Watch these types, comma separated names)</div>
</div>
<div class="col-md-12">
<div data-jc="textbox" data-jc-path="message_includes" data-jc-config="required:false">@(Watch messages that include any of the following strings)</div>
</div>
<div class="col-md-12">
<div data-jc="textbox" data-jc-path="tag_on_include" data-jc-config="required:false">@(Tag people if message includes something)</div>
</div>
</div>
</div>`;
exports.readme = `# Slack Filter`;
exports.install = function(instance) {
var running = false;
instance["savedSlackMessages"] = [];
var timer = null;
instance.on('data', function(response) {
if (!running) return;
let value = response.data;
if (typeof value !== 'object') return;
let can = false
var k = Object.keys(value);
var interested = JSON.parse(instance.options.types);
var msg_incl = JSON.parse(instance.options.message_includes);
var tags = JSON.parse(instance.options.tag_on_include);
if (k.length <= 0) return;
if (value[k[0]].length <= 0) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0], 'values')) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0]['values'], '_event')) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0]['values']['_event'], 'type')) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0]['values']['_event'], 'source')) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0]['values']['_event']['source'], 'func')) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0]['values']['_event'], 'message')) return;
if (!Object.prototype.hasOwnProperty.call(value[k[0]][0]['values']['_event'], 'message_data')) return;
let icon = ':totaljs:';
let type = value[k[0]][0]['values']['_event']['type'];
let source = value[k[0]][0]['values']['_event']['source']['func'];
let message = value[k[0]][0]['values']['_event']['message'];
let message_data = value[k[0]][0]['values']['_event']['message_data'];
let tag = '';
switch(type){
case 'debug':
icon = ':beetle:';
break;
case 'info':
icon = ':speech_balloon:';
break;
case 'notice':
icon = ':speech_balloon:';
break;
case 'warning':
icon = ':exclamation:';
break;
case 'alert':
icon = ':warning:';
break;
case 'error':
icon = ':no_entry:';
break;
case 'emergency':
icon = ':fire:';
break;
case 'critical':
icon = ':fire:';
break;
}
// Check if this message includes one of the strings we are watching for
for (const msg of msg_incl){
if (message.includes(msg)){
if (msg == 'is responding again') icon = ':large_green_circle:';
can = true;
break;
}
}
// Check if message is one of the types we are watching for
if (interested.includes(type)){
can = true;
}
if (!can) return;
// Check for each person tags based on what the message includes
for (const person of tags){
for (const msg of person.includes){
if (message.includes(msg)){
tag += '<@'+person.user_id+'> ';
break; // Break out from this person checks as they are already tagged now
}
}
}
// Now that all people are tagged add new line symbol
if (tag != '') tag += '\n';
let send_data = tag+instance.options.name+' '+type.toUpperCase()+'\n*Source*: '+source+'\n*Message*: '+message;
if (message_data) {
send_data += '\nData: '+message_data;
}
let ignore_msg = false
if (message.includes('Configuration of dimming profile to node no')){
for (let i = 0; i < FLOW["savedSlackMessages"].length; i++){
if (FLOW["savedSlackMessages"][i].message == message){
ignore_msg = true;
break;
}
}
if (!ignore_msg){
FLOW["savedSlackMessages"].push({message, 'dateandtime': Date.now()});
if (timer === null){
timer = setTimeout(checkSavedMessages, 60*60000);
}
}
}
if (!ignore_msg){
instance.send2({'msg':send_data,'bot_name':instance.options.name+' '+type.toUpperCase(),'bot_icon':icon,'channel':instance.options.slack_channel});
}
});
function checkSavedMessages(){
var d = Date.now();
d = d - 86400000; // older then 24hr
var a = [];
//Remove msgs older then 24hr
for (let i = 0; i < FLOW["savedSlackMessages"].length; i++){
if (FLOW["savedSlackMessages"][i].dateandtime > d){
a.push(FLOW["savedSlackMessages"][i]);
}
}
FLOW["savedSlackMessages"] = a;
if (FLOW["savedSlackMessages"].length > 0) {
timer = setTimeout(checkSavedMessages, 60*60000);
} else {
timer = null;
}
}
instance.reconfigure = function() {
try {
if (!FLOW["savedSlackMessages"]){
FLOW["savedSlackMessages"] = [];
}
if (instance.options.name) {
instance.status('Running');
running = true;
} else {
instance.status('Please enter name', 'red');
running = false;
}
} catch (e) {
instance.error('Citysys connector: ' + e.message);
}
};
instance.on('options', instance.reconfigure);
instance.reconfigure();
};

View file

@ -1,18 +1,18 @@
exports.id = 'gettemperature'; exports.id = 'thermometer';
exports.title = 'Thermometer'; exports.title = 'Thermometer';
exports.group = 'Worksys'; exports.group = 'Worksys';
exports.color = '#5CB36D'; exports.color = '#5CB36D';
exports.version = '1.0.2'; exports.version = '1.0.3';
exports.output = ["red", "white", "blue"]; exports.output = ["red", "white", "blue"];
exports.author = 'Rastislav Kovac'; exports.author = 'Rastislav Kovac';
exports.icon = 'thermometer-three-quarters'; exports.icon = 'thermometer-three-quarters';
exports.readme = `# Getting temperature values from RVO`; exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`;
const instanceSendTo = { const instanceSendTo = {
debug: 0, debug: 0,
tb: 1, tb: 1,
di_do_controller: 2 dido_controller: 2
} }
//read temperature - frequency //read temperature - frequency
@ -43,7 +43,7 @@ const monitor = log4js.getLogger("monitorLogs");
//monitor.info('info'); //monitor.info('info');
//errLogger.error("some error"); //errLogger.error("some error");
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper');
const dbSettings = TABLE("settings"); const dbSettings = TABLE("settings");
let temperatureAddress = ""; let temperatureAddress = "";
@ -60,7 +60,7 @@ loadSettings();
exports.install = function(instance) { exports.install = function(instance) {
const { exec } = require('child_process'); const { exec } = require('child_process');
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js'); const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
let startRead; let startRead;
let dataToTb; let dataToTb;
@ -130,8 +130,8 @@ exports.install = function(instance) {
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready); monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
instance.send(instanceSendTo.tb, dataToTb); // instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: status}); instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
} }
else parseData(stdout); else parseData(stdout);
} }
@ -164,8 +164,7 @@ exports.install = function(instance) {
if(!isNaN(data)) { if(!isNaN(data)) {
if(counter > 290)
//if ( counter > 290 )
{ {
instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break");
@ -174,21 +173,22 @@ exports.install = function(instance) {
} }
logger.debug("gettemperature", data); logger.debug("gettemperature", data);
const values = {
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
dataToTb = { dataToTb = {
[edgeName]: [ [edgeName]: [
{ {
"ts": Date.now(), "ts": Date.now(),
"values": { "values":values
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
} }
] ]
} }
instance.send(instanceSendTo.tb, dataToTb); instance.send(instanceSendTo.tb, dataToTb);
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "OK"}); instance.send(instanceSendTo.dido_controller, values);
counter = 0; counter = 0;
@ -204,7 +204,7 @@ exports.install = function(instance) {
sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer"); sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer");
instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day");
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "NOK"}); instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
} }
} }
@ -213,7 +213,7 @@ exports.install = function(instance) {
setTimeout(function(){ setTimeout(function(){
start(); start();
}, 3000); }, 15000);
startRead = setInterval(start, timeoutMin * 1000 * 60); startRead = setInterval(start, timeoutMin * 1000 * 60);

View file

@ -245,7 +245,6 @@ exports.install = function(instance) {
} }
instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message }); instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message });
}); });
broker.on('close', function(err) { broker.on('close', function(err) {

View file

@ -6,10 +6,12 @@
"dependencies": { "dependencies": {
"bitwise": "^2.1.0", "bitwise": "^2.1.0",
"easy-crc": "0.0.2", "easy-crc": "0.0.2",
"jsmodbus": "^4.0.6",
"log4js": "^6.3.0", "log4js": "^6.3.0",
"mqtt": "^4.2.6", "mqtt": "^4.2.6",
"nodemailer": "^6.9.7",
"serialport": "^9.2.8", "serialport": "^9.2.8",
"total.js": "^3.4.5" "total.js": "^3.4.13"
}, },
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"

View file

@ -1,174 +0,0 @@
{
"config":{
"isRunning":false,
"debug":true,
"timeoutTime":4000,
"msgWaitTime":17000,
"port":"/dev/ttymxc0",
"port_options":"stty -F /dev/ttymxc0 9600 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke"
},
"private":{
"errBuffer":[
],
"tbBuffer":[
],
"device_index":0,
"cmd_index":0,
"devices":[
{
"name":"Elektrometer 1",
"tb_name":"",
"type":"EM340",
"address":1,
"data":[
],
"cmd":[
],
"timeoutcount":0,
"status":"virtual"
},
{
"name":"Twilight sensor",
"tb_name":"",
"type":"twilight",
"address":2,
"data":[
],
"cmd":[
],
"timeoutcount":0,
"status":"virtual"
}
],
"cmd_tables":[
{
"type":"EM340",
"cmd":[
{
"name":"Voltage L1",
"tb_name":"Phase_1_voltage",
"register":0,
"size":2,
"multiplier":0.1
},
{
"name":"Voltage L2",
"tb_name":"Phase_2_voltage",
"register":2,
"size":2,
"multiplier":0.1
},
{
"name":"Voltage L3",
"tb_name":"Phase_3_voltage",
"register":4,
"size":2,
"multiplier":0.1
},
{
"name":"Current L1",
"tb_name":"Phase_1_current",
"register":12,
"size":2,
"multiplier":0.001
},
{
"name":"Current L2",
"tb_name":"Phase_2_current",
"register":14,
"size":2,
"multiplier":0.001
},
{
"name":"Current L3",
"tb_name":"Phase_3_current",
"register":16,
"size":2,
"multiplier":0.001
},
{
"name":"Power L1",
"tb_name":"Phase_1_power",
"register":18,
"size":2,
"multiplier":0.1
},
{
"name":"Power L2",
"tb_name":"Phase_2_power",
"register":20,
"size":2,
"multiplier":0.1
},
{
"name":"Power L3",
"tb_name":"Phase_3_power",
"register":22,
"size":2,
"multiplier":0.1
},
{
"name":"Power tot",
"tb_name":"total_power",
"register":40,
"size":2,
"multiplier":0.1
},
{
"name":"Energy in",
"tb_name":"total_energy",
"register":52,
"size":2,
"multiplier":0.1
},
{
"name":"PowF L1",
"tb_name":"Phase_1_pow_factor",
"register":46,
"size":1,
"multiplier":0.001
},
{
"name":"PowF L2",
"tb_name":"Phase_2_pow_factor",
"register":47,
"size":1,
"multiplier":0.001
},
{
"name":"PowF L3",
"tb_name":"Phase_3_pow_factor",
"register":48,
"size":1,
"multiplier":0.001
},
{
"name":"PowF",
"tb_name":"power_factor",
"register":49,
"size":1,
"multiplier":0.001
}
]
},
{
"type":"twilight",
"cmd":[
{
"name":"Twilight",
"tb_name":"twilight_sensor",
"register":60,
"size":2,
"multiplier":1
}
]
}
]
}
}