Merged in senica-rvo-fix-line-control (pull request #4)
Senica rvo fix line control
This commit is contained in:
commit
f7350c117d
30 changed files with 5209 additions and 9216 deletions
3
config
3
config
|
|
@ -1,4 +1,5 @@
|
|||
name : Total.js Flow
|
||||
default_timezone : Europe/Bratislava
|
||||
|
||||
// Packages settings
|
||||
package#flow (Object) : { url: '/' }
|
||||
|
|
@ -7,5 +8,5 @@ package#flow (Object) : { url: '/' }
|
|||
table.relays : line:number|tbname:string|contactor:number|profile:string
|
||||
table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
||||
table.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:number|type:string|line:number
|
||||
table.pins : pin:string|type:string|line:number
|
||||
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 };
|
||||
|
|
@ -1,48 +1,31 @@
|
|||
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
||||
+|697|joqRYBVL30k9eQWOlZ5qwpD2KJpNEmA6gPxXzwaM|3||1|1|...........
|
||||
+|659|Ymn9oleRxJ0vw17WzAyGwdyEBk4ObdMXj2VgpNLG|3||1|1|...........
|
||||
+|636|M6ogKQW09bOXewAYvZyvJqyJrV1aRnPGE37p42Nx|2||1|1|...........
|
||||
+|648|gaMGN4x1e9JlZz0QPRDd9Rym6dVr3OpvqKnoWBbk|2||1|1|...........
|
||||
+|664|oGVzxNWP9lrjaQ7vKODQ7g51gqp62YZREmdw3XBM|1||1|1|...........
|
||||
+|634|NGWamnYqlP1wbgrZQxDAWm5e2X7OVAK69koR04vL|2||1|1|...........
|
||||
+|670|dlE1VQjYrNx9gZRmb38g1YyoLBO4qaAk2M6JPnG7|2||1|1|...........
|
||||
+|641|vnmG4kJxaXWNBgMQq0D7Mz5e9oZzOAlr6LdR3w2V|2||1|1|...........
|
||||
+|632|LpkVlmq4b3jMwJQxBZ8aM78rXAP6o97Ke0aOYEg2|2||1|1|...........
|
||||
+|667|MzXBoWbEZjO0lrpqnRyoJ4DkmVeaNAGdL9g4QKxP|1||1|1|...........
|
||||
+|682|vnreBJ6PMqgz20pYEL82XQyG1jkWwdQxZVNAOlmK|1||1|1|...........
|
||||
+|643|oZmYXEbw9lVWRv1jLxDe9bDdgAMz4PKQnNJ6eB23|1||1|1|...........
|
||||
+|642|pEonaKBOGbj9034MgJ8W3G8qXvxNWVkAPQz21R6L|1||1|1|...........
|
||||
+|647|BLQal6Pn9oz1KmNgek5Yqd50vd2MAbqG3OV7Rp4j|1||1|1|...........
|
||||
+|646|4agVJ9dPQkmp1R2X3EDJKxyrK6ZlNoM0n7qxBOev|1||1|1|...........
|
||||
+|666|9PpgLEnvk4WMV6RmOJybMGDaeAXzo2BQNG3K17Zw|1||1|1|...........
|
||||
+|654|Mmp93b2nvd7OoqgBeEyEZq5kjlAV1Y4ZNXwW0zLG|1||1|1|...........
|
||||
+|637|koW06PeGrLlBp2YJQE5Ogw5RmMaXKzj3wOAZg9n7|3||1|1|...........
|
||||
+|680|KL2jNOVpdARa9XvoeJDPga8bkmPBxqn7Ww3gzGQ1|1|{"intervals":[{"cct":3000,"value":100,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":-1,"end_time":"13:50","start_time":"13:40"},{"cct":3000,"value":100,"end_time":"13:00","start_time":"13:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|0|...........
|
||||
+|645|jklN4JpQAx362o9XYZDN6wDgrWw1P7GEbdBM0vRV|1||1|1|...........
|
||||
+|660|gj7zbKV46oQ1p2e0AJ8XqZDG3YNWaRrlOEXvBxmM|3||1|1|...........
|
||||
+|669|Y9aLW03wOZkABvKXbMyL0lyV1xdNj72r4egqGRzJ|3|{"intervals":[{"cct":3000,"value":0,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"07:20","start_time":"13:50"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"07:20"},{"cct":3000,"value":0,"end_time":"13:50","start_time":"13:40"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|671|AvVdgzYJZaPx3oMqeED4Oj8NnmKkw716bRO90jLB|3||1|1|...........
|
||||
+|638|9xgzG4Op1BrKZPmoQkDrmj8E73ndJNMjavAwX2Re|3||1|1|...........
|
||||
+|639|BOjEzGRZ46bnp9wa2A8z76D0JkmW1QPNdrqevXVL|3||1|1|...........
|
||||
+|693|KjbN4q7JPZmexgdnz2yKdn5YAWwO0Q3BMX6ERLoV|2|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"22:10","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":30,"end_time":"02:40","start_time":"22:10"},{"cct":3000,"value":90,"end_time":"05:30","start_time":"02:40"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|649|0p2rwdP7aGoOQLJNgAynJNy6xWXbmMe3nvZqlzkV|1|{"intervals":[{"cct":3000,"value":100,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":-1,"end_time":"13:50","start_time":"13:40"},{"cct":3000,"value":100,"end_time":"13:00","start_time":"13:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|698|mYnBzbeGaAL62jowRv59M35Xq9QpZ0K7O1dg4xVl|1||1|1|...........
|
||||
+|640|WjBL12pg63eX4N9P7zy0XYyEJKmlbkGwZMx0avQV|2||1|1|...........
|
||||
+|656|BrQx3NGKgVMRaXYAo9y1GE8ZzkWnj1le6bdOLE20|1|{"intervals":[{"cct":3000,"value":100,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":-1,"end_time":"13:50","start_time":"13:40"},{"cct":3000,"value":100,"end_time":"13:00","start_time":"13:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|651|qaAOzENGrvpbe0VoK7D6Ld519PZmdg3nl24JLQMk|2||1|1|...........
|
||||
+|691|lekrmdvO0BQG1ZW4AV8jzq8M39xnN2wEbRgPjXLp|1||1|1|...........
|
||||
+|661|laYK7Pomn2bNZXEpedDxAqyOJkQ3WwV49gqxLrAR|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":60,"end_time":"05:30","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|665|gbv4nzqxW0XGAPKVNk8kr25ZQ2l3O6LRBprM97ew|3||1|1|...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|662|gbv4nzqxW0XGAPKVNk8kW48ZQ2l3O6LRBprM97ew|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|668|lekrmdvO0BQG1ZW4AV8jeZ5M39xnN2wEbRgPjXLp|3|{"intervals":[{"cct":3000,"value":0,"end_time":"13:10","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"12:50","start_time":"13:10"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"12:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|689|q0rElBPdL6kxMAjnzVDRl95emNZY7oOv2wK9gb31|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|683|XKQbz3WAwY21dGa0R453rWyJm9PZOjqlvpr6Nkeo|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|688|PaGbQ3wBAZWOmRvK9VDpvz5endLJYopEqlkzNMxX|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|672|0XYElWeKBNJn1gdoMG8lON5ALkPvj4V3xra2q6mO|2||1|1|.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|690|wGjQobgOK0n2YqBZmVDVR3DR9ep6EXA1ka3vzlP7|2||1|1|..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|692|l9YkRpoB2vVa0mKqEO8ZGGDjW43eXnJML6GxzbwQ|2|{"intervals":[{"cct":3000,"value":0,"end_time":"13:10","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"12:50","start_time":"13:10"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"12:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|655|RMgnK93rkoAazbqdQ4yBYpDZ1YXGx6pmwBeVEP2O|2||1|1|..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|694|Jm32GR1qpwQxlZza0N5mE15AP96YbOKLogrXVW4e|2|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|635|Vq2JaWpw1OdBKmNeoj8w605XE40l3kgL76Azb9QP|2|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|650|0XYElWeKBNJn1gdoMG8lYdDALkPvj4V3xra2q6mO|3|{"intervals":[{"cct":3000,"value":0,"end_time":"13:10","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"12:50","start_time":"13:10"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"12:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|663|LpkVlmq4b3jMwJQxBZ8akayrXAP6o97Ke0aOYEg2|1|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|0|1|...........
|
||||
+|3522|RO8rjaBDy21qPQJzW7oD96ApK3xmNleVZg9Ed4Gw|1||1|1|...........
|
||||
+|4018|3JjOWdylwgNLzxVab7NEznkZ2vG64rq8PEB5QmDo|1||1|1|...........
|
||||
+|4019|Z5KyJe9nEg1QNbWlX0wWRB0oDjBLdqzR83VGv624|1||1|1|...........
|
||||
+|4154|1JMYvnx2RzKEo4aWQ7DmN5AL8yZV3m9NBePXbrdj|1||1|0|...........
|
||||
+|3907|PjLblDgRBO6WQqnxmkJ59r0Jv3ewZN4p5a89yKdY|1||1|1|...........
|
||||
+|4148|dz4ojlpP85JMgDLZWkQOoGAaKYqQexEr62GXRV1y|1||1|1|...........
|
||||
+|4153|d5xjWYMwEJon6rLlK7yBYmAqgV4DaOeNB9ZX3Gzb|1||1|1|...........
|
||||
+|3938|gRoJEyXVx4qD9er287LP1v7wBzGldaPjLWQKm3Mv|1||1|1|...........
|
||||
+|3802|K94XLav1glVRnyQ6r01BNzkme3YJwBxM5oOzdP2j|1||1|1|...........
|
||||
+|4015|d9x2V5LGYBzXp4mMRAOBDj7PloaqJwnQj6DgrNe3|1||1|0|...........
|
||||
+|3929|B5EoxeMVp4zwr8nqW0GjDpARjvD1PNamOGbLg63Z|1||1|1|...........
|
||||
+|3946|aw4eELG2DlPMdn1JW0B1DnAqQXOZRN3xB5yp8VKr|1||1|1|...........
|
||||
+|4014|ZmRwd93QL4gaezxEbAxW5971prn2XjlPvGyqJ6BO|1||1|1|...........
|
||||
+|4155|eod9aRWLVl34Gx1Dn7VoaaA2rz6qjgmpEXwQJN5Z|1||1|1|...........
|
||||
+|4149|3a5oqJN1bgnx4Ol9dk86NBAByE6jQ8mKDWMpGrLV|1||1|1|...........
|
||||
+|3642|EjgWGnXaLy9opPOz20n694086BlYM3w1deVQvbKr|1||1|1|...........
|
||||
+|3636|wvKJdZML6mXP4DzWBAXWNW7jxNloa5g23Ve9Y1ry|1||1|1|...........
|
||||
+|3991|Nzp2OoJlqn6r1ZgvdA3GWdAabBwP5G4eE3RQmyxD|1||1|1|...........
|
||||
+|3994|PLBJzmK1r3Gynd6OW0gGdM0e5wV4vx9bDEqNgYR8|1||1|1|...........
|
||||
+|3990|52dD6ZlV1QaOpRBmbAqKZgkKnGzWMLj4eJq38Pgo|1||1|1|...........
|
||||
+|3967|rDbQ84xzwgdqEoPm3kbJw3k9anOZY1RXyBv2LVM6|1||1|1|...........
|
||||
+|3977|E6Kg9oDnLWyzPRMva7vrwa7Jxp4VG58qO2w1lZYe|1||1|1|...........
|
||||
+|3757|roKgWqY95V3mXMRzyAjm8D7bLjexpJPvaGDBw826|1||1|1|...........
|
||||
+|3633|nJL5lPMwBx23YpqRe0rlKV7damXvWVbOrD4gNzy8|1||1|1|...........
|
||||
+|3744|ZmRwd93QL4gaezxEbAxW5O71prn2XjlPvGyqJ6BO|1||1|1|...........
|
||||
+|4023|eod9aRWLVl34Gx1Dn7VoaMA2rz6qjgmpEXwQJN5Z|1||1|1|...........
|
||||
+|3720|3a5oqJN1bgnx4Ol9dk86NZAByE6jQ8mKDWMpGrLV|1||1|1|...........
|
||||
+|3734|EjgWGnXaLy9opPOz20n69V086BlYM3w1deVQvbKr|1||1|1|...........
|
||||
+|3741|wvKJdZML6mXP4DzWBAXWN17jxNloa5g23Ve9Y1ry|1||1|1|...........
|
||||
+|3721|Nzp2OoJlqn6r1ZgvdA3GWKAabBwP5G4eE3RQmyxD|1||0|0|...........
|
||||
|
|
|
|||
|
|
@ -25,8 +25,13 @@ key:string|weight:string|sk:string|en:string
|
|||
+|door_has_been_open_without_permision_alarm_is_on|WARNING|Dvere boli otvorené bez povolania - zapnutá siréna|Door has been open without permision - alarm is on|...............
|
||||
+|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|...............
|
||||
+|local_database_is_corrupted|CRITICAL|||...............
|
||||
+|electrometer_is_not_responding|ERROR|Elektromer neodpovedá|Electrometer is not responding|...............
|
||||
+|no_voltage_detected_on_phase|CRITICAL|Na fáze č. ${phase} nie je napätie|No voltage detected on phase no. ${phase}|...............
|
||||
+|electrometer_is_responding_again|NOTICE|Elektromer znovu odpovedá|Electrometer is responding again|...............
|
||||
+|voltage_on_phase_has_been_restored|NOTICE|Napätie na fáze č. ${phase} bolo obnovené|Voltage on phase no. ${phase} has been restored|...............
|
||||
+|electrometer_nok|ERROR|Elektromer neodpovedá|Electrometer is not responding|...............
|
||||
+|electrometer_ok|NOTICE|Elektromer znovu odpovedá|Electrometer is responding again|...............
|
||||
+|no_voltage_on_phase|CRITICAL|Na fáze č. ${phase} nie je napätie|No voltage detected on phase no. ${phase}|...............
|
||||
+|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 |...............
|
||||
+|twilight_sensor_nok|ERROR|Sensor súmraku neodpovedá|Twilight sensor is not responding|...............
|
||||
+|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|...............
|
||||
+|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|...............
|
||||
+|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|...............
|
||||
+|flow_restart|NOTICE|Restart flowu|Flow has been restarted|...............
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
pin:number|type:string|line:number
|
||||
*|1|state_of_main_switch|0|...........
|
||||
*|2|rotary_switch_state|0|...........
|
||||
*|3|rotary_switch_state|0|...........
|
||||
*|4|power_supply|0|...........
|
||||
*|5|battery|0|...........
|
||||
*|6|door_condition|0|...........
|
||||
*|8|state_of_breaker|1|...........
|
||||
*|9|state_of_breaker|2|...........
|
||||
*|10|state_of_breaker|3|...........
|
||||
*|11|state_of_contactor|1|...........
|
||||
*|12|state_of_contactor|2|...........
|
||||
*|13|state_of_contactor|3|...........
|
||||
*|16|twilight_sensor|0|...........
|
||||
pin:string|type:string|line:number
|
||||
*|input1_01|door_condition|0|...........
|
||||
*|input1_02|rotary_switch_state|0|...........
|
||||
*|input1_03|rotary_switch_state|0|...........
|
||||
*|intut1_04|power_supply|0|...........
|
||||
*|input1_05|state_of_main_switch|0|...........
|
||||
*|input1_06|state_of_breaker|1|...........
|
||||
*|input1_07|state_of_breaker|2|...........
|
||||
*|input1_08|state_of_breaker|3|...........
|
||||
*|relay1_02|state_of_contactor|1|...........
|
||||
*|relay1_03|state_of_contactor|2|...........
|
||||
*|relay1_04|state_of_contactor|3|...........
|
||||
*|28F46E9D0E00008B|temperature|0|...........
|
||||
*|twilight_sensor|twilight_sensor|0|...........
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
line:number|tbname:string|contactor:number|profile:string
|
||||
+|0|KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV|1||...........
|
||||
+|3|vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V|0||....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|1|RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O|0|{"intervals":[{"value":0,"end_time":"13:00","start_time":"13:00"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|2|dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7|0||................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|0|6lQGaY9RDywdVzObj0PadOkPg4NBn3exEK51LWZq|1||...........
|
||||
+|1|JzwxZXOvDj1bVrN4nkWw9Qk8qdyBl3MRKLpGPgaQ|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||
+|2|g9OxBZ5KRwNznlY6pAp6mxkWXvjdEL4eGQobMDy2|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||
+|3|OzNMgZ9n43qPbjXmy7zWMJA2DKdYvW5e6pxGRrVa|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
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
|
||||
+|testpanel|en|28.427B45920702|48.70826502|17.28455203|192.168.252.4|showroom_test_panel_led|xmRd6RJxW53WZe4vMFLU|1883|0|1|lm|ttymxc4|1|20|5|...........................................
|
||||
+|rvo_senica_39_10.0.0.132|en|28.427B45920702|48.70826502|17.28455203|192.168.252.1|rvo_senica_39_10.0.0.132|qzSNuCNrLP4OL1v47YEe|1883|0|68|unipi|ttyUSB0|1|20|5|...........................................
|
||||
|
|
|
|||
3168
flow/cmd_manager.js
3168
flow/cmd_manager.js
File diff suppressed because it is too large
Load diff
1252
flow/designer.json
1252
flow/designer.json
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
|
||||
{
|
||||
constructor(index) {
|
||||
this.index = index;
|
||||
|
||||
this.previousValues = {};
|
||||
this.debug = false;
|
||||
this.messageCounter = 0;
|
||||
|
||||
this.sender = "";
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
console.log("----------------------------");
|
||||
console.log("previousValues", this.previousValues);
|
||||
console.log("----------------------------");
|
||||
}
|
||||
|
||||
setSender(sender)
|
||||
{
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
isEmptyObject( obj ) {
|
||||
for ( var name in obj ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sendToTb(dataToTb, instance)
|
||||
{
|
||||
|
||||
if(!FLOW.OMS_brokerready)
|
||||
{
|
||||
return dataToTb;
|
||||
}
|
||||
|
||||
let keys = Object.keys(dataToTb);
|
||||
|
||||
if(keys.length == 0)
|
||||
{
|
||||
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let tbname = keys[0];
|
||||
let ts;
|
||||
|
||||
let arrayOfValues = dataToTb[tbname];
|
||||
let arrayOfValuesToSend = [];
|
||||
|
||||
for(let i = 0; i < arrayOfValues.length; i++)
|
||||
{
|
||||
ts = arrayOfValues[i].ts;
|
||||
|
||||
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
||||
|
||||
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
||||
|
||||
//console.log("sendToTb------------>after", values);
|
||||
|
||||
if(!this.isEmptyObject(values))
|
||||
{
|
||||
arrayOfValuesToSend.push({ts: ts, values: values});
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayOfValuesToSend.length == 0)
|
||||
{
|
||||
//if(this.debug) console.log("data not sent - empty array");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
let dataToTb = {
|
||||
[tbname]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": values
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
this.messageCounter++;
|
||||
|
||||
let dataToTbModified = {
|
||||
[tbname]: arrayOfValuesToSend
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
instance.send(this.index, dataToTbModified);
|
||||
}
|
||||
|
||||
getDiffTimestamp(key)
|
||||
{
|
||||
let seconds = 60*60;//1h
|
||||
//seconds = 1;//for testing
|
||||
|
||||
//TODO set different value for given key!!!
|
||||
//if(key == "status") seconds = 2*60*60;//2h
|
||||
|
||||
let timestampDiffToRemoveKey = seconds*1000;
|
||||
|
||||
return timestampDiffToRemoveKey;
|
||||
}
|
||||
|
||||
prepareValuesForTb(tbname, timestamp, values)
|
||||
{
|
||||
let keys = Object.keys(values);
|
||||
if(!this.previousValues.hasOwnProperty(tbname))
|
||||
{
|
||||
this.previousValues[tbname] = {};
|
||||
}
|
||||
|
||||
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
||||
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
let value = values[key];
|
||||
|
||||
if(!this.previousValues[tbname].hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.previousValues[tbname][key].value === value)
|
||||
{
|
||||
let diff = timestamp - this.previousValues[tbname][key].ts;
|
||||
|
||||
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
||||
if(diff > timestampDiffToRemoveKey)
|
||||
{
|
||||
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);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
delete values[key];
|
||||
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.previousValues[tbname][key].value = value;
|
||||
this.previousValues[tbname][key].ts = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
class DataToTbHandler
|
||||
{
|
||||
constructor(index) {
|
||||
this.index = index;
|
||||
|
||||
this.previousValues = {};
|
||||
this.debug = false;
|
||||
this.messageCounter = 0;
|
||||
|
||||
this.sender = "";
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
console.log("----------------------------");
|
||||
console.log("previousValues", this.previousValues);
|
||||
console.log("----------------------------");
|
||||
}
|
||||
|
||||
setSender(sender)
|
||||
{
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
isEmptyObject( obj ) {
|
||||
for ( var name in obj ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sendToTb(dataToTb, instance)
|
||||
{
|
||||
|
||||
if(!FLOW.OMS_brokerready)
|
||||
{
|
||||
return dataToTb;
|
||||
}
|
||||
|
||||
let keys = Object.keys(dataToTb);
|
||||
|
||||
if(keys.length == 0)
|
||||
{
|
||||
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let tbname = keys[0];
|
||||
let ts;
|
||||
|
||||
let arrayOfValues = dataToTb[tbname];
|
||||
let arrayOfValuesToSend = [];
|
||||
|
||||
for(let i = 0; i < arrayOfValues.length; i++)
|
||||
{
|
||||
ts = arrayOfValues[i].ts;
|
||||
|
||||
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
||||
|
||||
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
||||
|
||||
//console.log("sendToTb------------>after", values);
|
||||
|
||||
if(!this.isEmptyObject(values))
|
||||
{
|
||||
arrayOfValuesToSend.push({ts: ts, values: values});
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayOfValuesToSend.length == 0)
|
||||
{
|
||||
//if(this.debug) console.log("data not sent - empty array");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
let dataToTb = {
|
||||
[tbname]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": values
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
this.messageCounter++;
|
||||
|
||||
let dataToTbModified = {
|
||||
[tbname]: arrayOfValuesToSend
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
instance.send(this.index, dataToTbModified);
|
||||
}
|
||||
|
||||
getDiffTimestamp(key)
|
||||
{
|
||||
let seconds = 60*60;//1h
|
||||
//seconds = 1;//for testing
|
||||
|
||||
//TODO set different value for given key!!!
|
||||
//if(key == "status") seconds = 2*60*60;//2h
|
||||
|
||||
let timestampDiffToRemoveKey = seconds*1000;
|
||||
|
||||
return timestampDiffToRemoveKey;
|
||||
}
|
||||
|
||||
prepareValuesForTb(tbname, timestamp, values)
|
||||
{
|
||||
let keys = Object.keys(values);
|
||||
if(!this.previousValues.hasOwnProperty(tbname))
|
||||
{
|
||||
this.previousValues[tbname] = {};
|
||||
}
|
||||
|
||||
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
||||
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
let value = values[key];
|
||||
|
||||
if(!this.previousValues[tbname].hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.previousValues[tbname][key].value === value)
|
||||
{
|
||||
let diff = timestamp - this.previousValues[tbname][key].ts;
|
||||
|
||||
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
||||
if(diff > timestampDiffToRemoveKey)
|
||||
{
|
||||
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);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
delete values[key];
|
||||
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.previousValues[tbname][key].value = value;
|
||||
this.previousValues[tbname][key].ts = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DataToTbHandler;
|
||||
|
|
@ -1,124 +1,124 @@
|
|||
const { MD5 } = require('./md5.js');
|
||||
const { networkInterfaces } = require('os');
|
||||
|
||||
class ErrorToServiceHandler
|
||||
{
|
||||
constructor() {
|
||||
this.previousValues = {};
|
||||
|
||||
this.projects_id = undefined;
|
||||
this.message_type = "error_message";
|
||||
|
||||
const nets = networkInterfaces();
|
||||
this.ipAddresses = Object.create(null); // Or just '{}', an empty object
|
||||
|
||||
for (const name of Object.keys(nets)) {
|
||||
for (const net of nets[name]) {
|
||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||
if (net.family === 'IPv4' && !net.internal) {
|
||||
if (!this.ipAddresses[name]) {
|
||||
this.ipAddresses[name] = [];
|
||||
}
|
||||
this.ipAddresses[name].push(net.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(this.ipAddresses);
|
||||
|
||||
}
|
||||
|
||||
setProjectsId(projects_id)
|
||||
{
|
||||
this.projects_id = projects_id;
|
||||
}
|
||||
|
||||
processMessage(message, seconds, message_type)
|
||||
{
|
||||
if(Array.isArray(message)) message = message.join(', ');
|
||||
//keep in memory - default value is 1h
|
||||
if (seconds === undefined) seconds = 60*60;
|
||||
if(message_type) this.message_type = message_type;
|
||||
|
||||
let key = MD5(message);
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
if(!this.previousValues.hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||
}
|
||||
|
||||
let diff = (timestamp - this.previousValues[key].ts);
|
||||
if(diff < this.previousValues[key].duration*1000) return false;
|
||||
|
||||
this.previousValues[key].ts = timestamp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sendMessageToService(message, seconds, message_type)
|
||||
{
|
||||
|
||||
let f = this.processMessage(message, seconds, message_type);
|
||||
if(!f) return;
|
||||
|
||||
/*
|
||||
//-------------
|
||||
if(message_type == undefined) message_type = "error_message";
|
||||
if(Array.isArray(message)) message = message.join(', ');
|
||||
|
||||
let key = MD5(message);
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
//keep in memory
|
||||
if (seconds === undefined) seconds = 60*60;
|
||||
|
||||
if(!this.previousValues.hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||
}
|
||||
|
||||
let diff = (timestamp - this.previousValues[key].ts);
|
||||
if(diff < this.previousValues[key].duration*1000) return;
|
||||
|
||||
this.previousValues[key].ts = timestamp;
|
||||
*/
|
||||
|
||||
//-------------------------
|
||||
|
||||
//send to service
|
||||
|
||||
let dataToInfoSender = {id: this.projects_id};
|
||||
|
||||
//js_error || error_message
|
||||
dataToInfoSender[this.message_type] = message;
|
||||
dataToInfoSender.ipAddresses = this.ipAddresses;
|
||||
|
||||
console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender);
|
||||
|
||||
//TODO UGLY!!!
|
||||
if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id;
|
||||
|
||||
/*
|
||||
if(this.projects_id === undefined)
|
||||
{
|
||||
console.log("this.projects_id is undefined");
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
RESTBuilder.make(function(builder) {
|
||||
builder.method('POST');
|
||||
builder.post(dataToInfoSender);
|
||||
builder.url('http://192.168.252.2:8004/sentmessage');
|
||||
|
||||
builder.callback(function(err, response, output) {
|
||||
console.log("process.on error send", err, response, output, dataToInfoSender);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const { MD5 } = require('./md5.js');
|
||||
const { networkInterfaces } = require('os');
|
||||
|
||||
class ErrorToServiceHandler
|
||||
{
|
||||
constructor() {
|
||||
this.previousValues = {};
|
||||
|
||||
this.projects_id = undefined;
|
||||
|
||||
const nets = networkInterfaces();
|
||||
this.ipAddresses = Object.create(null); // Or just '{}', an empty object
|
||||
|
||||
for (const name of Object.keys(nets)) {
|
||||
for (const net of nets[name]) {
|
||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||
if (net.family === 'IPv4' && !net.internal) {
|
||||
if (!this.ipAddresses[name]) {
|
||||
this.ipAddresses[name] = [];
|
||||
}
|
||||
this.ipAddresses[name].push(net.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(this.ipAddresses);
|
||||
|
||||
}
|
||||
|
||||
setProjectsId(projects_id)
|
||||
{
|
||||
this.projects_id = projects_id;
|
||||
}
|
||||
|
||||
processMessage(message, seconds, message_type)
|
||||
{
|
||||
if(message_type == undefined) message_type = "error_message";
|
||||
if(Array.isArray(message)) message = message.join(', ');
|
||||
|
||||
let key = MD5(message);
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
//keep in memory - default value is 1h
|
||||
if (seconds === undefined) seconds = 60*60;
|
||||
|
||||
if(!this.previousValues.hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||
}
|
||||
|
||||
let diff = (timestamp - this.previousValues[key].ts);
|
||||
if(diff < this.previousValues[key].duration*1000) return false;
|
||||
|
||||
this.previousValues[key].ts = timestamp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sendMessageToService(message, seconds, message_type)
|
||||
{
|
||||
|
||||
let f = this.processMessage(message, seconds, message_type);
|
||||
if(!f) return;
|
||||
|
||||
/*
|
||||
//-------------
|
||||
if(message_type == undefined) message_type = "error_message";
|
||||
if(Array.isArray(message)) message = message.join(', ');
|
||||
|
||||
let key = MD5(message);
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
//keep in memory
|
||||
if (seconds === undefined) seconds = 60*60;
|
||||
|
||||
if(!this.previousValues.hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||
}
|
||||
|
||||
let diff = (timestamp - this.previousValues[key].ts);
|
||||
if(diff < this.previousValues[key].duration*1000) return;
|
||||
|
||||
this.previousValues[key].ts = timestamp;
|
||||
*/
|
||||
|
||||
//-------------------------
|
||||
|
||||
//send to service
|
||||
|
||||
let dataToInfoSender = {id: this.projects_id};
|
||||
|
||||
//js_error || error_message
|
||||
dataToInfoSender[message_type] = message;
|
||||
dataToInfoSender.ipAddresses = this.ipAddresses;
|
||||
|
||||
console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender);
|
||||
|
||||
//TODO UGLY!!!
|
||||
if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id;
|
||||
|
||||
/*
|
||||
if(this.projects_id === undefined)
|
||||
{
|
||||
console.log("this.projects_id is undefined");
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
RESTBuilder.make(function(builder) {
|
||||
builder.method('POST');
|
||||
builder.post(dataToInfoSender);
|
||||
builder.url('http://192.168.252.2:8004/sentmessage');
|
||||
|
||||
builder.callback(function(err, response, output) {
|
||||
console.log("process.on error send", err, response, output, dataToInfoSender);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ErrorToServiceHandler;
|
||||
|
|
@ -1,135 +1,137 @@
|
|||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js');
|
||||
const dbNotifications = TABLE("notifications");
|
||||
|
||||
//key is device, value = str
|
||||
let sentValues= {};
|
||||
let notificationsData = {};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
function getKey(map, val) {
|
||||
return Object.keys(map).findItem(key => map[key] === val);
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/41117799/string-interpolation-on-variable
|
||||
var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);
|
||||
|
||||
async function initNotifications()
|
||||
{
|
||||
let response = await promisifyBuilder(dbNotifications.find());
|
||||
notificationsData = makeMapFromDbResult(response, "key");
|
||||
|
||||
console.log("initNotifications done" );
|
||||
}
|
||||
|
||||
function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) {
|
||||
|
||||
let storeToSendValues = true;
|
||||
if(saveKey == undefined) storeToSendValues = false;
|
||||
|
||||
let lang = FLOW.OMS_language;
|
||||
if(lang != "en" || lang != "sk") lang = "en";
|
||||
|
||||
let tpl = key;
|
||||
let weight = "";
|
||||
|
||||
if(notificationsData[key])
|
||||
{
|
||||
weight = notificationsData[key].weight;
|
||||
weight = weight.toLowerCase();
|
||||
|
||||
tpl = notificationsData[key][lang];
|
||||
tpl = template(tpl, params);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
|
||||
return false;
|
||||
}
|
||||
|
||||
//detect invalid err weight
|
||||
if(getKey(ERRWEIGHT, weight) == undefined)
|
||||
{
|
||||
console.error("sendNotification: Notifications: undefined weight", weight, key, func);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sentValues.hasOwnProperty(saveKey))
|
||||
{
|
||||
if(sentValues[saveKey] == tpl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(sentValues[saveKey] == undefined)
|
||||
{
|
||||
if(storeToSendValues)
|
||||
{
|
||||
//do not send - flow is was started
|
||||
sentValues[saveKey] = tpl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(saveKey == "rvo_door")
|
||||
{
|
||||
//console.log("******", saveKey, sentValues[saveKey], tpl);
|
||||
}
|
||||
|
||||
if(storeToSendValues) sentValues[saveKey] = tpl;
|
||||
|
||||
let str = FLOW.OMS_rvo_name;
|
||||
if(str != "") str = str + ": ";
|
||||
str = str + tpl;
|
||||
|
||||
let content = {
|
||||
"type": weight,
|
||||
"status": "new",
|
||||
"source": {
|
||||
"func":func,
|
||||
"component":instance.id,
|
||||
"component_name":instance.name,
|
||||
"edge":device
|
||||
},
|
||||
"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(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendNotification,
|
||||
initNotifications,
|
||||
ERRWEIGHT
|
||||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js');
|
||||
const dbNotifications = TABLE("notifications");
|
||||
|
||||
//key is device, value = str
|
||||
let sentValues= {};
|
||||
let notificationsData = {};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
function getKey(map, val) {
|
||||
return Object.keys(map).findItem(key => map[key] === val);
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/41117799/string-interpolation-on-variable
|
||||
var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);
|
||||
|
||||
async function initNotifications()
|
||||
{
|
||||
let response = await promisifyBuilder(dbNotifications.find());
|
||||
notificationsData = makeMapFromDbResult(response, "key");
|
||||
|
||||
console.log("initNotifications done" );
|
||||
}
|
||||
|
||||
function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) {
|
||||
|
||||
// return;
|
||||
|
||||
let storeToSendValues = true;
|
||||
if(saveKey == undefined) storeToSendValues = false;
|
||||
|
||||
let lang = FLOW.OMS_language;
|
||||
if(lang != "en" || lang != "sk") lang = "en";
|
||||
|
||||
let tpl = key;
|
||||
let weight = "";
|
||||
|
||||
if(notificationsData[key])
|
||||
{
|
||||
weight = notificationsData[key].weight;
|
||||
weight = weight.toLowerCase();
|
||||
|
||||
tpl = notificationsData[key][lang];
|
||||
tpl = template(tpl, params);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
|
||||
return false;
|
||||
}
|
||||
|
||||
//detect invalid err weight
|
||||
if(getKey(ERRWEIGHT, weight) == undefined)
|
||||
{
|
||||
console.error("sendNotification: Notifications: undefined weight", weight, key, func);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sentValues.hasOwnProperty(saveKey))
|
||||
{
|
||||
if(sentValues[saveKey] == tpl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(sentValues[saveKey] == undefined)
|
||||
{
|
||||
if(storeToSendValues)
|
||||
{
|
||||
//do not send - flow is was started
|
||||
sentValues[saveKey] = tpl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(saveKey == "rvo_door")
|
||||
{
|
||||
//console.log("******", saveKey, sentValues[saveKey], tpl);
|
||||
}
|
||||
|
||||
if(storeToSendValues) sentValues[saveKey] = tpl;
|
||||
|
||||
let str = FLOW.OMS_rvo_name;
|
||||
if(str != "") str = str + ": ";
|
||||
str = str + tpl;
|
||||
|
||||
let content = {
|
||||
"type": weight,
|
||||
"status": "new",
|
||||
"source": {
|
||||
"func":func,
|
||||
"component":instance.id,
|
||||
"component_name":instance.name,
|
||||
"edge":device
|
||||
},
|
||||
"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(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendNotification,
|
||||
initNotifications,
|
||||
ERRWEIGHT
|
||||
}
|
||||
|
|
@ -22,16 +22,12 @@ exports.html = `<div class="padding">
|
|||
|
||||
exports.readme = `# send all data to projects.worksys.io, required to monitor status of controller(unipi)`;
|
||||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||||
const fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
exports.install = async function(instance) {
|
||||
|
||||
let id;
|
||||
let allValues = {};
|
||||
let dbSettings;
|
||||
|
||||
let sendAllValuesInterval;
|
||||
|
||||
let now = new Date();
|
||||
|
|
@ -52,23 +48,11 @@ exports.install = async function(instance) {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let p = path.join(__dirname + "/../databases/", 'settings.table');
|
||||
if (fs.existsSync(p)) {
|
||||
|
||||
dbSettings = TABLE("settings");
|
||||
let responseSettings = await promisifyBuilder(dbSettings.find());
|
||||
id = responseSettings[0]["projects_id"];
|
||||
|
||||
//console.log(exports.title, responseSettings, id);
|
||||
}
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
|
||||
function sendValues()
|
||||
{
|
||||
const id = FLOW.OMS_projects_id;
|
||||
|
||||
if(Object.keys(allValues).length > 0)
|
||||
{
|
||||
if(id !== undefined)
|
||||
|
|
|
|||
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);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
exports.id = 'monitorcpu';
|
||||
exports.title = 'CPU';
|
||||
exports.version = '1.0.0';
|
||||
exports.author = 'Peter Širka';
|
||||
exports.group = 'Monitoring';
|
||||
exports.color = '#F6BB42';
|
||||
exports.output = 1;
|
||||
exports.icon = 'microchip';
|
||||
exports.options = { enabled: true };
|
||||
exports.click = true;
|
||||
exports.readme = `# CPU monitoring
|
||||
|
||||
This component monitors CPU \`% percentage\` consumption in Linux systems. It uses \`mpstat\` command.
|
||||
|
||||
__Data Example__:
|
||||
|
||||
\`\`\`javascript
|
||||
{
|
||||
cpu: 30, // percentage
|
||||
cores: [4, 60, 0], // percentage
|
||||
count: 3 // count of cores
|
||||
}
|
||||
\`\`\``;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-3 m">
|
||||
<div data---="textbox__interval__placeholder:10000;increment:true;type:number;required:true;maxlength:10;align:center">@(Interval in milliseconds)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var fields = ['CPU', '%idle'];
|
||||
var current = { cores: [], cpu: 0, count: 0 };
|
||||
var proc = null;
|
||||
var tproc = null;
|
||||
|
||||
instance.custom.kill = function() {
|
||||
if (proc) {
|
||||
proc.kill('SIGKILL');
|
||||
proc = null;
|
||||
}
|
||||
};
|
||||
|
||||
instance.custom.run = function() {
|
||||
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
|
||||
instance.custom.kill();
|
||||
proc = require('child_process').spawn('mpstat', ['-P', 'ALL', 10]);
|
||||
proc.stdout.on('data', U.streamer('\n\n', instance.custom.process));
|
||||
proc.stdout.on('error', function(e) {
|
||||
instance.error(e);
|
||||
instance.custom.kill();
|
||||
tproc = setTimeout(instance.custom.run, instance.options.interval || 5000);
|
||||
});
|
||||
};
|
||||
|
||||
instance.custom.process = function(chunk) {
|
||||
current.cpu = 0;
|
||||
chunk.toString('utf8').parseTerminal(fields, instance.custom.parse);
|
||||
current.count = current.cores.length;
|
||||
if (current.count) {
|
||||
instance.send2(current);
|
||||
instance.custom.status();
|
||||
}
|
||||
};
|
||||
|
||||
instance.custom.parse = function(values) {
|
||||
var val = 100 - values[1].parseFloat2();
|
||||
if (values[0] === 'all')
|
||||
current.cpu = val;
|
||||
else
|
||||
current.cores[+values[0]] = val;
|
||||
};
|
||||
|
||||
instance.custom.status = function() {
|
||||
if (instance.options.enabled)
|
||||
instance.status(current.cpu.floor(1) + '%');
|
||||
else
|
||||
instance.status('Disabled', 'red');
|
||||
};
|
||||
|
||||
instance.on('click', function() {
|
||||
instance.options.enabled = !instance.options.enabled;
|
||||
instance.custom.status();
|
||||
if (instance.options.enabled)
|
||||
instance.custom.run();
|
||||
else
|
||||
instance.custom.kill();
|
||||
});
|
||||
|
||||
instance.on('close', function() {
|
||||
instance.custom.kill();
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(instance.custom.run, 1000);
|
||||
};
|
||||
441
flow/mqtt.js
441
flow/mqtt.js
|
|
@ -1,441 +0,0 @@
|
|||
exports.id = 'mqtt';
|
||||
exports.title = 'MQTT broker';
|
||||
exports.group = 'MQTT';
|
||||
exports.color = '#888600';
|
||||
exports.version = '1.0.1';
|
||||
exports.icon = 'exchange';
|
||||
exports.input = true;
|
||||
exports.output = 0;
|
||||
exports.author = 'Martin Smola';
|
||||
exports.variables = true;
|
||||
exports.options = { host: '127.0.0.1', port: 1883 };
|
||||
exports.traffic = false;
|
||||
exports.npm = ['mqtt'];
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="host" data-jc-config="placeholder:test.mosquitto.org;required:true" class="m">Hostname or IP address</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="port" data-jc-config="placeholder:1883;required:true" class="m">Port</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="clientid">@(Client id)</div>
|
||||
<div class="help m">@(Supports variables, example: \`client_{device-id}\`)</div>
|
||||
<div data-jc="checkbox" data-jc-path="secure" class="m">@(Secure (ssl))</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="checkbox" data-jc-path="auth" class="m">@(Require Authorization)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" data-bind="?.auth__show:value">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="username" class="m">@(Username)</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="password" data-jc-config="type:password" class="m">@(Password)</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="checkbox" data-jc-path="lwt" class="m">@(LWT)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" data-bind="?.lwt__show:value">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="lwttopic">@(Last will topic)</div>
|
||||
<div class="help m">@(Supports variables, example: \`lwt/{device-id}\`)</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="lwtmessage">@(Last will message)</div>
|
||||
<div class="help m">@(Supports variables, example: \`{device-id} is offline\`)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
ON('save.mqtt', function(component, options) {
|
||||
!component.name && (component.name = '{0} @ {1}:{2}'.format(options.username || '', options.host, options.port || '1883'));
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.readme = `
|
||||
# MQTT Broker
|
||||
|
||||
## Input
|
||||
Allows to change connection programaticaly
|
||||
\`\`\`javascipt
|
||||
{
|
||||
host: '1.2.3.4',
|
||||
port: '',
|
||||
secure: true/false,
|
||||
username: 'john',
|
||||
password: 'X',
|
||||
lwttopic: '',
|
||||
lwtmessage: '',
|
||||
clientid: ''
|
||||
}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
var MQTT_BROKERS = [];
|
||||
var mqtt;
|
||||
|
||||
global.MQTT = {};
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var broker;
|
||||
|
||||
mqtt = require('mqtt');
|
||||
|
||||
instance.on('data', function(flowdata){
|
||||
var data= flowdata.data;
|
||||
var options = instance.options;
|
||||
|
||||
if (data.host && data.port)
|
||||
return instance.custom.reconfigure(data, options);
|
||||
|
||||
if (data.close === true)
|
||||
instance.close(NOOP);
|
||||
});
|
||||
|
||||
instance.custom.reconfigure = function(o, old_options) {
|
||||
|
||||
if (old_options)
|
||||
MQTT_BROKERS = MQTT_BROKERS.remove(function(b){
|
||||
return b.id === old_options.id;
|
||||
});
|
||||
|
||||
var options = instance.options;
|
||||
|
||||
if (!options.host || !options.port) {
|
||||
instance.status('Not configured', 'red');
|
||||
return;
|
||||
}
|
||||
|
||||
options.id = (options.username || '') + '@' + options.host + ':' + options.port;
|
||||
|
||||
if (broker) {
|
||||
broker.close();
|
||||
EMIT('mqtt.brokers.status', 'reconfigured', old_options.id, options.id);
|
||||
}
|
||||
|
||||
instance.custom.createBroker();
|
||||
};
|
||||
|
||||
instance.custom.createBroker = function() {
|
||||
|
||||
ON('mqtt.brokers.status', brokerstatus);
|
||||
|
||||
var o = instance.options;
|
||||
var opts = {
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
id: o.id,
|
||||
secure: o.secure,
|
||||
rejectUnauthorized: false,
|
||||
reconnectPeriod: 3000,
|
||||
resubscribe: false
|
||||
};
|
||||
|
||||
if (o.auth) {
|
||||
opts.username = o.username;
|
||||
opts.password = o.password;
|
||||
}
|
||||
|
||||
if (o.lwt) {
|
||||
opts.will = {
|
||||
topic: instance.arg(o.lwttopic),
|
||||
payload: instance.arg(o.lwtmessage)
|
||||
}
|
||||
}
|
||||
|
||||
if (o.clientid)
|
||||
opts.clientId = instance.arg(o.clientid);
|
||||
|
||||
broker = new Broker(opts);
|
||||
MQTT_BROKERS.push(broker);
|
||||
|
||||
instance.status('Ready');
|
||||
};
|
||||
|
||||
instance.close = function(done) {
|
||||
|
||||
broker && broker.close(function() {
|
||||
MQTT_BROKERS = MQTT_BROKERS.remove('id', instance.options.id);
|
||||
EMIT('mqtt.brokers.status', 'removed', instance.options.id);
|
||||
});
|
||||
|
||||
OFF('mqtt.brokers.status', brokerstatus);
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
function brokerstatus(status, brokerid, err) {
|
||||
if (brokerid !== instance.options.id)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case 'connecting':
|
||||
instance.status('Connecting', '#a6c3ff');
|
||||
break;
|
||||
case 'connected':
|
||||
instance.status('Connected', 'green');
|
||||
break;
|
||||
case 'disconnected':
|
||||
instance.status('Disconnected', 'red');
|
||||
break;
|
||||
case 'connectionfailed':
|
||||
instance.status('Connection failed', 'red');
|
||||
break;
|
||||
case 'error':
|
||||
instance.error('MQTT Error, ID: ' + instance.id + '\n ' + err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instance.on('options', instance.custom.reconfigure);
|
||||
instance.custom.reconfigure();
|
||||
};
|
||||
|
||||
FLOW.trigger('mqtt.brokers', function(next) {
|
||||
var brokers = [''];
|
||||
MQTT_BROKERS.forEach(n => brokers.push(n.id));
|
||||
next(brokers);
|
||||
});
|
||||
|
||||
MQTT.add = function(brokerid, componentid) {
|
||||
var broker = MQTT_BROKERS.findItem('id', brokerid);
|
||||
|
||||
if (broker)
|
||||
broker.add(componentid);
|
||||
};
|
||||
|
||||
MQTT.remove = function(brokerid, componentid) {
|
||||
var broker = MQTT_BROKERS.findItem('id', brokerid);
|
||||
broker && broker.remove(componentid);
|
||||
};
|
||||
|
||||
MQTT.publish = function(brokerid, topic, data, options) {
|
||||
var broker = MQTT_BROKERS.findItem('id', brokerid);
|
||||
if (broker)
|
||||
broker.publish(topic, data, options);
|
||||
else
|
||||
EMIT('mqtt.brokers.status', 'error', brokerid, 'No such broker');
|
||||
};
|
||||
|
||||
MQTT.subscribe = function(brokerid, componentid, topic, qos) {
|
||||
var broker = MQTT_BROKERS.findItem('id', brokerid);
|
||||
|
||||
if (!broker)
|
||||
return;
|
||||
|
||||
broker.add(componentid);
|
||||
broker.subscribe(componentid, topic, qos);
|
||||
};
|
||||
|
||||
MQTT.unsubscribe = function(brokerid, componentid, topic, qos) {
|
||||
var broker = MQTT_BROKERS.findItem('id', brokerid);
|
||||
if (!broker)
|
||||
return;
|
||||
|
||||
broker.unsubscribe(componentid, topic);
|
||||
broker.remove(componentid);
|
||||
};
|
||||
|
||||
MQTT.broker = function(brokerid) {
|
||||
return MQTT_BROKERS.findItem('id', brokerid);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
https://github.com/mqttjs/MQTT.js/blob/master/examples/client/secure-client.js
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
- add `birth` and `last will and testament` messages
|
||||
- add options to self.client.connect(broker [,options]); - credentials, certificate etc.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
function Broker(options) {
|
||||
var self = this;
|
||||
|
||||
if (!options.host || !options.port)
|
||||
return false;
|
||||
|
||||
self.connecting = false;
|
||||
self.connected = false;
|
||||
self.closing = false;
|
||||
self.components = [];
|
||||
self.subscribtions = {};
|
||||
self.id = options.id;
|
||||
self.options = options;
|
||||
setTimeout(function() {
|
||||
EMIT('mqtt.brokers.status', 'new', self.id);
|
||||
}, 500);
|
||||
return self;
|
||||
}
|
||||
|
||||
Broker.prototype.connect = function() {
|
||||
|
||||
var self = this;
|
||||
if (self.connected || self.connecting)
|
||||
return EMIT('mqtt.brokers.status', self.connected ? 'connected' : 'connecting', self.id);
|
||||
|
||||
self.connecting = true;
|
||||
var broker = self.options.secure ? 'mqtts://' : 'mqtt://' + self.options.host + ':' + self.options.port;
|
||||
|
||||
EMIT('mqtt.brokers.status', 'connecting', self.id);
|
||||
|
||||
self.client = mqtt.connect(broker, self.options);
|
||||
|
||||
self.client.on('connect', function() {
|
||||
self.connecting = false;
|
||||
self.connected = true;
|
||||
if (self.reconnecting) {
|
||||
EMIT('mqtt.brokers.status', 'reconnected', self.id);
|
||||
self.reconnecting = false;
|
||||
self.resubscribe();
|
||||
}
|
||||
EMIT('mqtt.brokers.status', 'connected', self.id);
|
||||
});
|
||||
|
||||
self.client.on('reconnect', function() {
|
||||
self.connecting = true;
|
||||
self.connected = false;
|
||||
self.reconnecting = true;
|
||||
EMIT('mqtt.brokers.status', 'connecting', self.id);
|
||||
});
|
||||
|
||||
self.client.on('message', function(topic, message) {
|
||||
message = message.toString();
|
||||
if (message[0] === '{') {
|
||||
TRY(function() {
|
||||
message = JSON.parse(message);
|
||||
}, () => FLOW.debug('MQTT: Error parsing data', message));
|
||||
}
|
||||
EMIT('mqtt.brokers.message', self.id, topic, message);
|
||||
});
|
||||
|
||||
self.client.on('close', function(err) {
|
||||
if (err && err.toString().indexOf('Error')) {
|
||||
self.connecting = false;
|
||||
self.connected = false;
|
||||
EMIT('mqtt.brokers.status', 'error', self.id, err.code);
|
||||
}
|
||||
|
||||
if (self.connected || !self.connecting) {
|
||||
self.connected = false;
|
||||
EMIT('mqtt.brokers.status', 'disconnected', self.id);
|
||||
} else if (self.connecting) {
|
||||
self.connecting = false;
|
||||
EMIT('mqtt.brokers.status', 'connectionfailed', self.id);
|
||||
}
|
||||
});
|
||||
|
||||
self.client.on('error', function(err) {
|
||||
|
||||
if (self.connecting) {
|
||||
self.client.end();
|
||||
self.connecting = false;
|
||||
EMIT('mqtt.brokers.status', 'error', self.id, err);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Broker.prototype.disconnect = function(reconnect) {
|
||||
var self = this;
|
||||
if (!self.closing)
|
||||
self.close(function(){
|
||||
reconnect && self.connect();
|
||||
});
|
||||
};
|
||||
|
||||
Broker.prototype.close = function(callback) {
|
||||
var self = this;
|
||||
self.closing = true;
|
||||
|
||||
if ((self.connected || self.connecting) && self.client && self.client.end)
|
||||
self.client.end(true, cb);
|
||||
else
|
||||
cb();
|
||||
|
||||
function cb() {
|
||||
EMIT('mqtt.brokers.status', 'disconnected', self.id);
|
||||
self.client && self.client.removeAllListeners();
|
||||
self.components = [];
|
||||
self.client = null;
|
||||
callback && callback();
|
||||
}
|
||||
};
|
||||
|
||||
Broker.prototype.subscribe = function(componentid, topic) {
|
||||
var self = this;
|
||||
self.subscribtions[topic] = self.subscribtions[topic] || [];
|
||||
if (self.subscribtions[topic].indexOf(componentid) > -1)
|
||||
return;
|
||||
self.client.subscribe(topic);
|
||||
self.subscribtions[topic].push(componentid);
|
||||
};
|
||||
|
||||
Broker.prototype.resubscribe = function() {
|
||||
var self = this;
|
||||
var topics = Object.keys(self.subscribtions);
|
||||
for (var i = 0; i < topics.length; i++)
|
||||
self.client.subscribe(topics[i]);
|
||||
};
|
||||
|
||||
Broker.prototype.unsubscribe = function(componentid, topic) {
|
||||
var self = this;
|
||||
var sub = self.subscribtions[topic];
|
||||
if (sub) {
|
||||
self.subscribtions[topic] = sub.remove(componentid);
|
||||
self.client.connected && !self.subscribtions[topic].length && self.client.unsubscribe(topic);
|
||||
}
|
||||
};
|
||||
|
||||
Broker.prototype.publish = function(topic, data, options) {
|
||||
var self = this;
|
||||
if (!self.connected)
|
||||
return;
|
||||
|
||||
if (typeof(data) === 'object') {
|
||||
options.qos = parseInt(data.qos || options.qos);
|
||||
options.retain = data.retain || options.retain;
|
||||
topic = data.topic || topic;
|
||||
data.payload && (data = typeof(data.payload) === 'string' ? data.payload : JSON.stringify(data.payload));
|
||||
}
|
||||
|
||||
if (options.qos !== 0 || options.qos !== 1 || options.qos !== 2)
|
||||
options.qos = null;
|
||||
|
||||
if (typeof(data) !== 'string')
|
||||
data = JSON.stringify(data);
|
||||
|
||||
self.client.publish(topic, data || '', options);
|
||||
};
|
||||
|
||||
Broker.prototype.add = function(componentid) {
|
||||
var self = this;
|
||||
self.components.indexOf(componentid) === -1 && self.components.push(componentid);
|
||||
self.connect();
|
||||
};
|
||||
|
||||
Broker.prototype.remove = function(componentid) {
|
||||
var self = this;
|
||||
self.components = self.components.remove(componentid);
|
||||
!self.components.length && self.disconnect();
|
||||
};
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
exports.id = 'mqttlistener';
|
||||
exports.title = 'MQTT listener';
|
||||
exports.group = 'MQTT';
|
||||
exports.color = '#888600';
|
||||
exports.version = '1.0.0';
|
||||
exports.icon = 'sign-out';
|
||||
exports.input = 1;
|
||||
exports.output = ["red", "white"];
|
||||
exports.author = 'Rastislav Kovac';
|
||||
exports.options = { host: "", port: 1883, clientid: "", username: "" };
|
||||
//exports.npm = ['mqtt', 'streamroller'];
|
||||
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="host" data-jc-config="placeholder:test.mosquitto.org;required:false" class="m">Hostname or IP address</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="port" data-jc-config="placeholder:1883;required:true" class="m">Port</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="clientid">@(Client id)</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="username" class="m">@(Username)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
exports.readme = `
|
||||
# MQTT processor
|
||||
|
||||
Version 1.0.0
|
||||
|
||||
It serves as a client and subscribes to 'rpc' topic. It receives rpc calls from kovobel-prod01, which receives it from platform.
|
||||
|
||||
`;
|
||||
|
||||
const instanceSendTo = {
|
||||
debug: 0,
|
||||
rpcCall: 1,
|
||||
}
|
||||
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
|
||||
const mqtt = require("mqtt");
|
||||
|
||||
instance.on('options', loadNodes);
|
||||
|
||||
|
||||
function loadNodes()
|
||||
{
|
||||
|
||||
if(instance.options.host == "")
|
||||
{
|
||||
instance.status('No configuration', 'red');
|
||||
}
|
||||
else
|
||||
{
|
||||
var o = instance.options;
|
||||
opts = {
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
clientId: o.clientid,
|
||||
rejectUnauthorized: false,
|
||||
resubscribe: true
|
||||
};
|
||||
|
||||
connectToServer();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function connectToServer()
|
||||
{
|
||||
var url = "mqtt://" + opts.host + ":" + opts.port;
|
||||
console.log("MQTT URL: ", url);
|
||||
|
||||
client = mqtt.connect(url, opts);
|
||||
|
||||
client.on('connect', function() {
|
||||
instance.status("Connected", "green");
|
||||
client.subscribe('rpc', function (err) {
|
||||
if (!err) {
|
||||
client.publish('rpc', 'Hello mqtt');
|
||||
console.log('message published');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
client.on('reconnect', function() {
|
||||
instance.status("Reconnecting", "yellow");
|
||||
});
|
||||
|
||||
client.on('message', function(topic, message) {
|
||||
// message is type of buffer
|
||||
message = message.toString();
|
||||
|
||||
// instance.send(1, message);
|
||||
// return;
|
||||
|
||||
if (message[0] === '{') {
|
||||
|
||||
try {
|
||||
message = JSON.parse(message);
|
||||
|
||||
if (Object.keys(message).length < 2 || !Object.keys(message).includes("message")) return;
|
||||
|
||||
message.message = JSON.parse(message.message);
|
||||
instance.send(instanceSendTo.rpcCall, message);
|
||||
|
||||
} catch (e) {
|
||||
instance.debug(`MQTT: Error parsing data, ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
client.on('close', function(err) {
|
||||
if (err && err.toString().indexOf('Error')) {
|
||||
instance.status("Err: "+err.code, "red");
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
||||
} else {
|
||||
instance.status("Disconnected", "red");
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
||||
}
|
||||
});
|
||||
|
||||
client.on('error', function(err) {
|
||||
instance.status("Err: "+ err.code, "red");
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts });
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//set opts accortding to options
|
||||
instance.reconfigure = function() {
|
||||
|
||||
var o = instance.options;
|
||||
opts = {
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
clientId: o.clientid,
|
||||
rejectUnauthorized: false,
|
||||
resubscribe: true
|
||||
};
|
||||
|
||||
connectToServer();
|
||||
};
|
||||
|
||||
instance.close = function(done) {
|
||||
client.end();
|
||||
};
|
||||
|
||||
|
||||
loadNodes();
|
||||
|
||||
instance.on('options', instance.reconfigure);
|
||||
instance.reconfigure();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
exports.id = 'mqttpublish';
|
||||
exports.title = 'MQTT publish';
|
||||
exports.group = 'MQTT';
|
||||
exports.color = '#888600';
|
||||
exports.version = '1.1.0';
|
||||
exports.icon = 'sign-out';
|
||||
exports.input = true;
|
||||
exports.output = 1;
|
||||
exports.author = 'Martin Smola';
|
||||
exports.options = {};
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="dropdown" data-jc-path="broker" data-jc-config="datasource:mqttconfig.brokers;required:true" class="m">@(Brokers)</div>
|
||||
<div data-jc="textbox" data-jc-path="topic" data-jc-config="placeholder:hello/world">Topic</div>
|
||||
<div class="help m">@(Supports variables, example: \`device/{device-id}\`)</div>
|
||||
<div data-jc="textbox" data-jc-path="staticmessage" data-jc-config="placeholder:123">Static message(string)</div>
|
||||
<div class="help m">@(Supports variables), @(If specified then incoming data are ignored and this message is sent instead. Topic is required if static message is defined.)</div>
|
||||
<div data-jc="dropdown" data-jc-path="qos" data-jc-config="items:,0,1,2" class="m">@(QoS)</div>
|
||||
<div data-jc="checkbox" data-jc-path="retain" class="m">@(Retain)</div>
|
||||
</div>
|
||||
<script>
|
||||
var mqttconfig = { brokers: [] };
|
||||
ON('open.mqttpublish', function(component, options) {
|
||||
TRIGGER('mqtt.brokers', 'mqttconfig.brokers');
|
||||
});
|
||||
ON('save.mqttpublish', function(component, options) {
|
||||
!component.name && (component.name = options.broker + (options.topic ? ' -> ' + options.topic : ''));
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.readme = `# MQTT publish
|
||||
|
||||
If the topic field is left empty and the data object does not have a 'topic' property then nothing is send.
|
||||
Also if data object has a valid topic property it is assumed the object also have data property which is send as a payload;
|
||||
Example:
|
||||
\`\`\`javacsript
|
||||
{
|
||||
topic: '/topic',
|
||||
data: {
|
||||
hello: 'world'
|
||||
}
|
||||
}
|
||||
// in above case only { hello: 'world' } is published
|
||||
\`\`\`
|
||||
|
||||
If the topic field is not empty then the entire incomming data object is passed to the output.`;
|
||||
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var PUBLISH_OPTIONS = {};
|
||||
|
||||
var ready = false;
|
||||
|
||||
instance.custom.reconfigure = function() {
|
||||
|
||||
ready = false;
|
||||
|
||||
if (!MQTT.broker(instance.options.broker))
|
||||
return instance.status('No broker', 'red');
|
||||
|
||||
if (instance.options.broker) {
|
||||
|
||||
MQTT.add(instance.options.broker, instance.id);
|
||||
ready = true;
|
||||
PUBLISH_OPTIONS.retain = instance.options.retain || false;
|
||||
PUBLISH_OPTIONS.qos = parseInt(instance.options.qos || 0);
|
||||
return;
|
||||
}
|
||||
|
||||
instance.status('Not configured', 'red');
|
||||
};
|
||||
|
||||
instance.on('options', instance.custom.reconfigure);
|
||||
|
||||
instance.on('data', function(flowdata) {
|
||||
if (!ready)
|
||||
return;
|
||||
var msg = instance.options.staticmessage ? instance.arg(instance.options.staticmessage) : flowdata.data;
|
||||
var topic = instance.arg(instance.options.topic || msg.topic);
|
||||
if (topic) {
|
||||
if (msg.topic)
|
||||
msg = msg.data;
|
||||
MQTT.publish(instance.options.broker, topic, msg, PUBLISH_OPTIONS);
|
||||
} else
|
||||
instance.debug('MQTT publish no topic');
|
||||
|
||||
instance.send(flowdata);
|
||||
});
|
||||
|
||||
instance.on('close', function() {
|
||||
MQTT.remove(instance.options.broker, instance.id);
|
||||
OFF('mqtt.brokers.status', instance.custom.brokerstatus);
|
||||
});
|
||||
|
||||
|
||||
instance.custom.brokerstatus = function(status, brokerid, msg) {
|
||||
if (brokerid !== instance.options.broker)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case 'connecting':
|
||||
instance.status('Connecting', '#a6c3ff');
|
||||
break;
|
||||
case 'connected':
|
||||
instance.status('Connected', 'green');
|
||||
break;
|
||||
case 'disconnected':
|
||||
instance.status('Disconnected', 'red');
|
||||
break;
|
||||
case 'connectionfailed':
|
||||
instance.status('Connection failed', 'red');
|
||||
break;
|
||||
case 'new':
|
||||
!ready && instance.custom.reconfigure();
|
||||
break;
|
||||
case 'removed':
|
||||
instance.custom.reconfigure();
|
||||
break;
|
||||
case 'error':
|
||||
instance.status(msg, 'red');
|
||||
break;
|
||||
case 'reconfigured':
|
||||
instance.options.broker = msg;
|
||||
instance.reconfig();
|
||||
instance.custom.reconfigure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ON('mqtt.brokers.status', instance.custom.brokerstatus);
|
||||
|
||||
instance.custom.reconfigure();
|
||||
};
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
exports.id = 'mqttsubscribe';
|
||||
exports.title = 'MQTT subscribe';
|
||||
exports.group = 'MQTT';
|
||||
exports.color = '#888600';
|
||||
exports.version = '1.1.0';
|
||||
exports.icon = 'sign-in';
|
||||
exports.output = 1;
|
||||
exports.variables = true;
|
||||
exports.author = 'Martin Smola';
|
||||
exports.options = {};
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="dropdown" data-jc-path="broker" data-jc-config="datasource:mqttconfig.brokers;required:true" class="m">@(Select a broker)</div>
|
||||
<div data-jc="textbox" data-jc-path="topic" data-jc-config="placeholder:hello/world;required:true">Topic</div>
|
||||
<div class="help m">@(Supports variables, example: \`device/{device-id}\`)</div>
|
||||
<div data-jc="dropdown" data-jc-path="qos" data-jc-config="items:,0,1,2" class="m">@(QoS)</div>
|
||||
</div>
|
||||
<script>
|
||||
var mqttconfig = { brokers: [] };
|
||||
ON('open.mqttsubscribe', function(component, options) {
|
||||
TRIGGER('mqtt.brokers', 'mqttconfig.brokers');
|
||||
});
|
||||
ON('save.mqttsubscribe', function(component, options) {
|
||||
!component.name && (component.name = options.broker + (options.topic ? ' -> ' + options.topic : ''));
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.readme = `
|
||||
# MQTT subscribe
|
||||
|
||||
The data recieved are passed to the output as follows:
|
||||
\`\`\`javascript
|
||||
{
|
||||
topic: '/lights/on',
|
||||
data: 'kitchen'
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
If the topic is wildcard then there's an array of matches in flowdata repository which can be used in Function component like so:
|
||||
\`\`\`javascript
|
||||
// wildcard -> /+/status
|
||||
// topic -> /devicename/status
|
||||
|
||||
var match = flowdata.get('mqtt_wildcard');
|
||||
// match === ['devicename']
|
||||
\`\`\`
|
||||
|
||||
More on wildcard topics [here](https://mosquitto.org/man/mqtt-7.html)
|
||||
`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var old_topic;
|
||||
var ready = false;
|
||||
|
||||
instance.custom.reconfigure = function(o, old_options) {
|
||||
|
||||
|
||||
ready = false;
|
||||
|
||||
if (!MQTT.broker(instance.options.broker))
|
||||
return instance.status('No broker', 'red');
|
||||
|
||||
if (instance.options.broker && instance.options.topic) {
|
||||
|
||||
if (old_topic)
|
||||
MQTT.unsubscribe(instance.options.broker, instance.id, old_topic);
|
||||
|
||||
old_topic = instance.arg(instance.options.topic);
|
||||
MQTT.subscribe(instance.options.broker, instance.id, old_topic);
|
||||
ready = true;
|
||||
return;
|
||||
}
|
||||
|
||||
instance.status('Not configured', 'red');
|
||||
};
|
||||
|
||||
instance.on('options', instance.custom.reconfigure);
|
||||
|
||||
instance.on('close', function() {
|
||||
MQTT.unsubscribe(instance.options.broker, instance.id, instance.options.topic);
|
||||
OFF('mqtt.brokers.message', instance.custom.message);
|
||||
OFF('mqtt.brokers.status', instance.custom.brokerstatus);
|
||||
});
|
||||
|
||||
instance.custom.brokerstatus = function(status, brokerid, msg) {
|
||||
if (brokerid !== instance.options.broker)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case 'connecting':
|
||||
instance.status('Connecting', '#a6c3ff');
|
||||
break;
|
||||
case 'connected':
|
||||
instance.status('Connected', 'green');
|
||||
break;
|
||||
case 'disconnected':
|
||||
instance.status('Disconnected', 'red');
|
||||
break;
|
||||
case 'connectionfailed':
|
||||
instance.status('Connection failed', 'red');
|
||||
break;
|
||||
case 'new':
|
||||
!ready && instance.custom.reconfigure();
|
||||
break;
|
||||
case 'removed':
|
||||
instance.custom.reconfigure();
|
||||
break;
|
||||
case 'error':
|
||||
instance.status(msg, 'red');
|
||||
break;
|
||||
case 'reconfigured':
|
||||
instance.options.broker = msg;
|
||||
instance.reconfig();
|
||||
instance.custom.reconfigure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instance.custom.message = function(brokerid, topic, message) {
|
||||
if (brokerid !== instance.options.broker)
|
||||
return;
|
||||
|
||||
var match = mqttWildcard(topic, old_topic);
|
||||
if (match) {
|
||||
var flowdata = instance.make({ topic: topic, data: message })
|
||||
flowdata.set('mqtt_wildcard', match);
|
||||
instance.send2(flowdata);
|
||||
}
|
||||
}
|
||||
|
||||
ON('mqtt.brokers.message', instance.custom.message);
|
||||
ON('mqtt.brokers.status', instance.custom.brokerstatus);
|
||||
|
||||
instance.custom.reconfigure();
|
||||
};
|
||||
|
||||
// https://github.com/hobbyquaker/mqtt-wildcard
|
||||
function mqttWildcard(topic, wildcard) {
|
||||
if (topic === wildcard) {
|
||||
return [];
|
||||
} else if (wildcard === '#') {
|
||||
return [topic];
|
||||
}
|
||||
|
||||
var res = [];
|
||||
|
||||
var t = String(topic).split('/');
|
||||
var w = String(wildcard).split('/');
|
||||
|
||||
var i = 0;
|
||||
for (var lt = t.length; i < lt; i++) {
|
||||
if (w[i] === '+') {
|
||||
res.push(t[i]);
|
||||
} else if (w[i] === '#') {
|
||||
res.push(t.slice(i).join('/'));
|
||||
return res;
|
||||
} else if (w[i] !== t[i]) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (w[i] === '#') {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return (i === w.length) ? res : null;
|
||||
}
|
||||
191
flow/nosql.js
191
flow/nosql.js
|
|
@ -1,191 +0,0 @@
|
|||
exports.id = 'nosql';
|
||||
exports.title = 'NoSQL';
|
||||
exports.version = '1.2.1';
|
||||
exports.group = 'Databases';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.color = '#D770AD';
|
||||
exports.icon = 'database';
|
||||
exports.input = true;
|
||||
exports.output = 2;
|
||||
exports.options = {};
|
||||
exports.readme = `# NoSQL embedded
|
||||
|
||||
## Outputs
|
||||
|
||||
First output is response from nosql engine and second is the data passed in.
|
||||
|
||||
## Collection
|
||||
|
||||
if the collection field is left empty, then we try to look at \`flowdata.get('collection')\`, to set this value you need to use \`flowdata.set('collection', '<collection-name>')\` in previous component (currently only \`function\` can be used)
|
||||
|
||||
## Insert
|
||||
|
||||
- will insert recieved data
|
||||
- expects data to be an Object
|
||||
- returns error, success, id
|
||||
|
||||
## Read
|
||||
|
||||
- will read a document by id
|
||||
- expects data to be an Object with an \`id\` property
|
||||
- returns error, response
|
||||
|
||||
## Update
|
||||
|
||||
- will update document by id
|
||||
- expects data to be an Object with \`id\` property and all the props to be updated
|
||||
- returns error, response
|
||||
- if response is 0 then update failed
|
||||
|
||||
## Remove
|
||||
|
||||
- will remove document by id
|
||||
- expects data to be an Object with an \`id\` property
|
||||
- returns error, response
|
||||
- if response is 0 then remove failed
|
||||
|
||||
## Query
|
||||
|
||||
- will query DB
|
||||
- expects data to be an Array as shown bellow
|
||||
- returns error, response
|
||||
|
||||
\`\`\`javascript
|
||||
[
|
||||
['where', 'sensor', 'temp'], // builder.where('sensor', 'temp');
|
||||
['limit', 2] // builder.limit(2);
|
||||
]
|
||||
\`\`\``;
|
||||
|
||||
exports.html = `
|
||||
<div class="padding">
|
||||
<div data-jc="textbox" data-jc-path="collection" class="m mt10">DB collection name</div>
|
||||
<div data-jc="dropdown" data-jc-path="method" data-jc-config="required:true;items:insert,update,read,query,remove" class="m">@(Method)</div>
|
||||
<div data-jc="visible" data-jc-path="method" data-jc-config="if:value === 'insert'">
|
||||
<div data-jc="checkbox" data-jc-path="addid">Add unique ID to data before insert</div>
|
||||
</div>
|
||||
<div data-jc="visible" data-jc-path="method" data-jc-config="if:value === 'update'">
|
||||
<div data-jc="checkbox" data-jc-path="upsert">Insert document if it doesn't exist</div>
|
||||
<div data-jc="checkbox" data-jc-path="upsertid">Add unique ID to data before insert (only if it doesn't exist)</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
instance.on('data', function(flowdata, next) {
|
||||
|
||||
instance.send2(1, flowdata.clone());
|
||||
|
||||
var options = instance.options;
|
||||
var collection = options.collection || flowdata.get('collection');
|
||||
if (!collection) {
|
||||
flowdata.data = { err: '[DB] No collection specified' };
|
||||
next(0, flowdata);
|
||||
instance.error('[DB] No collection specified');
|
||||
return;
|
||||
}
|
||||
|
||||
var nosql = NOSQL(collection);
|
||||
var builder;
|
||||
|
||||
if (options.method === 'read') {
|
||||
|
||||
if (!flowdata.data.id) {
|
||||
flowdata.data = { err: '[DB] Cannot get record by id: `undefined`' };
|
||||
next(0, flowdata);
|
||||
instance.error('[DB] Cannot get record by id: `undefined`');
|
||||
return;
|
||||
}
|
||||
|
||||
builder = nosql.find();
|
||||
builder.where('id', flowdata.data.id);
|
||||
builder.first();
|
||||
builder.callback(function(err, response) {
|
||||
if (err) {
|
||||
instance.throw(err);
|
||||
} else {
|
||||
flowdata.data = { response: response };
|
||||
next(0, flowdata);
|
||||
}
|
||||
});
|
||||
|
||||
} else if (options.method === 'insert') {
|
||||
|
||||
options.addid && (flowdata.data.id = UID());
|
||||
nosql.insert(flowdata.data).callback(function(err) {
|
||||
if (err)
|
||||
instance.throw(err);
|
||||
else {
|
||||
flowdata.data = { success: err ? false : true, id: flowdata.data.id };
|
||||
next(0, flowdata);
|
||||
}
|
||||
});
|
||||
|
||||
} else if (options.method === 'query') {
|
||||
|
||||
var query = flowdata.data;
|
||||
builder = nosql.find();
|
||||
|
||||
query && query instanceof Array && query.forEach(function(q) {
|
||||
if (q instanceof Array) {
|
||||
var m = q[0];
|
||||
var args = q.splice(1);
|
||||
builder[m] && (builder[m].apply(builder, args));
|
||||
}
|
||||
});
|
||||
|
||||
builder.callback(function(err, response) {
|
||||
if (err) {
|
||||
instance.throw(err);
|
||||
} else {
|
||||
flowdata.data = { response: response || [] };
|
||||
next(0, flowdata);
|
||||
}
|
||||
});
|
||||
|
||||
} else if (options.method === 'update') {
|
||||
|
||||
if (!options.upsert && !flowdata.data.id) {
|
||||
flowdata.data = { err: '[DB] Cannot update record by id: `undefined`' };
|
||||
next(0, flowdata);
|
||||
instance.error('[DB] Cannot update record by id: `undefined`');
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.upsert && (options.upsertid && !flowdata.data.id)) {
|
||||
flowdata.data.id = UID();
|
||||
builder = nosql.modify(flowdata.data, options.upsert);
|
||||
builder.where('id', flowdata.data.id);
|
||||
builder.callback(function(err, count) {
|
||||
if (err)
|
||||
instance.throw(err);
|
||||
else {
|
||||
flowdata.data = { response: count || 0 };
|
||||
next(0, flowdata);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else if (options.method === 'remove') {
|
||||
|
||||
if (!flowdata.data.id) {
|
||||
flowdata.data = { err: '[DB] Cannot remove record by id: `undefined`' };
|
||||
next(0, flowdata);
|
||||
instance.error('[DB] Cannot remove record by id: `undefined`');
|
||||
return;
|
||||
}
|
||||
|
||||
builder = nosql.remove();
|
||||
builder.where('id', flowdata.data.id);
|
||||
builder.callback(function(err, count) {
|
||||
if (err)
|
||||
instance.throw(err);
|
||||
else {
|
||||
flowdata.data = { response: count || 0 };
|
||||
next(0, flowdata);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
357
flow/relays.js
357
flow/relays.js
|
|
@ -1,357 +0,0 @@
|
|||
exports.id = 'relay';
|
||||
exports.title = 'DI_DO_Controller';
|
||||
exports.version = '1.0.0';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#2134B0';
|
||||
exports.input = 1;
|
||||
exports.output = ["red", "white", "yellow"];
|
||||
exports.click = false;
|
||||
exports.author = 'Daniel Segeš';
|
||||
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 = `# Sets RS232 port and all digital pins on device. Then it starts to receive data from sensors.
|
||||
It receives:
|
||||
|
||||
rotary_switch_state,
|
||||
rotary_switch_state,
|
||||
door_condition,
|
||||
state_of_breaker,
|
||||
state_of_contactor,
|
||||
twilight_sensor
|
||||
`;
|
||||
|
||||
/*
|
||||
we open rsPort "/dev/ttymxc0" and set digital input and output pins with "setRSPortData"
|
||||
Currently we are interested in pins no. 1,2,3,6,8,9,10,16
|
||||
pins number 11, 12, 13 (we receive 10,11,12 in rsPortReceivedData) are "stykace"
|
||||
When port receives data, it must be exactly 4 bytes long. Second byte is pin, that changed its value, fourth byte is value itself.
|
||||
After that, we set this value to "previousValues[allPins[whichpin]]" variable
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
RVO objekt:
|
||||
state_of_main_switch - sem sa bude reportovať stav hlavného ističa : 0-> off 1-> on (toto nie je na platforme, ale Rado to už do entity type doplnil)
|
||||
rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky:
|
||||
Manual = 1 a Auto = 0 -> vyreportuje Manual
|
||||
Manual = 0 a Auto = 0 -> vyreportuje Off
|
||||
Manual = 0 a Auto = 1 -> vyreportuje Automatic
|
||||
|
||||
door_condition - tuto ide pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open
|
||||
twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy
|
||||
|
||||
zjavne nám v jsone chýba stav hlavného ističa. Musíme to potom doplniť
|
||||
|
||||
Na každú líniu:
|
||||
state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off 1-> on
|
||||
state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off 1-> on
|
||||
momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme
|
||||
*/
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
let previousValues = {};
|
||||
let rsPortReceivedData = [];
|
||||
|
||||
console.log("DI_DO_Relay_Controller installed");
|
||||
|
||||
//key is PIN number
|
||||
const conversionTable = {
|
||||
"1": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "state_of_main_switch"}, //state_of_main_switch pin1
|
||||
"2": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "rotary_switch_state"}, //rotary_switch_state - poloha manual = pin2
|
||||
"3": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "rotary_switch_state"}, //rotary_switch_state - poloha auto = pin3
|
||||
"6": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "door_condition"}, // door_condition = pin6, 1 -> vyreportuje Closed, 0 -> vyreportuje Open
|
||||
"8": {tbname: "RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O", type: "state_of_breaker", "line": 1}, // state_of_breaker linia 1 0=off, 1=on
|
||||
"9": {tbname: "dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7", type: "state_of_breaker", "line": 2}, // state_of_breaker linia 2 0=off, 1=on
|
||||
"10": {tbname: "vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V", type: "state_of_breaker", "line": 3}, // state_of_breaker linia 3 0=off, 1=on
|
||||
"11": {tbname: "RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O", type: "state_of_contactor", "line": 1}, // state_of_contactor linia 1 0=off, 1=on
|
||||
"12": {tbname: "dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7", type: "state_of_contactor", "line": 2}, // state_of_contactor linia 2 0=off, 1=on
|
||||
"13": {tbname: "vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V", type: "state_of_contactor", "line": 3}, // state_of_contactor linia 3 0=off, 1=on
|
||||
"16": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "state_of_main_switch"}, // twilight_sensor = pin16
|
||||
};
|
||||
|
||||
const dbRelays = TABLE("relays");
|
||||
dbRelays.on('change', function(doc, old) {
|
||||
instance.send(2, "reload_relays");
|
||||
});
|
||||
|
||||
//modify
|
||||
|
||||
const SerialPort = require('serialport');
|
||||
//const { exec } = require('child_process');
|
||||
const { openPort, runSyncExec, writeData } = require('./serialport_helper.js');
|
||||
|
||||
const setRSPortData = [0xAA,6,6,6,6,0,6,0,6,6,6,1,1,1,1,0,0,10,10,10,10,0,10,0,10,10,10,0,0,0,0,0,0,5,0,0,0,15,15,15,15,0,15,0,15,15,15,0,0,0,0,0,0,30,0,0,0];
|
||||
const rsPort = new SerialPort("/dev/ttymxc0", { autoOpen: false });
|
||||
|
||||
rsPort.on('open', async function() {
|
||||
|
||||
await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function (status) {
|
||||
instance.send(0, exports.title + " runSyncExec - Promise Resolved:" + status);
|
||||
|
||||
//set port
|
||||
rsPort.write(Buffer.from(setRSPortData), function(err) {
|
||||
instance.send(0, exports.title + " Digital in_out has been set");
|
||||
|
||||
//force turn off relays
|
||||
let keys = Object.keys(conversionTable);
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
|
||||
if(conversionTable[key].type == "state_of_contactor")
|
||||
{
|
||||
let pin = key - 1;
|
||||
let line = conversionTable[key].line;
|
||||
|
||||
turnOff(line, pin);
|
||||
}
|
||||
}
|
||||
|
||||
//dbRelays.modify({ contactor: 0 });
|
||||
})
|
||||
|
||||
}).catch(function (reason) {
|
||||
instance.send(0, exports.title + " runSyncExec - promise rejected:" + reason);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
rsPort.open();
|
||||
|
||||
rsPort.on('data', function (data){
|
||||
|
||||
rsPortReceivedData = [...rsPortReceivedData, ...data];
|
||||
|
||||
if (rsPortReceivedData[0] != 85) {
|
||||
rsPortReceivedData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let l = rsPortReceivedData.length;
|
||||
|
||||
if (l < 4 ) return;
|
||||
|
||||
if (l > 4 ) {
|
||||
|
||||
// if array length is greater than 4, we take first 4 byte and do the logic, second 4 bytes, do the logic and so on
|
||||
let i, j, temparray, chunk = 4;
|
||||
for ( i = 0, j = l; i < j; i += chunk ) {
|
||||
temparray = rsPortReceivedData.slice(i, i + chunk);
|
||||
|
||||
if ( temparray.length < 4 ){
|
||||
rsPortReceivedData = [...temparray];
|
||||
return;
|
||||
}
|
||||
|
||||
switchLogic(temparray);
|
||||
}
|
||||
|
||||
rsPortReceivedData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
switchLogic(rsPortReceivedData);
|
||||
|
||||
rsPortReceivedData = [];
|
||||
|
||||
});
|
||||
|
||||
rsPort.on('error', function(err) {
|
||||
instance.send(0, err.message);
|
||||
})
|
||||
|
||||
rsPort.on("close", () => {
|
||||
rsPort.close();
|
||||
})
|
||||
|
||||
instance.on("close", () => {
|
||||
rsPort.close();
|
||||
})
|
||||
|
||||
function getPin(line)
|
||||
{
|
||||
//conversionTable
|
||||
let keys = Object.keys(conversionTable);
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
|
||||
if(conversionTable[key].type == "state_of_contactor" && conversionTable[key].line == line)
|
||||
{
|
||||
return key - 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("no pin detected");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function turnOn(line, pin)
|
||||
{
|
||||
if( pin === undefined) pin = getPin(line);
|
||||
if( pin === undefined) return;
|
||||
|
||||
let arr = [0x55];
|
||||
arr.push( pin );
|
||||
arr.push( 0 );
|
||||
arr.push( 1 );
|
||||
|
||||
if(!rsPort.isOpen)
|
||||
{
|
||||
console.log("port is not opened");
|
||||
return;
|
||||
}
|
||||
|
||||
rsPort.write(Buffer.from(arr), function(err) {
|
||||
switchLogic(arr);
|
||||
});
|
||||
}
|
||||
|
||||
function turnOff(line, pin)
|
||||
{
|
||||
if( pin === undefined) pin = getPin(line);
|
||||
if( pin === undefined) return;
|
||||
|
||||
let arr = [0x55];
|
||||
arr.push( pin );
|
||||
arr.push( 0 );
|
||||
arr.push( 0 );
|
||||
|
||||
if(!rsPort.isOpen)
|
||||
{
|
||||
console.log("port is not opened");
|
||||
return;
|
||||
}
|
||||
|
||||
rsPort.write(Buffer.from(arr), function(err) {
|
||||
switchLogic(arr);
|
||||
});
|
||||
}
|
||||
|
||||
// we expect array as flowdata.data
|
||||
instance.on("data", (flowdata) => {
|
||||
|
||||
//console.log(flowdata.data);
|
||||
|
||||
if(flowdata.data instanceof Object)
|
||||
{
|
||||
let obj = flowdata.data;
|
||||
|
||||
let line = obj.line;
|
||||
|
||||
if(obj.command == "turnOn") turnOn(line);
|
||||
else if(obj.command == "turnOff") turnOff(line);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(flowdata.data)){
|
||||
rsPort.write(Buffer.from(flowdata.data), function(err) {
|
||||
switchLogic(flowdata.data);
|
||||
|
||||
instance.send(0,{"WRITE":flowdata.data});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
const switchLogic = (rsPortReceivedData) => {
|
||||
|
||||
let dataToTb;
|
||||
let values = {};
|
||||
|
||||
let pinIndex = rsPortReceivedData[1] + 1;
|
||||
if (pinIndex === 17) pinIndex--;
|
||||
|
||||
let newPinValue = rsPortReceivedData[3];
|
||||
|
||||
let obj = conversionTable[pinIndex];
|
||||
|
||||
if(obj == undefined)
|
||||
{
|
||||
//console.log("undefined pinIndex", pinIndex, rsPortReceivedData);
|
||||
return;
|
||||
}
|
||||
|
||||
let type = obj.type;
|
||||
let line = obj.line;
|
||||
let tbname = obj.tbname;
|
||||
|
||||
//default value
|
||||
let value = "On";
|
||||
if(newPinValue === 0) value = "Off";
|
||||
|
||||
if(type == "rotary_switch_state")
|
||||
{
|
||||
// combination of these two pins required to get result
|
||||
let pin2, pin3;
|
||||
if(pinIndex == 2)
|
||||
{
|
||||
pin2 = newPinValue;
|
||||
pin3 = previousValues[pinIndex];
|
||||
if (pin3 == undefined) pin3 = 0;
|
||||
}
|
||||
else if(pinIndex == 3)
|
||||
{
|
||||
pin3 = newPinValue;
|
||||
pin2 = previousValues[1];
|
||||
if (pin2 == undefined) pin2 = 0;
|
||||
}
|
||||
|
||||
if (pin2 == 1 && pin3 == 0) value = "Manual";
|
||||
if (pin2 == 0 && pin3 == 0) value = "Off";
|
||||
if (pin2 == 0 && pin3 == 1) value = "Automatic";
|
||||
}
|
||||
else if(type == "door_condition")
|
||||
{
|
||||
newPinValue === 0 ? value = "Open" : value = "Closed";
|
||||
}
|
||||
else if(type == "twilight_sensor")
|
||||
{
|
||||
value = parseFloat(newPinValue + (256*rsPortReceivedData[2]));
|
||||
}
|
||||
else if(type == "state_of_contactor")
|
||||
{
|
||||
//modify table relays
|
||||
dbRelays.modify({ contactor: newPinValue }).where("line", line);
|
||||
}
|
||||
|
||||
values[obj.type] = value;
|
||||
|
||||
if(conversionTable.hasOwnProperty(pinIndex))
|
||||
{
|
||||
let insertIntoTb = false;
|
||||
if(newPinValue != previousValues[pinIndex]) insertIntoTb = true;
|
||||
if(obj.hasOwnProperty("state_of_contactor")) insertIntoTb = true;
|
||||
|
||||
if(insertIntoTb)
|
||||
{
|
||||
dataToTb = {
|
||||
[tbname]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": values
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
instance.send(1, dataToTb);
|
||||
}
|
||||
}
|
||||
|
||||
//pin was changed
|
||||
previousValues[pinIndex] = newPinValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
};
|
||||
120
flow/test.js
120
flow/test.js
|
|
@ -1,120 +0,0 @@
|
|||
const getTimezoneOffset = (timeZone, date = new Date()) => {
|
||||
const tz = date.toLocaleString("en", {timeZone, timeStyle: "long"}).split(" ").slice(-1)[0];
|
||||
const dateString = date.toString();
|
||||
const offset = Date.parse(`${dateString} UTC`) - Date.parse(`${dateString} ${tz}`);
|
||||
|
||||
// return UTC offset in millis
|
||||
return offset;
|
||||
}
|
||||
|
||||
let profile = {
|
||||
"time_points": [
|
||||
{
|
||||
"start_time": "13:00",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"start_time": "16:00",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"astro_clock": true,
|
||||
"dusk_astro_clock_offset": 0,
|
||||
"dawn_astro_clock_offset": 0,
|
||||
"dusk_lux_sensor": false,
|
||||
"dawn_lux_sensor": false,
|
||||
"dusk_lux_sensor_value": 5,
|
||||
"dawn_lux_sensor_value": 5,
|
||||
"dusk_lux_sensor_time_window": 30,
|
||||
"dawn_lux_sensor_time_window": 30,
|
||||
}
|
||||
|
||||
let now = new Date(); // Creates a Date Object using the clients current time
|
||||
console.log(now, now.getTime());
|
||||
|
||||
//let [hours, minutes, seconds] = "18:19:02".split(':');
|
||||
//d.setHours(+hours); // Set the hours, using implicit type coercion
|
||||
//d.setMinutes(minutes); // You can pass Number or String. It doesn't really matter
|
||||
//d.setSeconds(seconds);
|
||||
|
||||
let time_points = profile.time_points;
|
||||
time_points.push( {"start_time": "1:00", "value": 1} );
|
||||
|
||||
time_points.sort(function (a, b) {
|
||||
|
||||
let [ahours, aminutes, aseconds] = a.start_time.split(':');
|
||||
let [bhours, bminutes, bseconds] = b.start_time.split(':');
|
||||
|
||||
let ad = new Date();
|
||||
ad.setHours( parseInt(ahours) );
|
||||
ad.setMinutes( parseInt(aminutes) );
|
||||
ad.setSeconds(0);
|
||||
|
||||
let bd = new Date();
|
||||
bd.setHours( parseInt(bhours) );
|
||||
bd.setMinutes( parseInt(bminutes) );
|
||||
ad.setSeconds(0);
|
||||
|
||||
return ad.getTime() - bd.getTime();
|
||||
});
|
||||
|
||||
console.log(time_points);
|
||||
|
||||
let value = time_points[ time_points.length - 1].value;
|
||||
|
||||
for(let i = 0; i < time_points.length; i++)
|
||||
{
|
||||
let [hours, minutes, seconds] = time_points[i].start_time.split(':');
|
||||
|
||||
let start_time = new Date();
|
||||
start_time.setHours( parseInt(hours) );
|
||||
start_time.setMinutes( parseInt(minutes) );
|
||||
start_time.setSeconds(0);
|
||||
|
||||
if(now.getTime() > start_time.getTime())
|
||||
{
|
||||
value = time_points[i].value;
|
||||
}
|
||||
|
||||
//console.log(start_time);
|
||||
}
|
||||
|
||||
console.log(value);
|
||||
|
||||
const offset = getTimezoneOffset("Europe/Bratislava");
|
||||
console.log(offset);
|
||||
|
||||
|
||||
function removeTask(obj)
|
||||
{
|
||||
|
||||
let keys = Object.keys(obj);
|
||||
tasks = tasks.filter((task) => {
|
||||
|
||||
let counter = 0;
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
if(task.hasOwnProperty(key) && obj.hasOwnProperty(key))
|
||||
{
|
||||
if(task[key] == obj[key]) counter++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(counter == keys.length) return false;
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function convertUTCDateToLocalDate(date) {
|
||||
var newDate = new Date(date);
|
||||
newDate.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||||
return newDate;
|
||||
}
|
||||
|
||||
let d = convertUTCDateToLocalDate(new Date("2022-04-26T06:56:54.000Z"));
|
||||
|
||||
console.log(d, d.getHours());
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
exports.id = 'gettemperature';
|
||||
exports.id = 'thermometer';
|
||||
exports.title = 'Thermometer';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#5CB36D';
|
||||
exports.version = '1.0.2';
|
||||
exports.version = '1.0.3';
|
||||
exports.output = ["red", "white", "blue"];
|
||||
exports.author = 'Rastislav Kovac';
|
||||
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 = {
|
||||
debug: 0,
|
||||
tb: 1,
|
||||
di_do_controller: 2
|
||||
dido_controller: 2
|
||||
}
|
||||
|
||||
//read temperature - frequency
|
||||
|
|
@ -43,7 +43,7 @@ const monitor = log4js.getLogger("monitorLogs");
|
|||
//monitor.info('info');
|
||||
//errLogger.error("some error");
|
||||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper');
|
||||
const dbSettings = TABLE("settings");
|
||||
let temperatureAddress = "";
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ loadSettings();
|
|||
exports.install = function(instance) {
|
||||
|
||||
const { exec } = require('child_process');
|
||||
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js');
|
||||
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
|
||||
|
||||
let startRead;
|
||||
let dataToTb;
|
||||
|
|
@ -76,9 +76,9 @@ exports.install = function(instance) {
|
|||
})
|
||||
|
||||
|
||||
const start = function(){
|
||||
const start = function() {
|
||||
|
||||
try{
|
||||
try {
|
||||
|
||||
if(FLOW.OMS_controller_type === "unipi")
|
||||
{
|
||||
|
|
@ -130,8 +130,8 @@ exports.install = function(instance) {
|
|||
|
||||
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
|
||||
|
||||
instance.send(instanceSendTo.tb, dataToTb);
|
||||
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: status});
|
||||
// instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
|
||||
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
|
||||
}
|
||||
else parseData(stdout);
|
||||
}
|
||||
|
|
@ -162,10 +162,9 @@ exports.install = function(instance) {
|
|||
|
||||
logger.debug("gettemperature", data);
|
||||
|
||||
if (!isNaN(data)){
|
||||
if(!isNaN(data)) {
|
||||
|
||||
|
||||
//if ( counter > 290 )
|
||||
if(counter > 290)
|
||||
{
|
||||
instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break");
|
||||
|
||||
|
|
@ -174,21 +173,22 @@ exports.install = function(instance) {
|
|||
}
|
||||
|
||||
logger.debug("gettemperature", data);
|
||||
const values = {
|
||||
"temperature": Number(data.toFixed(2)),
|
||||
"status": "OK"
|
||||
}
|
||||
|
||||
dataToTb = {
|
||||
[edgeName]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": {
|
||||
"temperature": Number(data.toFixed(2)),
|
||||
"status": "OK"
|
||||
}
|
||||
"values":values
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
instance.send(instanceSendTo.tb, dataToTb);
|
||||
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "OK"});
|
||||
instance.send(instanceSendTo.dido_controller, values);
|
||||
|
||||
counter = 0;
|
||||
|
||||
|
|
@ -204,7 +204,7 @@ exports.install = function(instance) {
|
|||
sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer");
|
||||
|
||||
instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day");
|
||||
instance.send(instanceSendTo.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(){
|
||||
start();
|
||||
}, 3000);
|
||||
}, 15000);
|
||||
|
||||
startRead = setInterval(start, timeoutMin * 1000 * 60);
|
||||
|
||||
|
|
@ -119,6 +119,7 @@ process.on('uncaughtException', function (err) {
|
|||
const nosql = NOSQL('tbdata');
|
||||
const nosqlBackup = NOSQL('/backup/tbdata');
|
||||
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var broker;
|
||||
|
|
@ -186,7 +187,7 @@ exports.install = function(instance) {
|
|||
let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
|
||||
let mqtt_username = responseSettings[0]["mqtt_username"];
|
||||
let mqtt_port = responseSettings[0]["mqtt_port"];
|
||||
|
||||
|
||||
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
|
||||
|
||||
opts = {
|
||||
|
|
@ -203,8 +204,7 @@ exports.install = function(instance) {
|
|||
}
|
||||
|
||||
connectToTbServer();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function connectToTbServer()
|
||||
{
|
||||
|
|
@ -215,16 +215,18 @@ exports.install = function(instance) {
|
|||
|
||||
broker.on('connect', function() {
|
||||
instance.status("Connected", "green");
|
||||
brokerready = true;
|
||||
monitor.info("MQTT broker connected");
|
||||
|
||||
brokerready = true;
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
wsmqtt_status = 'connected';
|
||||
wsmqtt_status = 'connected';
|
||||
});
|
||||
|
||||
broker.on('reconnect', function() {
|
||||
instance.status("Reconnecting", "yellow");
|
||||
brokerready = false;
|
||||
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
});
|
||||
|
||||
broker.on('message', function(topic, message) {
|
||||
|
|
@ -243,7 +245,6 @@ exports.install = function(instance) {
|
|||
}
|
||||
|
||||
instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message });
|
||||
|
||||
});
|
||||
|
||||
broker.on('close', function(err) {
|
||||
|
|
@ -258,11 +259,14 @@ exports.install = function(instance) {
|
|||
instance.status("Disconnected", "red");
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
||||
}
|
||||
|
||||
broker.reconnect();
|
||||
});
|
||||
|
||||
broker.on('error', function(err) {
|
||||
instance.status("Err: "+ err.code, "red");
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts });
|
||||
monitor.info('MQTT broker error', err);
|
||||
|
||||
brokerready = false;
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
|
|
@ -270,89 +274,66 @@ exports.install = function(instance) {
|
|||
|
||||
});
|
||||
|
||||
//broker = new Broker(opts);
|
||||
//MQTT_BROKERS.push(broker);
|
||||
|
||||
//instance.status('Ready');
|
||||
}
|
||||
|
||||
//set opts accortding to options
|
||||
/*
|
||||
instance.reconfigure = function() {
|
||||
instance.on('data', function(data) {
|
||||
|
||||
|
||||
var o = instance.options;
|
||||
opts = {
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
keepalive: 10,
|
||||
clientId: o.clientid,
|
||||
username: o.username,
|
||||
rejectUnauthorized: false,
|
||||
resubscribe: false
|
||||
};
|
||||
|
||||
//connectToTbServer();
|
||||
};
|
||||
*/
|
||||
|
||||
instance.on('data', function(data) {
|
||||
|
||||
if (brokerready)
|
||||
if (brokerready)
|
||||
{
|
||||
//do we have some data in backup file?
|
||||
//if any, process data from database
|
||||
if(saveTelemetryOnError)
|
||||
{
|
||||
//do we have some data in backup file?
|
||||
//if any, process data from database
|
||||
if(saveTelemetryOnError)
|
||||
//read telemetry data and send back to server
|
||||
if(!processingData) processDataFromDatabase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (brokerready)
|
||||
{
|
||||
let stringifiedJson = JSON.stringify(data.data);
|
||||
broker.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1});
|
||||
|
||||
//backup telemetry
|
||||
if(createTelemetryBackup)
|
||||
{
|
||||
data.data.id = UID();
|
||||
nosqlBackup.insert(data.data);
|
||||
|
||||
insertBackupNoSqlCounter++;
|
||||
if(insertBackupNoSqlCounter > 150)
|
||||
{
|
||||
//read telemetry data and send back to server
|
||||
if(!processingData) processDataFromDatabase();
|
||||
}
|
||||
|
||||
let options = {compress: true};
|
||||
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
||||
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
||||
stream.write("");
|
||||
stream.end();
|
||||
|
||||
insertBackupNoSqlCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(logger) logger.debug("Broker unavailable. Data not sent !", data.data);
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker unavailable. Data not sent !", "data": data.data });
|
||||
|
||||
if(saveTelemetryOnError)
|
||||
{
|
||||
//create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql
|
||||
makeBackupFromDbFile();
|
||||
|
||||
//write to tb
|
||||
data.data.id = UID();
|
||||
nosql.insert(data.data);
|
||||
}
|
||||
|
||||
if (brokerready)
|
||||
{
|
||||
let stringifiedJson = JSON.stringify(data.data);
|
||||
broker.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1});
|
||||
}
|
||||
});
|
||||
|
||||
//backup telemetry
|
||||
if(createTelemetryBackup)
|
||||
{
|
||||
data.data.id = UID();
|
||||
nosqlBackup.insert(data.data);
|
||||
|
||||
insertBackupNoSqlCounter++;
|
||||
if(insertBackupNoSqlCounter > 150)
|
||||
{
|
||||
let options = {compress: true};
|
||||
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
||||
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
||||
stream.write("");
|
||||
stream.end();
|
||||
|
||||
insertBackupNoSqlCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(logger) logger.debug("Broker unavailable. Data not sent !", data.data);
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker unavailable. Data not sent !", "data": data.data });
|
||||
|
||||
if(saveTelemetryOnError)
|
||||
{
|
||||
//create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql
|
||||
makeBackupFromDbFile();
|
||||
|
||||
//write to tb
|
||||
data.data.id = UID();
|
||||
nosql.insert(data.data);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
instance.close = function(done) {
|
||||
if (brokerready){
|
||||
|
|
@ -449,7 +430,6 @@ exports.install = function(instance) {
|
|||
fs.truncateSync(source, 0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const processDataFromDatabase = async () => {
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -4,12 +4,14 @@
|
|||
"version": "1.0.0",
|
||||
"main": "debug.js",
|
||||
"dependencies": {
|
||||
"easy-crc": "0.0.2",
|
||||
"mqtt": "^4.2.6",
|
||||
"serialport": "^9.0.0",
|
||||
"total.js": "^3.4.5",
|
||||
"bitwise": "^2.1.0",
|
||||
"log4js": "^6.3.0"
|
||||
"easy-crc": "0.0.2",
|
||||
"jsmodbus": "^4.0.6",
|
||||
"log4js": "^6.3.0",
|
||||
"mqtt": "^4.2.6",
|
||||
"nodemailer": "^6.9.7",
|
||||
"serialport": "^9.2.8",
|
||||
"total.js": "^3.4.13"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
{
|
||||
"config": {
|
||||
"isRunning": false,
|
||||
"debug": true,
|
||||
"timeoutTime": 4000,
|
||||
"msgWaitTime": 20000,
|
||||
"port": "/dev/ttymxc1",
|
||||
"port_options": "stty -F /dev/ttymxc1 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"
|
||||
}],
|
||||
"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
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue