1127 lines
35 KiB
JavaScript
1127 lines
35 KiB
JavaScript
exports.id = 'modbus_citysys';
|
||
exports.title = 'Modbus_citysys';
|
||
exports.version = '1.0.0';
|
||
exports.group = 'Worksys';
|
||
exports.color = '#2134B0';
|
||
exports.input = 1;
|
||
exports.output = ["red", "white", "blue", "orange"];
|
||
exports.click = false;
|
||
exports.author = 'Jakub Klena';
|
||
exports.icon = 'bolt';
|
||
exports.options = { edge: "undefined" };
|
||
|
||
exports.html = `<div class="padding">
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div data-jc="textbox" data-jc-path="edge" data-jc-config="placeholder:undefined;required:true" class="m">Edge TB Name</div>
|
||
</div>
|
||
</div>
|
||
</div>`;
|
||
|
||
exports.readme = `# Energomonitor
|
||
## Outputs
|
||
|
||
- *Red* - ERROR output (can connect to filewriter or something)
|
||
- *White* - STATUS output (answers to your commands, ERRORS and WARNINGS from your commands go both to this and to their own outputs, so they get logged)
|
||
- *Blue* - TB output (pure data for TB)
|
||
`;
|
||
|
||
|
||
const instanceSendTo = {
|
||
error: 0,
|
||
debug: 1,
|
||
tb: 2,
|
||
di_do_controller: 3
|
||
}
|
||
|
||
const DataToTbHandler = require('./helper/DataToTbHandler.js');
|
||
const { sendNotification } = require('./helper/notification_reporter.js');
|
||
const dbRelays = TABLE("relays");
|
||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||
let tbname;
|
||
|
||
async function loadSettings()
|
||
{
|
||
//todo global FLOW.OMS_edgeName is making problem, so we load it here as well, it should not be
|
||
let responseRelays = await promisifyBuilder(dbRelays.find());
|
||
FLOW.OMS_edgeName = responseRelays[0]["tbname"];
|
||
tbname = FLOW.OMS_edgeName;
|
||
}
|
||
|
||
loadSettings();
|
||
|
||
exports.install = function(instance) {
|
||
const SerialPort = require('serialport');
|
||
const { exec } = require('child_process');
|
||
const fs = require("fs");
|
||
const filepath = F.path.root("saved_data/modbus_settings");
|
||
const backup_filepath = F.path.root("saved_data/modbus_settings_backup");
|
||
|
||
const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler.js');
|
||
const errorHandler = new ErrorToServiceHandler();
|
||
|
||
let receivedDataArray = [];
|
||
|
||
|
||
instance.CONFIG = {
|
||
"isRunning": false,
|
||
"debug": true,
|
||
"timeoutTime": 10000,
|
||
"msgWaitTime": 1000,
|
||
"port": "/dev/ttymxc1",
|
||
//"port_options": "stty -F /dev/ttymxc1 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke"
|
||
"port_options": "stty -F /dev/ttymxc1 9600 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke"
|
||
};
|
||
|
||
let PRIVATEVARS = {
|
||
"errBuffer": [], // Buffer for error messages
|
||
"tbBuffer": [], // Buffer for TB push messages
|
||
"device_index": 0,
|
||
"cmd_index": -1,
|
||
"devices": [
|
||
/*{
|
||
"name": "Elektrometer 1",
|
||
"tb_name": "EOzNMgZ9n43qPbjXmy7zwdA2DKdYvW5e6pxGRrVa",
|
||
"type": "EM111",
|
||
"address": 1,
|
||
"data":[],
|
||
"cmd":[],
|
||
"timeoutcount":0,
|
||
"status":"virtual"
|
||
},*/
|
||
// {
|
||
// "name": "Elektrometer 2",
|
||
// "tb_name": "pJX1ObgmqGZ54DMyYL7aDdkEVdve38WKRzwjNrQ9",
|
||
// "type": "EM111",
|
||
// "address": 2,
|
||
// "data":[],
|
||
// "cmd":[],
|
||
// "timeoutcount":0,
|
||
// "status":"virtual"
|
||
// },
|
||
// {
|
||
// "name": "Elektrometer 3",
|
||
// "tb_name": "XRvmwNz8QPblKp41GD7lKVkJrLVYoBO92dMegn6W",
|
||
// "type": "EM111",
|
||
// "address": 3,
|
||
// "data":[],
|
||
// "cmd":[],
|
||
// "timeoutcount":0,
|
||
// "status":"virtual"
|
||
// },
|
||
// {
|
||
// "name": "Elektrometer 4",
|
||
// "tb_name": "oRO8rjaBDy21qPQJzW7oD9ApK3xmNleVZg9Ed4Gw",
|
||
// "type": "EM111",
|
||
// "address": 4,
|
||
// "data":[],
|
||
// "cmd":[],
|
||
// "timeoutcount":0,
|
||
// "status":"virtual"
|
||
// },
|
||
{
|
||
"name": "Elektrometer 1",
|
||
"tb_name": "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV",
|
||
"type": "EM340",
|
||
"address": 1,
|
||
"data":[],
|
||
"cmd":[],
|
||
"timeoutcount":0,
|
||
"status":"virtual"
|
||
}
|
||
],
|
||
"cmd_tables": [
|
||
{
|
||
"type":"EM340",
|
||
"cmd":[
|
||
{
|
||
"name": "Voltage L1",
|
||
"tb_name": "a",
|
||
"register": 0,
|
||
"size": 2,
|
||
"multiplier": 0.1
|
||
},
|
||
{
|
||
"name": "Voltage L2",
|
||
"tb_name": "b",
|
||
"register": 2,
|
||
"size": 2,
|
||
"multiplier": 0.1
|
||
},
|
||
{
|
||
"name": "Voltage L3",
|
||
"tb_name": "c",
|
||
"register": 4,
|
||
"size": 2,
|
||
"multiplier": 0.1
|
||
},
|
||
{
|
||
"name": "Current L1",
|
||
"tb_name": "d",
|
||
"register": 12,
|
||
"size": 2,
|
||
"multiplier": 0.001
|
||
},
|
||
{
|
||
"name": "Current L2",
|
||
"tb_name": "e",
|
||
"register": 14,
|
||
"size": 2,
|
||
"multiplier": 0.001
|
||
},
|
||
{
|
||
"name": "Current L3",
|
||
"tb_name": "f",
|
||
"register": 16,
|
||
"size": 2,
|
||
"multiplier": 0.001
|
||
}
|
||
|
||
|
||
// {
|
||
// "name": "Power factor",
|
||
// "tb_name": "power_factor",
|
||
// "register": 14,
|
||
// "size": 1,
|
||
// "multiplier": 0.001
|
||
// },
|
||
// {
|
||
// "name": "Frequency",
|
||
// "tb_name": "frequency",
|
||
// "register": 15,
|
||
// "size": 1,
|
||
// "multiplier": 0.1
|
||
// },
|
||
// {
|
||
// "name": "Energy",
|
||
// "tb_name": "consumption",
|
||
// "register": 16,
|
||
// "size": 2,
|
||
// "multiplier": 0.1
|
||
// }
|
||
]
|
||
}
|
||
]
|
||
};
|
||
|
||
let ERRWEIGHT = {
|
||
EMERGENCY: "emergency", // System unusable
|
||
ALERT: "alert", // Action must be taken immidiately
|
||
CRITICAL: "critical", // Component unable to function
|
||
ERROR: "error", // Error, but component able to recover from it
|
||
WARNING: "warning", // Possibility of error, system running futher
|
||
NOTICE: "notice", // Significant message but not an error, things user might want to know about
|
||
INFO: "informational", // Info
|
||
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
|
||
};
|
||
|
||
instance.currentData = function(){
|
||
let resp = [];
|
||
for (let f = 0; f < PRIVATEVARS.devices.length; f++){
|
||
let dev = PRIVATEVARS.devices[f];
|
||
for (let e = 0; e < dev.data.length; e++){
|
||
let d = dev.data[e];
|
||
resp.push({
|
||
"name": dev.name+" - "+d.name,
|
||
"value": d.value
|
||
});
|
||
}
|
||
}
|
||
return resp;
|
||
};
|
||
|
||
instance.configList = function(){
|
||
let resp = [];
|
||
/*let data = PRIVATEVARS.feeds;
|
||
for (let a = 0; a < data.length; a++){
|
||
for (let i = 0; i < instance.CONFIG.feeds.length; i++){
|
||
let feed = instance.CONFIG.feeds[i];
|
||
if (feed.name === data[a].id){
|
||
for (let b = 0; b < data[a].streams.length; b++){
|
||
for (let j = 0; j < feed.streams.length; j++){
|
||
let stream = feed.streams[j];
|
||
if (stream.name === data[a].streams[b].id){
|
||
data[a].streams[b]["exists"] = true;
|
||
data[a].streams[b]["currently"] = stream;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
resp.push({
|
||
"name":"Device manager",
|
||
"icon":"tasks",
|
||
"_show":false,
|
||
"js_func":"energoDevManager",
|
||
"data": data
|
||
});*/
|
||
|
||
return resp;
|
||
}
|
||
|
||
let timeoutInterval = null;
|
||
let msgWaitInterval = null;
|
||
let port = null;
|
||
let myEdge = "undefined";
|
||
let starter = null;
|
||
instance.status("Loading...", "red");
|
||
|
||
|
||
instance.availableCommands = [
|
||
{
|
||
"name": "Status",
|
||
"cmd": "qStatus",
|
||
"icon": "stream",
|
||
"func": function(body){
|
||
let a = true;
|
||
if (timeoutInterval === null){
|
||
a = false;
|
||
}
|
||
let b = true;
|
||
if (msgWaitInterval === null){
|
||
b = false;
|
||
}
|
||
let st = {
|
||
"isRunning":instance.CONFIG.isRunning,
|
||
"timeoutInterval":a,
|
||
"msgWaitInterval":b,
|
||
"CONFIG":instance.CONFIG
|
||
};
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": st
|
||
};
|
||
}
|
||
},
|
||
{
|
||
"name": "Start Reading",
|
||
"cmd": "sStart",
|
||
"icon": "play",
|
||
"func": function(body){
|
||
/*if (running === false){
|
||
startCmdWaitInterval();
|
||
running = true;
|
||
return "Reading started !";
|
||
} else {
|
||
return "Reading already active !";
|
||
}*/
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": "WIP"
|
||
};
|
||
}
|
||
},
|
||
{
|
||
"name": "Stop Reading",
|
||
"cmd": "sStop",
|
||
"icon": "stop",
|
||
"func": function(body){
|
||
/*if (running === true){
|
||
stopCmdWaitInterval();
|
||
stopTimeoutInterval();
|
||
running = false;
|
||
return "Reading stopped !";
|
||
} else {
|
||
return "Reading already inactive !";
|
||
}*/
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": "WIP"
|
||
};
|
||
}
|
||
},
|
||
{
|
||
"name": "Read current data",
|
||
"cmd": "qCurrentData",
|
||
"icon": "chart-bar",
|
||
"func": function(body){
|
||
let resp = instance.currentData();
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": resp
|
||
};
|
||
}
|
||
},
|
||
{
|
||
"name": "Save current config",
|
||
"cmd": "saveConfig",
|
||
"icon": "save",
|
||
"func": function(body){
|
||
|
||
instance.set("config", JSON.stringify(instance.CONFIG));
|
||
instance.set("private", JSON.stringify(PRIVATEVARS));
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": "done"
|
||
};
|
||
}
|
||
},
|
||
{
|
||
"name": "Toggle debug",
|
||
"cmd": "sDebug",
|
||
"icon": "comment-dots",
|
||
"func": function(body){
|
||
|
||
if (instance.CONFIG.debug){
|
||
instance.CONFIG.debug = false;
|
||
instance.set("config", JSON.stringify(instance.CONFIG));
|
||
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": "debug OFF"
|
||
};
|
||
} else {
|
||
instance.CONFIG.debug = true;
|
||
instance.set("config", JSON.stringify(instance.CONFIG));
|
||
|
||
return {
|
||
"type": "ok",
|
||
"timestamp": humanReadableTimeAndDate(),
|
||
"resp": "debug ON"
|
||
};
|
||
}
|
||
|
||
}
|
||
}
|
||
];
|
||
|
||
|
||
|
||
function saveData(){
|
||
if (checkFile(filepath)){
|
||
let content = undefined;
|
||
try {
|
||
content = fs.readFileSync(filepath);
|
||
} catch (err){
|
||
console.log("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message});
|
||
|
||
//sendError("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message});
|
||
}
|
||
|
||
if (content !== undefined){
|
||
try {
|
||
fs.writeFileSync(backup_filepath, content, "utf8");
|
||
} catch (err) {
|
||
//sendError("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to save backup of configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message});
|
||
console.log("saveData", myEdge, ERRWEIGHT.ERROR, "Unable to save backup of configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message});
|
||
}
|
||
}
|
||
}
|
||
|
||
let a = {
|
||
"config":instance.CONFIG,
|
||
"private":PRIVATEVARS
|
||
};
|
||
|
||
try {
|
||
fs.writeFileSync(filepath, JSON.stringify(a), "utf8");
|
||
} catch (err) {
|
||
//sendError("saveData", myEdge, ERRWEIGHT.CRITICAL, "Unable to save configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message});
|
||
console.log("saveData", myEdge, ERRWEIGHT.CRITICAL, "Unable to save configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message});
|
||
}
|
||
}
|
||
|
||
function loadData(){
|
||
let content = undefined;
|
||
//console.log(filepath);
|
||
if (checkFile(filepath)){
|
||
try {
|
||
content = fs.readFileSync(filepath);
|
||
} catch (err){
|
||
//sendError("loadData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message});
|
||
console.log("loadData", myEdge, ERRWEIGHT.ERROR, "Unable to read original configuration !", {"name":instance.name, "id":instance.id, "file":filepath, "err":err.message});
|
||
}
|
||
} else {
|
||
if (checkFile(backup_filepath)){
|
||
try {
|
||
content = fs.readFileSync(backup_filepath);
|
||
} catch (err){
|
||
//sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "Unable to read backup configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message});
|
||
console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "Unable to read backup configuration !", {"name":instance.name, "id":instance.id, "file":backup_filepath, "err":err.message});
|
||
}
|
||
if (content !== undefined){
|
||
//sendError("loadData", myEdge, ERRWEIGHT.WARNING, "No configuration, loading from backup !", {"name":instance.name, "id":instance.id});
|
||
console.log("loadData", myEdge, ERRWEIGHT.WARNING, "No configuration, loading from backup !", {"name":instance.name, "id":instance.id});
|
||
}
|
||
} else {
|
||
//sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "No configuration, not even backup !", {"name":instance.name, "id":instance.id});
|
||
console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "No configuration, not even backup !", {"name":instance.name, "id":instance.id, "filepath": filepath});
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (content !== undefined){
|
||
let a = JSON.parse(content);
|
||
instance.send(instanceSendTo.debug, a);
|
||
let c = a.config;
|
||
let p = a.private;
|
||
|
||
|
||
if (c === undefined){
|
||
//sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "Configuration not found !", {"name":instance.name, "id":instance.id});
|
||
console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "Configuration not found !", {"name":instance.name, "id":instance.id});
|
||
instance.status("Error - no config", "red");
|
||
} else if (p === undefined){
|
||
//sendError("loadData", myEdge, ERRWEIGHT.CRITICAL, "Privatevars not found !", {"name":instance.name, "id":instance.id});
|
||
console.log("loadData", myEdge, ERRWEIGHT.CRITICAL, "Privatevars not found !", {"name":instance.name, "id":instance.id});
|
||
instance.status("Error - no private vars", "red");
|
||
} else {
|
||
instance.CONFIG = c;
|
||
PRIVATEVARS = p;
|
||
|
||
// Daj kazdemu device jeho tabulku prikazu
|
||
for (let i = 0; i < PRIVATEVARS.devices.length; i++){
|
||
let device = PRIVATEVARS.devices[i];
|
||
for (let j = 0; j < PRIVATEVARS.cmd_tables.length; j++){
|
||
let table = PRIVATEVARS.cmd_tables[j];
|
||
|
||
if (device.type === table.type){
|
||
PRIVATEVARS.devices[i].cmd = table.cmd;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (myEdge === "undefined"){
|
||
instance.status("Unconfigured", "red");
|
||
} else {
|
||
instance.status("Running", "green");
|
||
startCmdWaitInterval();
|
||
|
||
instance.CONFIG.isRunning = true;
|
||
console.log("modbus loaded: ", PRIVATEVARS.devices);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function checkFile(name){
|
||
try {
|
||
fs.accessSync(name, fs.constants.F_OK | fs.constants.R_OK | fs.constants.W_OK);
|
||
return true;
|
||
} catch (err) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//Zapina slucku vycitavania dat
|
||
function readDeviceData(){
|
||
stopCmdWaitInterval();
|
||
|
||
// let tbname = FLOW.OMS_edgeName;
|
||
|
||
// Check port existance
|
||
if (port === null)
|
||
{
|
||
port = new SerialPort(instance.CONFIG.port);
|
||
|
||
port.on('error', function(err) {
|
||
//logger.debug("rsPort opened error - failed", err.message);
|
||
//instance.send(instanceSendTo.debug, err.message);
|
||
|
||
errorHandler.sendMessageToService( exports.title + " MODBUS RS485 open - failed: " + err.message);
|
||
})
|
||
|
||
port.on('open', function() {
|
||
|
||
console.log("--->MODBUS RS485 READY - port opened");
|
||
|
||
exec("sudo halfduplex /dev/ttymxc1", (error, stdout, stderr) => {
|
||
instance.send(instanceSendTo.debug, {"err": error});
|
||
|
||
if (error) {
|
||
console.log("--->MODBUS RS485", error, stderr);
|
||
errorHandler.sendMessageToService( exports.title + " sudo halfduplex /dev/ttymxc1 - failed: " + error);
|
||
}
|
||
|
||
});
|
||
|
||
exec(instance.CONFIG.port_options, (error, stdout, stderr) => {
|
||
instance.send(instanceSendTo.debug, {"stdout":stdout,"stderr":stderr,"err":error});
|
||
|
||
if (error) {
|
||
console.log("--->MODBUS RS485", error, stderr);
|
||
errorHandler.sendMessageToService( exports.title + " " + instance.CONFIG.port_options + " - failed: " + error);
|
||
}
|
||
|
||
});
|
||
|
||
});
|
||
|
||
port.on('data', receivedData);
|
||
|
||
//sendError("readDeviceData", myEdge, ERRWEIGHT.DEBUG, "Serial port open!", {});
|
||
//console.log("-->MODBUS readDeviceData", myEdge, ERRWEIGHT.DEBUG, "Serial port open!", {});
|
||
|
||
startCmdWaitInterval();
|
||
return; // Cakame na port aby sa spravne otvoril a rozbehol
|
||
}
|
||
|
||
|
||
// Skontroluj existenciu device listu
|
||
if (PRIVATEVARS.devices.length > 0){
|
||
// Ponastavuj indexy
|
||
PRIVATEVARS.cmd_index++;
|
||
if (PRIVATEVARS.cmd_index >= PRIVATEVARS.devices[PRIVATEVARS.device_index].cmd.length){
|
||
// Kedže všetky príkazy pre daný device sú vybavené, je treba odoslat vyčítané data do TB
|
||
updateDataInTB();
|
||
|
||
PRIVATEVARS.cmd_index = 0;
|
||
PRIVATEVARS.device_index++;
|
||
|
||
if (PRIVATEVARS.device_index >= PRIVATEVARS.devices.length){
|
||
PRIVATEVARS.device_index = 0;
|
||
}
|
||
}
|
||
|
||
let device = PRIVATEVARS.devices[PRIVATEVARS.device_index];
|
||
|
||
// Skontroluj existenciu príkazú pre daný device type
|
||
if (device.cmd.length < 1){
|
||
//sendError("readDeviceData", tbname, ERRWEIGHT.ERROR, "No commands for this device type !", {"type": device.type});
|
||
console.log("readDeviceData", tbname, ERRWEIGHT.ERROR, "No commands for this device type !", {"type": device.type});
|
||
startCmdWaitInterval();
|
||
return;
|
||
}
|
||
|
||
// Odešli nasledujúci príkaz
|
||
sendCommand();
|
||
|
||
} else {
|
||
instance.CONFIG.isRunning = false;
|
||
//sendError("readDeviceData", myEdge, ERRWEIGHT.CRITICAL, "Modbus has no devices registered!", {});
|
||
console.log("modbus_citys: readDeviceData", myEdge, ERRWEIGHT.CRITICAL, "Modbus has no devices registered!", {});
|
||
}
|
||
}
|
||
|
||
function readingTimeouted(){
|
||
stopCmdWaitInterval();
|
||
stopTimeoutInterval();
|
||
|
||
// let tbname = FLOW.OMS_edgeName;
|
||
|
||
let device = PRIVATEVARS.devices[PRIVATEVARS.device_index];
|
||
let com = device.cmd[PRIVATEVARS.cmd_index];
|
||
//sendError("readingTimeouted", tbame, ERRWEIGHT.WARNING, "Reading timeouted !", {"device": device.address, "cmd": com.register});
|
||
console.log("modbus_citys: readingTimeouted", tbname, ERRWEIGHT.WARNING, "Reading timeouted !", {"device": device.address, "cmd": com.register});
|
||
|
||
device.timeoutcount++;
|
||
//console.log("device.timeoutcount", device.timeoutcount);
|
||
if (device.timeoutcount === 16)
|
||
{
|
||
|
||
//sendError("modbus_citys: readingTimeouted", tbname, ERRWEIGHT.CRITICAL, "Electrometer is not responding - reading timeouted", "");
|
||
sendNotification("modbus_citys: readingTimeouted", tbname, "electrometer_is_not_responding", {}, "", instanceSendTo.tb, instance );
|
||
|
||
if (device.status === "OK"){
|
||
device.status === "NOK";
|
||
}
|
||
}
|
||
|
||
startCmdWaitInterval();
|
||
}
|
||
|
||
function receivedData(data){
|
||
|
||
//let array = [...data];
|
||
//console.log("received data", array);
|
||
|
||
// let tbname = FLOW.OMS_edgeName;
|
||
|
||
//!if received data are less than 9 bytes, we store it in array variable and return. than we concatenate second incoming
|
||
// data and then length of array is 9
|
||
receivedDataArray = [...receivedDataArray, ...data];
|
||
//if (array.length < 9) return;
|
||
let l = receivedDataArray.length;
|
||
//console.log("received",receivedDataArray, l)
|
||
|
||
if ( l < 7 || l > 9 || l == 8 ) return;
|
||
|
||
let device = PRIVATEVARS.devices[PRIVATEVARS.device_index];
|
||
let com = device.cmd[PRIVATEVARS.cmd_index];
|
||
|
||
if (device.timeoutcount > 16) {
|
||
//sendError("Modbus_citysys: receivedData", tbname, ERRWEIGHT.NOTICE, "Electrometer is responding again", "");
|
||
sendNotification("modbus_citys: receivedData", tbname, "electrometer_is_responding_again", {}, "", instanceSendTo.tb, instance );
|
||
}
|
||
|
||
device.timeoutcount = 0;
|
||
|
||
if ((l == 7 && com.size != 1) || ( l == 9 && com.size != 2)) return;
|
||
|
||
stopTimeoutInterval();
|
||
|
||
//sendError("receivedData", tbname, ERRWEIGHT.DEBUG, "Received data !", {"cmd": receivedDataArray});
|
||
//console.log("receivedData", tbname, ERRWEIGHT.DEBUG, "Received data !", {"cmd": receivedDataArray});
|
||
|
||
// Skontroluj či sedí počet bytú v správe
|
||
//console.log("com size", com.size*2, "array2", receivedDataArray[2]);
|
||
if (receivedDataArray[2] !== (com.size*2)){
|
||
//sendError("receivedData", tbname, ERRWEIGHT.ERROR, "Received data of incorrect size !", {"expected": (com.size*2), "received": receivedDataArray[2], "cmd": com.register, "whole_msg": receivedDataArray});
|
||
console.log("modbus_citys: receivedData", tbname, ERRWEIGHT.ERROR, "Received data of incorrect size !", {"expected": (com.size*2), "received": receivedDataArray[2], "cmd": com.register, "whole_msg": receivedDataArray});
|
||
startTimeoutInterval();
|
||
} else {
|
||
// Konvertuj raw data na human readable
|
||
|
||
let v = (receivedDataArray[3] << 8) | receivedDataArray[4];
|
||
if (com.size == 2){
|
||
v = v | (receivedDataArray[5] << 24) | (receivedDataArray[6] << 16);
|
||
}
|
||
v = Math.round((v * com.multiplier) * 100) / 100;
|
||
|
||
|
||
// Pokad device nemá ešte žádné hodnoty vyčítané, pushni túto hodnotu do pola
|
||
if (device.data.length < 1){
|
||
device.data.push({ // Vždy ked správne zakomunikuje obnov status na OK
|
||
"changed": true,
|
||
"name": "status",
|
||
"value": "OK"
|
||
});
|
||
device.data.push({
|
||
"changed": true,
|
||
"name": com.tb_name,
|
||
"value": v
|
||
});
|
||
} else {
|
||
// Kedže už neco v poli má, kukni sa či je tam aj táto hodnota
|
||
let found = false;
|
||
for (let i = 0; i < device.data.length; i++){
|
||
let d = device.data[i];
|
||
if (d.name == "status"){ // Ked natrefíš na status (vždy tam musí byt) prepíš ho na OK lebo zakomunikoval správne
|
||
device.data[i].changed = true;
|
||
device.data[i].value = "OK";
|
||
}
|
||
|
||
if (d.name == com.tb_name){
|
||
found = true;
|
||
device.data[i].changed = true;
|
||
device.data[i].value = v;
|
||
}
|
||
}
|
||
|
||
// Pole existuje, ale táto hodnota tam neni, pridaj ju
|
||
if (found === false){
|
||
device.data.push({
|
||
"changed": true,
|
||
"name": com.tb_name,
|
||
"value": v
|
||
});
|
||
}
|
||
}
|
||
|
||
//Správne sme prijali odpoveď, je čas na další msg
|
||
startCmdWaitInterval();
|
||
}
|
||
//console.log('received data array', receivedDataArray);
|
||
receivedDataArray = [];
|
||
}
|
||
|
||
function updateDataInTB(){
|
||
let device = PRIVATEVARS.devices[PRIVATEVARS.device_index];
|
||
|
||
// console.log("---- MB device", device);
|
||
// console.log("---MB device data", device.data);
|
||
|
||
let values = "";
|
||
for (let i = 0; i < device.data.length; i++){
|
||
let data = device.data[i];
|
||
if (data.changed){
|
||
if (values !== ""){
|
||
values += ", ";
|
||
}
|
||
|
||
if (data.name === "status"){
|
||
values += "\""+data.name+"\":\""+data.value+"\"";
|
||
// This makes sure, that if this device doesn’t respond even once in next reading cycle, it will be marked as NOK
|
||
device.data[i].changed = true;
|
||
device.data[i].value = "NOK";
|
||
} else {
|
||
values += "\""+data.name+"\":"+data.value;
|
||
device.data[i].changed = false;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//console.log("values modbus", values);
|
||
|
||
|
||
if (values !== ""){
|
||
|
||
// let tbname = FLOW.OMS_edgeName;
|
||
// if(tbname == "" || tbname === undefined )
|
||
// {
|
||
// console.log("!!!!!!FLOW.OMS_edgeName is empty - 1");
|
||
// return;
|
||
// }
|
||
|
||
let tbmsg = "{\"" + tbname + "\":[{\"ts\":"+Date.now()+", \"values\":{"+values+"}}] }";
|
||
tbmsg = JSON.parse(tbmsg);
|
||
|
||
values = tbmsg[tbname][0]["values"];
|
||
|
||
//console.log("modbus", Object.keys(values));
|
||
|
||
//sum Phase_1_power, Phase_2_power, Phase_3_power (if one of them is undefined, we handle it)
|
||
const numOr0 = n => isNaN(n) ? 0 : n;
|
||
let calculated_total_power = [values["Phase_1_power"], values["Phase_2_power"], values["Phase_3_power"]].reduce((a, b) => numOr0(a) + numOr0(b));
|
||
values["total_power"] = parseFloat(calculated_total_power.toFixed(2));
|
||
tbmsg[tbname][0]["values"] = values;
|
||
|
||
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();
|
||
|
||
if(values[singleValue] == 0)
|
||
{
|
||
sendNotification("modbus_citys: updateDataInTB", tbname, "no_voltage_detected_on_phase", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase );
|
||
|
||
FLOW.OMS_no_voltage.add(phase);
|
||
}
|
||
else
|
||
{
|
||
FLOW.OMS_no_voltage.delete(phase);
|
||
sendNotification("modbus_citys: updateDataInTB", tbname, "voltage_on_phase_has_been_restored", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase);
|
||
}
|
||
|
||
}
|
||
})
|
||
|
||
sendThingsBoard(tbmsg);
|
||
}
|
||
}
|
||
|
||
|
||
let electrometerNotResponding = 0;
|
||
function sendCommand(){
|
||
let device = PRIVATEVARS.devices[PRIVATEVARS.device_index];
|
||
let com = device.cmd[PRIVATEVARS.cmd_index];
|
||
let array = [device.address, 3, ((com.register >> 8) & 0xFF), (com.register & 0xFF), ((com.size >> 8) & 0xFF), (com.size & 0xFF)];
|
||
array = modbusCRC(array);
|
||
|
||
//console.log('---device--', device);
|
||
//console.log('---device type--', device.type);
|
||
|
||
//sendError("sendCommand", device.tb_name, ERRWEIGHT.DEBUG, "Sending command !", {"cmd": array});
|
||
//console.log("sendCommand", device.tb_name, ERRWEIGHT.DEBUG, "Sending command !", {"cmd": array});
|
||
|
||
// let tbname = FLOW.OMS_edgeName;
|
||
// if(tbname == "" || tbname === undefined )
|
||
// {
|
||
// console.log("!!!!!!FLOW.OMS_edgeName is empty - 2");
|
||
// return;
|
||
// }
|
||
|
||
startTimeoutInterval();
|
||
port.write(Buffer.from(array), function(err) {
|
||
|
||
//! poslany command
|
||
//console.log("poslany tento commant", array, err, device.type);
|
||
|
||
if (err) {
|
||
stopTimeoutInterval();
|
||
stopCmdWaitInterval();
|
||
|
||
|
||
// elektromer neodpoveda viac ako 5 minut (15 commands za minutu sa posiela)
|
||
if (device.type === "EM111" || device.type === "EM340")
|
||
{
|
||
electrometerNotResponding++;
|
||
|
||
if (electrometerNotResponding > 15 && electrometerNotResponding < 17)
|
||
{
|
||
|
||
//sendError("Modbus_citys: sendCommand", tbname, ERRWEIGHT.CRITICAL, "Electrometer is not responding", {"err": err.message, "info": "No response more than 5 minutes"});
|
||
sendNotification("modbus_citys: sendCommand", tbname, "electrometer_is_not_responding", {}, {"err": err.message, "info": "No response more than 5 minutes"}, instanceSendTo.tb, instance );
|
||
|
||
let tbmsg = {
|
||
[tbname]: [
|
||
{
|
||
"ts": Date.now(),
|
||
"values": {
|
||
"status": "NOK"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
|
||
sendThingsBoard(tbmsg)
|
||
}
|
||
}
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
if (device.type === "EM111")
|
||
{
|
||
if (electrometerNotResponding > 15)
|
||
{
|
||
//sendError("Modbus_citys: sendCommand", tbname, ERRWEIGHT.NOTICE, "Electrometer is responding again", "");
|
||
sendNotification("modbus_citys: sendCommand", tbname, "electrometer_is_responding_again", {}, "", instanceSendTo.tb, instance );
|
||
}
|
||
electrometerNotResponding = 0;
|
||
|
||
}
|
||
});
|
||
}
|
||
|
||
function modbusCRC(array){
|
||
let crc = 0xFFFF;
|
||
for (let i = 0; i < array.length; i++){
|
||
let b = array[i];
|
||
crc = crc ^ b;
|
||
|
||
for (let j = 8; j>0; j--){
|
||
if ((crc & 0x0001) != 0){
|
||
crc = crc >> 1;
|
||
crc = crc ^ 0xA001;
|
||
} else {
|
||
crc = crc >> 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
array.push(crc & 0xFF);
|
||
array.push((crc >> 8) & 0xFF);
|
||
|
||
return array;
|
||
}
|
||
|
||
|
||
instance.on('data', function(flowdata) {
|
||
|
||
console.log("flowdata on data", flowdata);
|
||
sendStatus({"CONFIG": instance.CONFIG, "PRIVATEVARS":PRIVATEVARS})
|
||
|
||
});
|
||
|
||
instance.reconfigure = function() {
|
||
|
||
//TODO remove ftom options
|
||
myEdge = instance.options.edge;
|
||
if (starter === null){
|
||
starter = setInterval(function(){
|
||
loadData();
|
||
clearInterval(starter);
|
||
starter = null;
|
||
}, 5000);
|
||
}
|
||
};
|
||
|
||
instance.close = function() {
|
||
// close sockets and such
|
||
};
|
||
|
||
function sendError(func, device, weight, str, extra){
|
||
if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){
|
||
return; // Allow debug messages only if CONFIG.debug is active
|
||
}
|
||
|
||
let content = {
|
||
"type": weight,
|
||
"status": "new",
|
||
"source": {
|
||
"func":func,
|
||
"component":instance.id,
|
||
"component_name":instance.name,
|
||
"edge":myEdge
|
||
},
|
||
"message":str,
|
||
"message_data": extra
|
||
};
|
||
let msg = {};
|
||
msg[device] = [
|
||
{
|
||
"ts": Date.now(),
|
||
"values": {
|
||
"_event":content
|
||
}
|
||
}
|
||
];
|
||
|
||
// Msg can be outputted from components only after configuration
|
||
/*if (canSendErrData()){
|
||
sendBufferedErrors();
|
||
} else {
|
||
bufferError(msg);
|
||
}*/
|
||
instance.send(instanceSendTo.tb, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||
|
||
|
||
|
||
function sendBufferedErrors(){
|
||
if (PRIVATEVARS.errBuffer === undefined){
|
||
console.log("errBuffer undefined");
|
||
console.log("private: ", PRIVATEVARS);
|
||
}
|
||
console.log("errBuffer size: ", PRIVATEVARS.errBuffer.length);
|
||
if (PRIVATEVARS.errBuffer.length > 0){
|
||
for (let i = 0; i < PRIVATEVARS.errBuffer.length; i++){
|
||
instance.send(instanceSendTo.error, PRIVATEVARS.errBuffer[i]);
|
||
}
|
||
PRIVATEVARS.errBuffer = []; //Clear the buffer
|
||
saveData();
|
||
}
|
||
}
|
||
|
||
function bufferError(msg){
|
||
PRIVATEVARS.errBuffer.push(msg);
|
||
saveData();
|
||
}
|
||
}
|
||
|
||
function canSendErrData(){
|
||
//if (FLOW.errServerAvailable)
|
||
return true;
|
||
//else
|
||
// return false;
|
||
}
|
||
|
||
function sendStatus(str){
|
||
instance.send(instanceSendTo.debug, str);
|
||
}
|
||
|
||
function sendThingsBoard(obj){
|
||
// Msg can be outputted from components only after configuration
|
||
/*if (canSendTbData()){
|
||
sendBufferedTB();
|
||
} else {
|
||
console.log("cant send data");
|
||
bufferTB(str);
|
||
}*/
|
||
//console.log("send thingsboard", str);
|
||
|
||
//console.log("FLOW.OMS_edgeName", FLOW.OMS_edgeName, obj);
|
||
|
||
if(obj.hasOwnProperty(FLOW.OMS_edgeName) && FLOW.OMS_edgeName != "")
|
||
{
|
||
//send it to di_do_controller
|
||
instance.send(instanceSendTo.di_do_controller, {sender: "modbus_citysys", tbdata: obj});
|
||
}
|
||
// else
|
||
{
|
||
instance.send(instanceSendTo.tb, obj); // Even if TB server is unavailable, send this message to output, for other possible component connections
|
||
}
|
||
|
||
//instance.send(2, str); // Even if TB server is unavailable, send this message to output, for other possible component connections
|
||
|
||
|
||
function sendBufferedTB(){
|
||
if (PRIVATEVARS.tbBuffer.length > 0){
|
||
console.log("sending buffered: ", PRIVATEVARS.tbBuffer.length );
|
||
for (let i = 0; i < PRIVATEVARS.tbBuffer.length; i++){
|
||
instance.send(instanceSendTo.tb, PRIVATEVARS.tbBuffer[i]);
|
||
}
|
||
PRIVATEVARS.tbBuffer = []; //Clear the buffer
|
||
saveData();
|
||
}
|
||
}
|
||
|
||
function bufferTB(str){
|
||
PRIVATEVARS.tbBuffer.push(str);
|
||
saveData();
|
||
}
|
||
}
|
||
|
||
function canSendTbData(){
|
||
//if (FLOW.tbAvailable)
|
||
return true;
|
||
//else
|
||
// return false;
|
||
}
|
||
|
||
function startTimeoutInterval(){
|
||
if (!timeoutInterval){
|
||
timeoutInterval = setInterval(readingTimeouted, instance.CONFIG.timeoutTime);
|
||
}
|
||
}
|
||
|
||
function stopTimeoutInterval(){
|
||
if (timeoutInterval){
|
||
clearInterval(timeoutInterval);
|
||
timeoutInterval = null;
|
||
}
|
||
}
|
||
|
||
function startCmdWaitInterval(){
|
||
if (!msgWaitInterval){
|
||
msgWaitInterval = setInterval(readDeviceData, instance.CONFIG.msgWaitTime);
|
||
}
|
||
}
|
||
|
||
function stopCmdWaitInterval(){
|
||
if (msgWaitInterval){
|
||
clearInterval(msgWaitInterval);
|
||
msgWaitInterval = null;
|
||
}
|
||
}
|
||
|
||
instance.on('options', instance.reconfigure);
|
||
instance.reconfigure();
|
||
|
||
// LAST SECTION FOR COMMON FUNCTIONS
|
||
function humanReadableTimeAndDate(){
|
||
let date_ob = new Date();
|
||
|
||
let date = ("0" + date_ob.getDate()).slice(-2);
|
||
let month = ("0" + (date_ob.getMonth() + 1)).slice(-2);
|
||
let year = date_ob.getFullYear();
|
||
|
||
let hours = ("0" + date_ob.getHours()).slice(-2);
|
||
let minutes = ("0" + date_ob.getMinutes()).slice(-2);
|
||
let seconds = ("0" + date_ob.getSeconds()).slice(-2);
|
||
|
||
return date+"."+month+"."+year+" "+hours+":"+minutes+":"+seconds;
|
||
}
|
||
|
||
if (starter === null){
|
||
starter = setInterval(function(){
|
||
loadData();
|
||
clearInterval(starter);
|
||
starter = null;
|
||
}, 5000);
|
||
}
|
||
|
||
//setTimeout(loadData, 5000);
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|