Compare commits

..

1 commit

Author SHA1 Message Date
rasta5man
a30a8588a7 Actual code running on CEZ rvo 2024-04-13 21:46:15 +02:00
15 changed files with 8104 additions and 7228 deletions

5
config
View file

@ -1,12 +1,13 @@
name : Total.js Flow
default_timezone : Europe/Bratislava
default_timezone : Europe/Bratislava
// Packages settings
package#flow (Object) : { url: '/' }
table.relays : line:number|tbname:string|contactor:number|profile:string
table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
table.nodes : node:number|tbname:string|line:number|dimming: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.pins : pin:string|type:string|line:number
table.notifications : key:string|weight:string|sk:string|en:string

View file

@ -1,114 +0,0 @@
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,6 +32,3 @@ key:string|weight:string|sk:string|en:string
+|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_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 = 'dido_controller';
exports.title = 'DIDO_Controller';
exports.version = '2.0.0';
exports.id = 'di_do_controller';
exports.title = 'DI_DO_Controller';
exports.version = '1.0.0';
exports.group = 'Worksys';
exports.color = '#2134B0';
exports.input = 3;
exports.input = 1;
exports.output = ["red", "white", "yellow"];
exports.click = false;
exports.author = 'Daniel Segeš';
@ -11,11 +11,11 @@ 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 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.
@ -60,7 +60,7 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda
//globals
//FIRMWARE version
FLOW.OMS_edge_fw_version = "2024-07-08";//rok-mesiac-den
FLOW.OMS_edge_fw_version = "2022-05-12";//rok-mesiac-den
FLOW.OMS_edgeName = "";
FLOW.OMS_maintenance_mode = false;
@ -78,7 +78,7 @@ FLOW.OMS_temperature_adress = "";//cmd_manager
let alarmStatus = "OFF";
const SEND_TO = {
const instanceSendTo = {
debug: 0,
tb: 1,
cmd_manager: 2
@ -152,7 +152,7 @@ exports.install = function(instance) {
/*
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
"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},
@ -178,7 +178,7 @@ exports.install = function(instance) {
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["rvo"] = {status: "OK"};//elektromer rvo
deviceStatuses["temperature"] = "OK";//templomer
deviceStatuses["battery"] = "OK";//Batéria
deviceStatuses["power_supply"] = "OK";//Zdroj
@ -187,12 +187,11 @@ exports.install = function(instance) {
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");
instance.send(instanceSendTo.cmd_manager, "reload_relays");
});
*/
@ -264,13 +263,14 @@ exports.install = function(instance) {
{
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 );
//sendNotification("set port ", edgeName, ERRWEIGHT.CRITICAL, "local database is corrupted", "", instanceSendTo.tb, instance, null );
sendNotification("set port ", edgeName, "local_database_is_corrupted", {}, "", instanceSendTo.tb, instance );
}
}
if(pinsData[key].type == "state_of_contactor")
{
let pin = key - 1;
if(controller_type === "unipi") pin = key;
@ -298,13 +298,13 @@ exports.install = function(instance) {
]
}
instance.send(SEND_TO.tb, dataToTb);
instance.send(instanceSendTo.tb, dataToTb);
let time = 3*1000;
setTimeout(function(){
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"});
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "buildTasks"});
sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", SEND_TO.tb, instance );
sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", instanceSendTo.tb, instance );
monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version);
}, time);
@ -320,7 +320,7 @@ exports.install = function(instance) {
rsPort.on('error', function(err) {
logger.debug("rsPort opened error - failed", err.message);
instance.send(SEND_TO.debug, err.message);
instance.send(instanceSendTo.debug, err.message);
errorHandler.sendMessageToService( exports.title + " rsPort opened error - failed: " + err.message);
})
@ -342,7 +342,7 @@ exports.install = function(instance) {
})
}).catch(function (reason) {
//instance.send(SEND_TO.debug, exports.title + " runSyncExec - promise rejected:" + reason);
//instance.send(instanceSendTo.debug, exports.title + " runSyncExec - promise rejected:" + reason);
errLogger.error( exports.title + " runSyncExec - promise rejected:" + reason);
errorHandler.sendMessageToService( exports.title + " runSyncExec - promise rejected:" + reason);
@ -448,6 +448,7 @@ exports.install = function(instance) {
// dev: 'input',
// mode: 'Simple'
// },
ws.onmessage = function(data) {
data = JSON.parse(data.data);
@ -471,7 +472,7 @@ exports.install = function(instance) {
deviceStatuses["temperature"] = "OK";
previousValues["temperature"] = value;
instance.send(SEND_TO.tb, dataToTb);
instance.send(instanceSendTo.tb, dataToTb);
}
return;
}
@ -488,18 +489,15 @@ exports.install = function(instance) {
ws.on('error', (err) => {
monitor.info('websocket error, reconnect')
instance.send(SEND_TO.debug, err.message);
instance.send(instanceSendTo.debug, err.message);
clearInterval(startRequests);
ws = null;
setTimeout(handleWebSocket, 1000);
startRequests = null;
})
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...");
@ -521,7 +519,6 @@ exports.install = function(instance) {
instance.on("close", () => {
if(rsPort) rsPort.close();
if(ws) ws.close();
clearInterval(sendRebuildTasksAt11);
})
loadAllDb();
@ -638,14 +635,14 @@ exports.install = function(instance) {
//console.log(values);
instance.send(SEND_TO.tb, dataToTb);
instance.send(instanceSendTo.tb, dataToTb);
}
function turnOnLine(line, pin, force, info)
{
instance.send(SEND_TO.debug, "turn on line " + line );
instance.send(instanceSendTo.debug, "turn on line " + line );
if(force == undefined) force = false;
if(line == 0)
@ -679,7 +676,7 @@ exports.install = function(instance) {
{
if(relaysData[line].contactor == 1)
{
instance.send(SEND_TO.debug, "line is already on " + line );
instance.send(instanceSendTo.debug, "line is already on " + line );
logger.debug("turnOnLine: line is already on: ", line);
return;
@ -689,7 +686,7 @@ exports.install = function(instance) {
// if(!rsPort.isOpen && !ws)
if(!rsPort && !ws)
{
errLogger.error("dido controller - port or websocket is not opened");
errLogger.error("di do controller - port or websocket is not opened");
return;
}
@ -720,7 +717,7 @@ exports.install = function(instance) {
//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)
switchLogic(pin, 1)
}
}
@ -756,7 +753,7 @@ exports.install = function(instance) {
{
if(relaysData[line].contactor == 0)
{
instance.send(SEND_TO.debug, "line is already off " + line );
instance.send(instanceSendTo.debug, "line is already off " + line );
logger.debug("turnOffLine: line already off:", line);
return;
@ -794,97 +791,71 @@ exports.install = function(instance) {
else if(ws)
{
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
//monitor.info("turnOffLine pin (relay)", pin);
// 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)
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 => {
instance.on("data", (flowdata) => {
console.log(flowdata.data);
if(!flowdata.data instanceof Object) return;
if(flowdata.data instanceof Object)
{
let obj = flowdata.data;
let line = obj.line;
let force = obj.force;
let info = obj.info;
if(flowdata.data.hasOwnProperty("sender"))
{
//console.log("sender", flowdata.data);
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();
if(flowdata.data.sender == "gettemperature")
{
deviceStatuses["temperature"] = flowdata.data.status;
}
else if(flowdata.data.sender == "modbus_citysys")
{
//elektromer rvo
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"))
{
switchLogic('twilight_sensor', flowdata.data.tbdata["twilight_sensor"])
}
}
instance.send(instanceSendTo.debug, flowdata.data );
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();
return;
}
//! ake data prichadzaju z cmd_manager.js ???
//TODO transform to websocket
if (Array.isArray(obj)){
if (Array.isArray(flowdata.data)){
rsPort.write(Buffer.from(obj), function(err) {
switchLogic(obj);
rsPort.write(Buffer.from(flowdata.data), function(err) {
switchLogic(flowdata.data);
instance.send(SEND_TO.debug, {"WRITE":obj} );
instance.send(instanceSendTo.debug, {"WRITE":flowdata.data} );
});
}
})
@ -896,17 +867,12 @@ exports.install = function(instance) {
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
//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
if(!FLOW.OMS_maintenance_mode)
{
if(deviceStatuses["rotary_switch_state"] == "Manual")
@ -937,88 +903,41 @@ exports.install = function(instance) {
{
bits.push(0);
}
else
{
bits.push(1);
}
else bits.push(1);
//EM
if(deviceStatuses["em"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
if(deviceStatuses["rvo"].status == "NOK") bits.push(1);
else bits.push(0);
//Teplomer
if(deviceStatuses["temperature"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
if(deviceStatuses["temperature"] == "NOK") bits.push(1);
else bits.push(0);
//Batéria
if(deviceStatuses["battery"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
if(deviceStatuses["battery"] == "NOK") bits.push(1);
else bits.push(0);
//Zdroj
if(deviceStatuses["power_supply"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
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);
}
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["no_voltage"] == "NOK") bits.push(1);
else bits.push(0);
if(deviceStatuses["twilight_sensor"] == "NOK")
{
bits.push(1);
}
else
{
bits.push(0);
}
bits.push(0);
bits.push(0);
bits.push(0);
bits.push(0);
bits.push(0);
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);
//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());
@ -1040,18 +959,10 @@ exports.install = function(instance) {
let status = "OK";
if(deviceStatuses["em"] == "NOK")
if(deviceStatuses["rvo"].status == "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");
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: rvo status is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: rvo status is NOK", deviceStatuses["rvo"].tbdata);
status = "NOK";
}
@ -1068,15 +979,10 @@ exports.install = function(instance) {
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) {
for (const pinIndex of [1, 4, 6]) {
if (previousValues[pinIndex] === 0) {
if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue;
if (pinIndex === 6 && FLOW.OMS_maintenance_mode) continue;
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0");
if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type);
@ -1088,7 +994,7 @@ exports.install = function(instance) {
}
}
// battery status. If value is 1 - battery is NOK
// battery status. If value is 1 - battery is not ok
if (previousValues[5] === 1)
{
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery");
@ -1097,6 +1003,18 @@ exports.install = function(instance) {
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);
if(!FLOW.OMS_masterNodeIsResponding)
@ -1123,7 +1041,17 @@ exports.install = function(instance) {
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;
}
@ -1132,8 +1060,8 @@ exports.install = function(instance) {
}
// we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]]
// we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1]
// we pass array to function in case of rsPort ==> switchLogic([55,3,0,1])
// we pass two values in case of websocket ==> switchLogic("relay1_03",1)
const switchLogic = (...args) => {
let values = {status: "OK"};
@ -1167,27 +1095,28 @@ exports.install = function(instance) {
let tbname = obj.tbname;
//default value
let value = "On";
if(newPinValue === 0) value = "Off";
let value = "Off";
if(newPinValue === 1) value = "On";
//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(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";
deviceStatuses["state_of_main_switch"] = "Off";
value = "Off";
}
else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
else if (newPinValue === 0 && 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";
sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", instanceSendTo.tb, instance , "state_of_main_switch");
value = "On";
}
deviceStatuses["state_of_main_switch"] = value;
}
//Prevádzkový mód
@ -1219,9 +1148,9 @@ exports.install = function(instance) {
}
//console.log('***********************', pin2, pin3)
if (pin2 == 1 && pin3 == 0) value = "Manual";
if (pin2 == 0 && pin3 == 1) value = "Manual";
if (pin2 == 0 && pin3 == 0) value = "Off";
if (pin2 == 0 && pin3 == 1) value = "Automatic";
if (pin2 == 1 && pin3 == 0) value = "Automatic";
deviceStatuses["rotary_switch_state"] = value;
@ -1229,25 +1158,25 @@ exports.install = function(instance) {
//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});
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_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])
if (newPinValue === 1 && 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");
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", instanceSendTo.tb, instance);
sendNotification("switchLogic", edgeName, "power_supply_has_disconnected_input", {}, "", instanceSendTo.tb, instance, "power_supply");
values["status"] = "NOK";
deviceStatuses["power_supply"] = "NOK";
}
else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
else if (newPinValue === 0 && 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");
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", instanceSendTo.tb, instance);
sendNotification("switchLogic", edgeName, "power_supply_works_correctly", {}, "", instanceSendTo.tb, instance, "power_supply");
deviceStatuses["power_supply"] = "OK";
}
@ -1255,51 +1184,48 @@ exports.install = function(instance) {
//Batéria - pin 5
else if (type === "battery")
{
if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
if (newPinValue === 0 && 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");
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", instanceSendTo.tb, instance);
sendNotification("switchLogic", edgeName, "battery_level_is_low", {}, "", instanceSendTo.tb, instance, "battery_level");
values["status"] = "NOK";
deviceStatuses["battery"] = "NOK";
}
else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
else if (newPinValue === 1 && 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");
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", instanceSendTo.tb, instance);
sendNotification("switchLogic", edgeName, "battery_level_is_ok", {}, "", instanceSendTo.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")
else if(type == "door_condition")
{
newPinValue === 0 ? value = "open" : value = "closed";
newPinValue === 0 ? value = "closed" : value = "open";
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");
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, `RVO door ${value}`, "", instanceSendTo.tb, instance, "rvo_door");
//TODO ? sendNotification("switchLogic", edgeName, "door_value", {value: value}, "", instanceSendTo.tb, instance, "rvo_door");
}
if (value === "open" && FLOW.OMS_maintenance_mode)
{
sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", SEND_TO.tb, instance, "rvo_door");
sendNotification("switchLogic", edgeName, "door_has_been_open", {}, "", instanceSendTo.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");
//sendNotification("switchLogic", edgeName, ERRWEIGHT.WARNING, "RVO open door out of maintenance mode", "", instanceSendTo.tb, instance);
sendNotification("switchLogic", edgeName, "door_has_been_open_without_permision_alarm_is_on", {}, "", instanceSendTo.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();
//zapneme sirenu
turnOnAlarm();
}
if (value === "closed")
@ -1307,7 +1233,7 @@ exports.install = function(instance) {
if(alarmStatus == "ON") turnOffAlarm();
//turnOffAlarm();
sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", SEND_TO.tb, instance, "rvo_door");
sendNotification("switchLogic", edgeName, "door_has_been_closed", {}, "", instanceSendTo.tb, instance, "rvo_door");
}
deviceStatuses["door_condition"] = value;
@ -1346,12 +1272,12 @@ exports.install = function(instance) {
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 );
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, instanceSendTo.tb, instance );
newPinValue = 0;
}
else if (set.size !== 1 && twilightError)
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance );
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", instanceSendTo.tb, instance );
twilightError = false;
twilight_sensor_array.shift();
newPinValue = value;
@ -1369,7 +1295,7 @@ exports.install = function(instance) {
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});
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "lux_sensor", value: average});
twilight_sensor = [];
@ -1377,19 +1303,17 @@ exports.install = function(instance) {
}
//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")
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance );
//sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", instanceSendTo.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
if(value === "On") value = true;
@ -1426,7 +1350,7 @@ exports.install = function(instance) {
//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});
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged});
}, time);
reportLineStatus(line);
@ -1451,53 +1375,7 @@ exports.install = function(instance) {
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];
}
}
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "state_of_breaker", value: value, line: line});
}
if(value == "Off") values["status"] = "NOK";
@ -1505,27 +1383,25 @@ exports.install = function(instance) {
deviceStatuses["state_of_breaker"][line] = value;
reportLineStatus(line);
}
values[type] = value;
let result = checkFinalRVOStatus();
if(!result && line == 0)
{
values["status"] = "NOK";
}
//--
//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(newPinValue != previousValues[pinIndex]) valueChanged = true;
if(type == "state_of_contactor") valueChanged = true;
if(type == "rotary_switch_state") valueChanged = true;
@ -1536,7 +1412,7 @@ exports.install = function(instance) {
if(FLOW.OMS_rvo_tbname == "")
{
console.log("FLOW.OMS_rvo_tbname is EMPTY");
console.log("FLOW.OMS_rvo_tbname is EMPTY");
}
if(FLOW.OMS_rvo_tbname == tbname)
@ -1545,6 +1421,7 @@ exports.install = function(instance) {
//console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname);
}
if(valueChanged)
{
sendTelemetry(values, tbname);
@ -1566,6 +1443,10 @@ exports.install = function(instance) {
logger.debug("no pinIndex", pinsData[pinIndex], pinsData);
}
//pin was changed
previousValues[pinIndex] = newPinValue;
}
@ -1580,7 +1461,7 @@ exports.install = function(instance) {
]
}
instance.send(SEND_TO.tb, dataToTb);
instance.send(instanceSendTo.tb, dataToTb);
}
}
@ -1911,24 +1792,3 @@ 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": ""
// }
// }
// }
// ]
// }

View file

@ -1,18 +1,18 @@
exports.id = 'thermometer';
exports.id = 'gettemperature';
exports.title = 'Thermometer';
exports.group = 'Worksys';
exports.color = '#5CB36D';
exports.version = '1.0.3';
exports.version = '1.0.2';
exports.output = ["red", "white", "blue"];
exports.author = 'Rastislav Kovac';
exports.icon = 'thermometer-three-quarters';
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`;
exports.readme = `# Getting temperature values from RVO`;
const instanceSendTo = {
debug: 0,
tb: 1,
dido_controller: 2
di_do_controller: 2
}
//read temperature - frequency
@ -43,7 +43,7 @@ const monitor = log4js.getLogger("monitorLogs");
//monitor.info('info');
//errLogger.error("some error");
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper');
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
const dbSettings = TABLE("settings");
let temperatureAddress = "";
@ -60,7 +60,7 @@ loadSettings();
exports.install = function(instance) {
const { exec } = require('child_process');
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js');
let startRead;
let dataToTb;
@ -76,9 +76,9 @@ exports.install = function(instance) {
})
const start = function() {
const start = function(){
try {
try{
if(FLOW.OMS_controller_type === "unipi")
{
@ -130,8 +130,8 @@ exports.install = function(instance) {
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
// instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
instance.send(instanceSendTo.tb, dataToTb);
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: status});
}
else parseData(stdout);
}
@ -162,9 +162,10 @@ exports.install = function(instance) {
logger.debug("gettemperature", data);
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");
@ -173,22 +174,21 @@ exports.install = function(instance) {
}
logger.debug("gettemperature", data);
const values = {
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
dataToTb = {
[edgeName]: [
{
"ts": Date.now(),
"values":values
"values": {
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
}
]
}
instance.send(instanceSendTo.tb, dataToTb);
instance.send(instanceSendTo.dido_controller, values);
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "OK"});
counter = 0;
@ -204,7 +204,7 @@ exports.install = function(instance) {
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.dido_controller, {status: "NOK-thermometer"});
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "NOK"});
}
}
@ -213,7 +213,7 @@ exports.install = function(instance) {
setTimeout(function(){
start();
}, 15000);
}, 3000);
startRead = setInterval(start, timeoutMin * 1000 * 60);

1135
flow/modbus_citysys.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,348 +0,0 @@
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);
}

View file

@ -1,124 +0,0 @@
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();
}

View file

@ -1,187 +0,0 @@
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

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

View file

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

174
saved_data/modbus_settings Normal file
View file

@ -0,0 +1,174 @@
{
"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
}
]
}
]
}
}