Compare commits
15 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7350c117d | ||
|
|
e3c68f3d86 | ||
|
|
7e20c7d3f0 | ||
| 8929cc3a53 | |||
|
|
464e74fe26 | ||
|
|
f4ffef94b9 | ||
|
|
1b7aae4590 | ||
|
|
6d468acd6f | ||
|
|
9e2ab3ccea | ||
|
|
2f659a164b | ||
|
|
54038a06f8 | ||
|
|
d289a99d07 | ||
|
|
31fe341ef1 | ||
|
|
86c7b3a942 | ||
|
|
ac2ccc1c61 |
15 changed files with 7230 additions and 8106 deletions
5
config
5
config
|
|
@ -1,13 +1,12 @@
|
||||||
name : Total.js Flow
|
name : Total.js Flow
|
||||||
|
default_timezone : Europe/Bratislava
|
||||||
default_timezone : Europe/Bratislava
|
|
||||||
|
|
||||||
// Packages settings
|
// Packages settings
|
||||||
package#flow (Object) : { url: '/' }
|
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
114
databases/modbus_config.js
Normal 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 };
|
||||||
|
|
@ -31,4 +31,7 @@ key:string|weight:string|sk:string|en:string
|
||||||
+|voltage_on_phase_restored|NOTICE|Napätie na fáze č. ${phase} bolo obnovené|Voltage on phase no. ${phase} has been restored|...............
|
+|voltage_on_phase_restored|NOTICE|Napätie na fáze č. ${phase} bolo obnovené|Voltage on phase no. ${phase} has been restored|...............
|
||||||
+|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|...............
|
||||||
|
|
|
||||||
8203
flow/cmd_manager.js
8203
flow/cmd_manager.js
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,163 +1,163 @@
|
||||||
class DataToTbHandler
|
class DataToTbHandler
|
||||||
{
|
{
|
||||||
constructor(index) {
|
constructor(index) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
|
||||||
this.previousValues = {};
|
this.previousValues = {};
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
this.messageCounter = 0;
|
this.messageCounter = 0;
|
||||||
|
|
||||||
this.sender = "";
|
this.sender = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
dump()
|
dump()
|
||||||
{
|
{
|
||||||
console.log("----------------------------");
|
console.log("----------------------------");
|
||||||
console.log("previousValues", this.previousValues);
|
console.log("previousValues", this.previousValues);
|
||||||
console.log("----------------------------");
|
console.log("----------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
setSender(sender)
|
setSender(sender)
|
||||||
{
|
{
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmptyObject( obj ) {
|
isEmptyObject( obj ) {
|
||||||
for ( var name in obj ) {
|
for ( var name in obj ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToTb(dataToTb, instance)
|
sendToTb(dataToTb, instance)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(!FLOW.OMS_brokerready)
|
if(!FLOW.OMS_brokerready)
|
||||||
{
|
{
|
||||||
return dataToTb;
|
return dataToTb;
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = Object.keys(dataToTb);
|
let keys = Object.keys(dataToTb);
|
||||||
|
|
||||||
if(keys.length == 0)
|
if(keys.length == 0)
|
||||||
{
|
{
|
||||||
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let tbname = keys[0];
|
let tbname = keys[0];
|
||||||
let ts;
|
let ts;
|
||||||
|
|
||||||
let arrayOfValues = dataToTb[tbname];
|
let arrayOfValues = dataToTb[tbname];
|
||||||
let arrayOfValuesToSend = [];
|
let arrayOfValuesToSend = [];
|
||||||
|
|
||||||
for(let i = 0; i < arrayOfValues.length; i++)
|
for(let i = 0; i < arrayOfValues.length; i++)
|
||||||
{
|
{
|
||||||
ts = arrayOfValues[i].ts;
|
ts = arrayOfValues[i].ts;
|
||||||
|
|
||||||
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
||||||
|
|
||||||
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
||||||
|
|
||||||
//console.log("sendToTb------------>after", values);
|
//console.log("sendToTb------------>after", values);
|
||||||
|
|
||||||
if(!this.isEmptyObject(values))
|
if(!this.isEmptyObject(values))
|
||||||
{
|
{
|
||||||
arrayOfValuesToSend.push({ts: ts, values: values});
|
arrayOfValuesToSend.push({ts: ts, values: values});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arrayOfValuesToSend.length == 0)
|
if(arrayOfValuesToSend.length == 0)
|
||||||
{
|
{
|
||||||
//if(this.debug) console.log("data not sent - empty array");
|
//if(this.debug) console.log("data not sent - empty array");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let dataToTb = {
|
let dataToTb = {
|
||||||
[tbname]: [
|
[tbname]: [
|
||||||
{
|
{
|
||||||
"ts": Date.now(),
|
"ts": Date.now(),
|
||||||
"values": values
|
"values": values
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
this.messageCounter++;
|
this.messageCounter++;
|
||||||
|
|
||||||
let dataToTbModified = {
|
let dataToTbModified = {
|
||||||
[tbname]: arrayOfValuesToSend
|
[tbname]: arrayOfValuesToSend
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance);
|
//console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance);
|
||||||
if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend);
|
if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend);
|
||||||
|
|
||||||
instance.send(this.index, dataToTbModified);
|
instance.send(this.index, dataToTbModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDiffTimestamp(key)
|
getDiffTimestamp(key)
|
||||||
{
|
{
|
||||||
let seconds = 60*60;//1h
|
let seconds = 60*60;//1h
|
||||||
//seconds = 1;//for testing
|
//seconds = 1;//for testing
|
||||||
|
|
||||||
//TODO set different value for given key!!!
|
//TODO set different value for given key!!!
|
||||||
//if(key == "status") seconds = 2*60*60;//2h
|
//if(key == "status") seconds = 2*60*60;//2h
|
||||||
|
|
||||||
let timestampDiffToRemoveKey = seconds*1000;
|
let timestampDiffToRemoveKey = seconds*1000;
|
||||||
|
|
||||||
return timestampDiffToRemoveKey;
|
return timestampDiffToRemoveKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareValuesForTb(tbname, timestamp, values)
|
prepareValuesForTb(tbname, timestamp, values)
|
||||||
{
|
{
|
||||||
let keys = Object.keys(values);
|
let keys = Object.keys(values);
|
||||||
if(!this.previousValues.hasOwnProperty(tbname))
|
if(!this.previousValues.hasOwnProperty(tbname))
|
||||||
{
|
{
|
||||||
this.previousValues[tbname] = {};
|
this.previousValues[tbname] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
||||||
|
|
||||||
for(let i = 0; i < keys.length; i++)
|
for(let i = 0; i < keys.length; i++)
|
||||||
{
|
{
|
||||||
let key = keys[i];
|
let key = keys[i];
|
||||||
let value = values[key];
|
let value = values[key];
|
||||||
|
|
||||||
if(!this.previousValues[tbname].hasOwnProperty(key))
|
if(!this.previousValues[tbname].hasOwnProperty(key))
|
||||||
{
|
{
|
||||||
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.previousValues[tbname][key].value === value)
|
if(this.previousValues[tbname][key].value === value)
|
||||||
{
|
{
|
||||||
let diff = timestamp - this.previousValues[tbname][key].ts;
|
let diff = timestamp - this.previousValues[tbname][key].ts;
|
||||||
|
|
||||||
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
||||||
if(diff > timestampDiffToRemoveKey)
|
if(diff > timestampDiffToRemoveKey)
|
||||||
{
|
{
|
||||||
this.previousValues[tbname][key].ts = Date.now();
|
this.previousValues[tbname][key].ts = Date.now();
|
||||||
//if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter);
|
//if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
delete values[key];
|
delete values[key];
|
||||||
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.previousValues[tbname][key].value = value;
|
this.previousValues[tbname][key].value = value;
|
||||||
this.previousValues[tbname][key].ts = timestamp;
|
this.previousValues[tbname][key].ts = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = DataToTbHandler;
|
module.exports = DataToTbHandler;
|
||||||
File diff suppressed because it is too large
Load diff
348
flow/modbus_reader.js
Normal file
348
flow/modbus_reader.js
Normal 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
124
flow/slack_connector.js
Normal 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
187
flow/slack_filter.js
Normal 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();
|
||||||
|
};
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -76,9 +76,9 @@ exports.install = function(instance) {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const start = function(){
|
const start = function() {
|
||||||
|
|
||||||
try{
|
try {
|
||||||
|
|
||||||
if(FLOW.OMS_controller_type === "unipi")
|
if(FLOW.OMS_controller_type === "unipi")
|
||||||
{
|
{
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -162,10 +162,9 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
logger.debug("gettemperature", data);
|
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");
|
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);
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ exports.install = function(instance) {
|
||||||
let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
|
let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
|
||||||
let mqtt_username = responseSettings[0]["mqtt_username"];
|
let mqtt_username = responseSettings[0]["mqtt_username"];
|
||||||
let mqtt_port = responseSettings[0]["mqtt_port"];
|
let mqtt_port = responseSettings[0]["mqtt_port"];
|
||||||
|
|
||||||
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
|
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
|
||||||
|
|
||||||
opts = {
|
opts = {
|
||||||
|
|
@ -204,7 +204,7 @@ exports.install = function(instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
connectToTbServer();
|
connectToTbServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectToTbServer()
|
function connectToTbServer()
|
||||||
{
|
{
|
||||||
|
|
@ -217,16 +217,16 @@ exports.install = function(instance) {
|
||||||
instance.status("Connected", "green");
|
instance.status("Connected", "green");
|
||||||
monitor.info("MQTT broker connected");
|
monitor.info("MQTT broker connected");
|
||||||
|
|
||||||
brokerready = true;
|
brokerready = true;
|
||||||
FLOW.OMS_brokerready = brokerready;
|
FLOW.OMS_brokerready = brokerready;
|
||||||
wsmqtt_status = 'connected';
|
wsmqtt_status = 'connected';
|
||||||
});
|
});
|
||||||
|
|
||||||
broker.on('reconnect', function() {
|
broker.on('reconnect', function() {
|
||||||
instance.status("Reconnecting", "yellow");
|
instance.status("Reconnecting", "yellow");
|
||||||
brokerready = false;
|
brokerready = false;
|
||||||
|
|
||||||
FLOW.OMS_brokerready = brokerready;
|
FLOW.OMS_brokerready = brokerready;
|
||||||
});
|
});
|
||||||
|
|
||||||
broker.on('message', function(topic, message) {
|
broker.on('message', function(topic, message) {
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue