Compare nodesDb; thermometer instead evok; no status.table

This commit is contained in:
rasta5man 2024-12-02 17:06:49 +01:00
parent f63ac50497
commit 5270a898a3
13 changed files with 1255 additions and 4969 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -40,7 +40,6 @@ exports.install = async function(instance) {
const dbNodes = TABLE("nodes");
const dbRelays = TABLE("relays");
const dbSettings = TABLE("settings");
const dbStatus = TABLE("status");
const dbPins = TABLE("pins");
const dbNotifications = TABLE("notifications");
@ -50,14 +49,12 @@ exports.install = async function(instance) {
const responseSettings = await promisifyBuilder(dbSettings.find());
const responseNodes = await promisifyBuilder(dbNodes.find());
const responsePins = await promisifyBuilder(dbPins.find());
const responseStatus = await promisifyBuilder(dbStatus.find());
const responseRelays = await promisifyBuilder(dbRelays.find());
const response = await promisifyBuilder(dbNotifications.find());
dbs.pinsData = makeMapFromDbResult(responsePins, "pin");
dbs.relaysData = makeMapFromDbResult(responseRelays, "line");
dbs.nodesData = makeMapFromDbResult(responseNodes, "node");
dbs.statusData = responseStatus[0];
dbs.notificationsData = makeMapFromDbResult(response, "key");
//+|354|nodesdata.....+|482|nodesdata....

View file

@ -140,8 +140,8 @@
"component": "debug",
"tab": "1612772287426",
"name": "to TB",
"x": 323.75,
"y": 429,
"x": 312.75,
"y": 426,
"connections": {},
"disabledio": {
"input": [
@ -270,9 +270,7 @@
"y": 557.3500061035156,
"connections": {},
"disabledio": {
"input": [
0
],
"input": [],
"output": []
},
"state": {
@ -291,14 +289,12 @@
"id": "1615809128443",
"component": "debug",
"tab": "1611921777196",
"name": "Debug",
"name": "tempToTb",
"x": 598.8833312988281,
"y": 654.3500061035156,
"connections": {},
"disabledio": {
"input": [
0
],
"input": [],
"output": []
},
"state": {
@ -1142,7 +1138,7 @@
"output": []
},
"state": {
"text": "847.42 MB / 985.68 MB",
"text": "846.37 MB / 985.68 MB",
"color": "gray"
},
"options": {
@ -1172,7 +1168,7 @@
"output": []
},
"state": {
"text": "5.79 GB / 7.26 GB",
"text": "5.77 GB / 7.26 GB",
"color": "gray"
},
"options": {
@ -1628,7 +1624,7 @@
"output": []
},
"state": {
"text": "1.3% / 74.34 MB",
"text": "6.2% / 70.96 MB",
"color": "gray"
},
"options": {
@ -1960,6 +1956,10 @@
{
"index": "0",
"id": "1621340721628"
},
{
"index": "0",
"id": "1732889185927"
}
]
},
@ -2141,9 +2141,9 @@
"options": {
"slack_channel": "C071KN2Q8SK",
"tag_on_include": "[{\"user_id\":\"U072JE5JUQG\", \"includes\":[\"Electrometer\", \"Twilight sensor\"]}]",
"message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\"]",
"message_includes": "[\"is responding again\", \"Lamps have turned\", \"Flow has been restarted\", \"Node db has changed\"]",
"types": "[\"emergency\", \"critical\", \"error\", \"alert\"]",
"name": "rvo_senica_1_10.0.0.141"
"name": "rvo_senica_46_10.0.0.133"
},
"color": "#30E193",
"notes": ""
@ -2342,7 +2342,7 @@
"clientid": "",
"port": "2764",
"host": "192.168.252.2",
"topic": "u118"
"topic": "u133"
},
"color": "#888600",
"notes": ""
@ -2780,7 +2780,137 @@
"options": {},
"color": "#F6BB42",
"notes": ""
},
{
"id": "1732700042559",
"component": "nodesdb_change_check",
"tab": "1612772287426",
"name": "Nodes DB change check",
"x": 250.88333129882812,
"y": 1813.2333984375,
"connections": {
"0": [
{
"index": "0",
"id": "1732700071298"
},
{
"index": "0",
"id": "1732700642917"
}
]
},
"disabledio": {
"input": [],
"output": []
},
"state": {
"text": "",
"color": "gray"
},
"options": {},
"color": "#888600",
"notes": ""
},
{
"id": "1732700057052",
"component": "virtualwirein",
"tab": "1612772287426",
"name": "db-init",
"x": 71.75,
"y": 1814,
"connections": {
"0": [
{
"index": "0",
"id": "1732700042559"
}
]
},
"disabledio": {
"input": [],
"output": []
},
"state": {
"text": "db-init",
"color": "gray"
},
"options": {
"wirename": "db-init"
},
"color": "#303E4D",
"notes": ""
},
{
"id": "1732700071298",
"component": "debug",
"tab": "1612772287426",
"name": "nodesChange",
"x": 548.8833312988281,
"y": 1875.2333984375,
"connections": {},
"disabledio": {
"input": [],
"output": []
},
"state": {
"text": "Enabled",
"color": "gray"
},
"options": {
"type": "data",
"repository": false,
"enabled": true
},
"color": "#967ADC",
"notes": ""
},
{
"id": "1732700642917",
"component": "virtualwireout",
"tab": "1612772287426",
"name": "tb-push",
"x": 544.8833312988281,
"y": 1769,
"connections": {},
"disabledio": {
"input": [],
"output": []
},
"state": {
"text": "tb-push",
"color": "gray"
},
"options": {
"wirename": "tb-push"
},
"color": "#303E4D",
"notes": ""
},
{
"id": "1732889185927",
"component": "debug",
"tab": "1611921777196",
"name": "tempToDido",
"x": 594.8833312988281,
"y": 745,
"connections": {},
"disabledio": {
"input": [],
"output": []
},
"state": {
"text": "Enabled",
"color": "gray"
},
"options": {
"type": "data",
"repository": false,
"enabled": true
},
"color": "#967ADC",
"notes": ""
}
],
"version": 615
}
}

View file

@ -56,7 +56,6 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda
momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale zmena by sa mala ukázať aj na platforme
*/
const dbStatus = TABLE("status");
const { errLogger, logger, monitor } = require('./helper/logger');
const SerialPort = require('serialport');
const WebSocket = require('ws');
@ -90,9 +89,6 @@ const SEND_TO = {
cmd_manager: 2
}
const TIME_AFTER_TEMPERATURE_NOK_STATUS = 3600; //seconds
const DIFFERENCE_TO_SEND_TEMPERATURE = 0.31;
exports.install = function(instance) {
@ -108,8 +104,7 @@ exports.install = function(instance) {
//process.exit(1);
})
// temperature value is initialized to -1000. It can be literally anything, we just needs to be able to enter if block in ws.onmessage function, when first temperatere data comes
let previousValues = {temperature: {value: -1000, lastTimeTemperatureReceived: Date.now() / 1000}};
let previousValues = {};
let rsPortReceivedData = [];
//to be able to get proper twilight values, when
@ -163,15 +158,12 @@ exports.install = function(instance) {
rvoTbName = SETTINGS.rvoTbName;
pinsData = GLOBALS.pinsData;
relaysData = GLOBALS.relaysData;
statusData = GLOBALS.statusData;
tbHandler = new DataToTbHandler(SEND_TO.tb)
tbHandler.setSender(exports.title);
controller_type = SETTINGS.controller_type //"lm" or "unipi" //logicMachine
if(controller_type == "") controller_type = "lm";
deviceStatus["temperature"] = statusData.thermometer;
console.log(exports.title, "controller type: ", controller_type);
@ -379,39 +371,19 @@ exports.install = function(instance) {
data = JSON.parse(data.data);
// data comes in array except of "temperature" ==> it comes as an object
if(isObject(data))
{
let value = data['value'];
const values = {};
previousValues["temperature"]["lastTimeTemperatureReceived"] = data['time'];
// we received data from thermometer, but thermometer status is NOK:
if(deviceStatus["temperature"] === "NOK")
{
await writeThermometerStatusToDb("OK");
sendRvoStatus();
}
// temperature value comes very often. To handle it, we check if it change for more than 0.3 degrees, if yes, we send to TB
if(Math.abs(previousValues["temperature"]["value"] - value) > DIFFERENCE_TO_SEND_TEMPERATURE)
{
previousValues["temperature"]["value"] = value;
values['temperature'] = value;
sendTelemetry(values, rvoTbName);
}
return;
}
// we do not handle temperature from evok any more => we return, if temperature comes:
if(isObject(data)) return;
data.map(item => {
let value = item['value'];
let pin = item["dev"] + item["circuit"]; // for example "relay1_03" or "input1_01"
if(pin == undefined) return;
if(pin == undefined) return;
switchLogic(pin, value);
})
}
ws.on('error', (err) => {
monitor.info('websocket error, reconnect')
@ -464,19 +436,19 @@ exports.install = function(instance) {
{
let value = 0;
if(onOrOff == "on") value = 1;
if(value == 1 && SETTINGS.maintenance_mode) return;
alarmStatus = "OFF";
if(value == 1) alarmStatus = "ON";
if(rsPort)
{
let arr = [0x55];
arr.push(13);
arr.push(0);
arr.push(value);
rsPort.write(Buffer.from(arr), function(err) {
logger.debug(`sirena - ${onOrOff}`);
});
@ -605,7 +577,7 @@ exports.install = function(instance) {
//data from modbus_reader or temperature sensor or twilight sensor or other modbus device
instance.on("0", flowdata => {
if(!flowdata.data instanceof Object) return;
if(!isObject(flowdata.data)) return;
// console.log('***********************', flowdata.data)
instance.send(SEND_TO.debug, flowdata.data);
@ -622,10 +594,8 @@ exports.install = function(instance) {
{
deviceStatus["em"] = "NOK";
}
//"NOK-thermometer" comes just from LM. Unipi handles thermometer from ws evok.
else if(status == "NOK-thermometer")
{
previousValues["temperature"]["lastTimeTemperatureReceived"] = null;
deviceStatus["temperature"] = "NOK";
}
}
@ -637,10 +607,8 @@ exports.install = function(instance) {
instance.send(SEND_TO.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]});
deviceStatus["twilight_sensor"] = "OK"
}
//"temperature" comes just from LM. Unipi handles thermometer from ws evok.
else if(values.hasOwnProperty("temperature"))
{
previousValues["temperature"]["lastTimeTemperatureReceived"] = Date.now() / 1000; //time in seconds
deviceStatus["temperature"] = "OK";
}
// EM
@ -669,8 +637,8 @@ exports.install = function(instance) {
let force = obj.force;
let info = obj.info;
if(obj.command == "turnOn") turnLine("on", line, undefined, force, info);
else if(obj.command == "turnOff") turnLine("off", line, undefined, force, info);
if(obj.command == "on") turnLine("on", line, undefined, force, info);
else if(obj.command == "off") turnLine("off", line, undefined, force, info);
else if(obj.command == "turnOnAlarm") turnAlarm("on");
else if(obj.command == "turnOffAlarm") turnAlarm("off");
@ -822,15 +790,13 @@ exports.install = function(instance) {
let byte = bytesToInt([byte1, byte0]);
//console.log("calculateStateCode -------------------", byte);
return byte;
}
async function sendRvoStatus() {
// test if dbLoaded is ok to check
//if(!FLOW.dbLoaded) return;
if(SETTINGS === undefined) return;
const table = {
@ -849,21 +815,11 @@ exports.install = function(instance) {
};
for (const phase of SETTINGS.no_voltage) dataToTb[`phase_${phase}_status`] = 0;
//thermometer did not send data for more than a hour. Time in seconds
if(deviceStatus["temperature"] === "OK")
{
if(previousValues["temperature"]["lastTimeTemperatureReceived"] + TIME_AFTER_TEMPERATURE_NOK_STATUS < Date.now() / 1000)
{
// to be able to calculate proper RVO status, we need to await writeThermometerStatusToDb function
await writeThermometerStatusToDb("NOK");
dataToTb["thermometer_status"] = 0;
}
}
dataToTb["status"] = checkRvoStatus();
dataToTb["statecode"] = calculateStateCode();
//console.log(dataToTb);
sendTelemetry(dataToTb, rvoTbName);
}
@ -876,8 +832,7 @@ exports.install = function(instance) {
let status = "OK";
for (const [key, value] of Object.entries(deviceStatus)) {
//if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK";
if(["em", "twilight_sensor"].includes(key) && value == "NOK") status = "NOK";
if(["em", "twilight_sensor", "temperature"].includes(key) && value == "NOK") status = "NOK";
}
if(status == "OK")
@ -899,6 +854,7 @@ exports.install = function(instance) {
if(!SETTINGS.masterNodeIsResponding) status = "NOK";
if(SETTINGS.no_voltage.size > 0) status = "NOK";
// console.log("rvo status",status)
return status;
}
@ -1258,7 +1214,6 @@ exports.install = function(instance) {
}
if(newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue;
if(rvoTbName == tbname) sendRvoStatus();
if(Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname);
}
@ -1277,24 +1232,6 @@ exports.install = function(instance) {
}
function writeThermometerStatusToDb(status) {
return new Promise(function(resolve, reject) {
dbStatus.modify({ thermometer: status }).make(function(builder) {
builder.callback(function(err, response) {
if(!err)
{
deviceStatus["temperature"] = status;
console.log(`Wrote to db status: thermometer ${status}`);
resolve("ok")
}
reject("nok")
});
});
})
}
function isObject (item) {
return (typeof item === "object" && !Array.isArray(item) && item !== null);
}

View file

@ -0,0 +1,70 @@
exports.id = 'nodesdb_change_check';
exports.title = 'Nodes DB change check';
exports.group = 'Worksys';
exports.color = '#888600';
exports.version = '1.0.2';
exports.icon = 'sign-out';
exports.input = 1;
exports.output = 1;
exports.readme = `Check, if nodes.table db changed compared to original database`;
const fs = require('fs');
const path = require('path');
const { sendNotification } = require('./helper/notification_reporter');
const nodesOriginalFile = path.join(__dirname, '../databases/nodes_original/', 'nodes_original.table');
exports.install = function(instance) {
function compareArrays(array1, array2) {
let message = "";
let areEqual = true;
let zmenene = []
if (array1.length !== array2.length) {
message += "Nezhoda v pocte nodov. "
}
const set1 = new Set(array1.map(obj => JSON.stringify(obj)));
const set2 = new Set(array2.map(obj => JSON.stringify(obj)));
for (const objStr of set1) {
if (!set2.has(objStr)) {
zmenene.push(objStr)
areEqual = false;
} else {
set2.delete(objStr);
}
}
if(!areEqual) {
message += `Aktualne nody: ${zmenene.toString()}. Zmenene proti originalu: ${Array.from(set2).join(' ')}`;
sendNotification("Nodesdb_changecheck", FLOW.GLOBALS.settings.rvoTbName, "nodes_db_changed", "", message, 0, instance);
}
else console.log("Arrays are equal.");
console.log(message)
}
instance.on("data", _ => {
let nodesData = FLOW.GLOBALS.nodesData;
// we check if nodes.table has changed compared to nodes_original.table (we have array of nodes e.g. [{node:255, tbname: "agruhuwhgursuhgo34hgsdiguhrr"}]
const nodes_actual = Object.keys(nodesData).map(node => ({[node]: nodesData[node].tbname}))
let nodes_original = fs.readFileSync(nodesOriginalFile, { encoding: 'utf8', flag: 'r' });
try {
nodes_original = JSON.parse(nodes_original);
} catch(e) {
console.log(e)
}
setTimeout(() => compareArrays(nodes_actual, nodes_original),10000);
})
}

View file

@ -1,122 +1,98 @@
exports.id = 'thermometer';
exports.title = 'Thermometer';
exports.group = 'Worksys';
exports.color = '#5CB36D';
exports.input = 1;
exports.version = '1.0.3';
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`;
const { errLogger, logger, monitor } = require('./helper/logger');
const SEND_TO = {
debug: 0,
tb: 1,
dido_controller: 2
}
//read temperature - frequency
let timeoutMin = 5;//minutes
exports.install = function(instance) {
const { exec } = require('child_process');
const { sendNotification } = require('./helper/notification_reporter');
let startRead;
let counter = 0;
let rvoTbName = "";
let temperatureAddress = "";
logger.debug(exports.title, "installed");
instance.on("close", function(){
clearInterval(startRead);
})
const main = function() {
try {
if(FLOW.GLOBALS.settings.controller_type === "unipi")
{
clearInterval(startRead);
return;
}
if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined";
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
if(!error)
{
parseData(stdout)
return;
}
sendNotification("main", rvoTbName, "thermometer_is_not_responding", {}, {"Error": error}, SEND_TO.tb, instance, "thermometer");
monitor.info("Thermometer is not responding", error);
instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"});
});
}
catch(err) {
errLogger.error(exports.title, err);
clearInterval(startRead);
}
}
const parseData = function(data) {
data = parseFloat(data);
logger.debug("gettemperature", data);
if(!isNaN(data)) {
if(counter > 290)
{
instance.send(SEND_TO.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break");
sendNotification("parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer");
}
logger.debug("gettemperature", data);
const values = {
"temperature": Number(data.toFixed(2)),
}
instance.send(SEND_TO.dido_controller, {values: values});
counter = 0;
} else {
counter++;
monitor.info("gettemperature err", counter, data);
//ked je problem 1 den
let day = 24 * 60 / timeoutMin;
if ( counter > day && counter < day + 2 ) {
//sendNotification("parseData", rvoTbName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", SEND_TO.tb, instance, "thermometer");
sendNotification("parseData", rvoTbName, "thermometer_sends_invalid_data", {}, "", SEND_TO.tb, instance, "thermometer");
instance.send(SEND_TO.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day");
instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"});
}
}
}
instance.on("data", _ => {
temperatureAddress = FLOW.GLOBALS.settings.temperature_address;
rvoTbName = FLOW.GLOBALS.settings.rvoTbName;
startRead = setInterval(main, timeoutMin * 1000 * 60);
main();
})
};
exports.id = 'thermometer';
exports.title = 'Thermometer';
exports.group = 'Worksys';
exports.color = '#5CB36D';
exports.input = 1;
exports.version = '1.0.3';
exports.output = ["red", "white", "blue"];
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`;
const { errLogger, logger, monitor } = require('./helper/logger');
const SEND_TO = {
debug: 0,
tb: 1,
dido_controller: 2
}
//read temperature - frequency
let timeoutMin = 5;//minutes
let NUMBER_OF_FAILURES_TO_SEND_ERROR = 13;
exports.install = function(instance) {
const { exec } = require('child_process');
const { sendNotification } = require('./helper/notification_reporter');
let startRead;
let counter = 0;
let rvoTbName = "";
let temperatureAddress = "";
logger.debug(exports.title, "installed");
instance.on("close", function(){
clearInterval(startRead);
})
const main = function() {
try {
if(temperatureAddress === "") throw "Thermometer: temperatureAddress is not defined";
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
if(!error)
{
parseData(stdout)
return;
}
counter++;
if(counter == NUMBER_OF_FAILURES_TO_SEND_ERROR) sendNotification("Thermometer_main", rvoTbName, "thermometer_is_not_responding", {}, {"Error": error}, SEND_TO.tb, instance, "thermometer");
monitor.info("Thermometer is not responding", error);
instance.send(SEND_TO.dido_controller, {status: "NOK-thermometer"});
});
}
catch(err) {
errLogger.error(exports.title, err);
clearInterval(startRead);
}
}
const parseData = function(data) {
data = parseFloat(data);
logger.debug("Thermometer", data);
if(isNaN(data)) {
errLogger.error("Thermometer sends invalid data");
return;
}
if(counter > NUMBER_OF_FAILURES_TO_SEND_ERROR) //1 hour
{
instance.send(SEND_TO.debug, "Thermometer - temperature data are comming again");
sendNotification("Thermometer_parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer");
}
const values = {
"temperature": Number(data.toFixed(2)),
}
instance.send(SEND_TO.dido_controller, {values: values});
counter = 0;
}
instance.on("data", _ => {
temperatureAddress = FLOW.GLOBALS.settings.temperature_address;
rvoTbName = FLOW.GLOBALS.settings.rvoTbName;
startRead = setInterval(main, timeoutMin * 1000 * 60);
setTimeout(main, 20000);
})
};