Actual flowserver on Senica rvo

This commit is contained in:
rasta5man 2024-04-13 20:29:31 +02:00
parent 67c503d980
commit 86619fbcff
29 changed files with 5833 additions and 9853 deletions

3
config
View file

@ -1,4 +1,5 @@
name : Total.js Flow name : Total.js Flow
default_timezone : Europe/Bratislava
// Packages settings // Packages settings
package#flow (Object) : { url: '/' } package#flow (Object) : { url: '/' }
@ -7,5 +8,5 @@ package#flow (Object) : { url: '/' }
table.relays : line:number|tbname:string|contactor:number|profile:string table.relays : line:number|tbname:string|contactor:number|profile:string
table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
table.pins : pin:number|type:string|line:number table.pins : pin:string|type:string|line:number
table.notifications : key:string|weight:string|sk:string|en:string table.notifications : key:string|weight:string|sk:string|en:string

113
databases/modbus_config.js Normal file
View file

@ -0,0 +1,113 @@
const timeoutInterval = 300000;
const deviceConfig = [
{
device: "em340",
deviceAddress: 1,
stream: [
{
"tbAttribute": "Phase_1_voltage",
"register": 0,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_2_voltage",
"register": 2,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_3_voltage",
"register": 4,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_1_current",
"register": 12,
"size": 2,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_2_current",
"register": 14,
"size": 2,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_3_current",
"register": 16,
"size": 2,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_1_power",
"register": 18,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_2_power",
"register": 20,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_3_power",
"register": 22,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "total_power",
"register": 40,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "total_energy",
"register": 52,
"size": 2,
"multiplier": 0.1
},
{
"tbAttribute": "Phase_1_pow_factor",
"register": 46,
"size": 1,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_2_pow_factor",
"register": 47,
"size": 1,
"multiplier": 0.001
},
{
"tbAttribute": "Phase_3_pow_factor",
"register": 48,
"size": 1,
"multiplier": 0.001
},
{
"tbAttribute": "power_factor",
"register": 49,
"size": 1,
"multiplier": 0.001
}
]
},
{
device: "twilight_sensor",
deviceAddress: 2,
stream: [
{
"tbAttribute": "twilight_sensor",
"register": 60,
"size": 2,
"multiplier": 1
}
]
}
];
module.exports = { timeoutInterval, deviceConfig };

View file

@ -1,48 +1,31 @@
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
+|697|joqRYBVL30k9eQWOlZ5qwpD2KJpNEmA6gPxXzwaM|3||1|1|........... +|3522|RO8rjaBDy21qPQJzW7oD96ApK3xmNleVZg9Ed4Gw|1||1|1|...........
+|659|Ymn9oleRxJ0vw17WzAyGwdyEBk4ObdMXj2VgpNLG|3||1|1|........... +|4018|3JjOWdylwgNLzxVab7NEznkZ2vG64rq8PEB5QmDo|1||1|1|...........
+|636|M6ogKQW09bOXewAYvZyvJqyJrV1aRnPGE37p42Nx|2||1|1|........... +|4019|Z5KyJe9nEg1QNbWlX0wWRB0oDjBLdqzR83VGv624|1||1|1|...........
+|648|gaMGN4x1e9JlZz0QPRDd9Rym6dVr3OpvqKnoWBbk|2||1|1|........... +|4154|1JMYvnx2RzKEo4aWQ7DmN5AL8yZV3m9NBePXbrdj|1||1|0|...........
+|664|oGVzxNWP9lrjaQ7vKODQ7g51gqp62YZREmdw3XBM|1||1|1|........... +|3907|PjLblDgRBO6WQqnxmkJ59r0Jv3ewZN4p5a89yKdY|1||1|1|...........
+|634|NGWamnYqlP1wbgrZQxDAWm5e2X7OVAK69koR04vL|2||1|1|........... +|4148|dz4ojlpP85JMgDLZWkQOoGAaKYqQexEr62GXRV1y|1||1|1|...........
+|670|dlE1VQjYrNx9gZRmb38g1YyoLBO4qaAk2M6JPnG7|2||1|1|........... +|4153|d5xjWYMwEJon6rLlK7yBYmAqgV4DaOeNB9ZX3Gzb|1||1|1|...........
+|641|vnmG4kJxaXWNBgMQq0D7Mz5e9oZzOAlr6LdR3w2V|2||1|1|........... +|3938|gRoJEyXVx4qD9er287LP1v7wBzGldaPjLWQKm3Mv|1||1|1|...........
+|632|LpkVlmq4b3jMwJQxBZ8aM78rXAP6o97Ke0aOYEg2|2||1|1|........... +|3802|K94XLav1glVRnyQ6r01BNzkme3YJwBxM5oOzdP2j|1||1|1|...........
+|667|MzXBoWbEZjO0lrpqnRyoJ4DkmVeaNAGdL9g4QKxP|1||1|1|........... +|4015|d9x2V5LGYBzXp4mMRAOBDj7PloaqJwnQj6DgrNe3|1||1|0|...........
+|682|vnreBJ6PMqgz20pYEL82XQyG1jkWwdQxZVNAOlmK|1||1|1|........... +|3929|B5EoxeMVp4zwr8nqW0GjDpARjvD1PNamOGbLg63Z|1||1|1|...........
+|643|oZmYXEbw9lVWRv1jLxDe9bDdgAMz4PKQnNJ6eB23|1||1|1|........... +|3946|aw4eELG2DlPMdn1JW0B1DnAqQXOZRN3xB5yp8VKr|1||1|1|...........
+|642|pEonaKBOGbj9034MgJ8W3G8qXvxNWVkAPQz21R6L|1||1|1|........... +|4014|ZmRwd93QL4gaezxEbAxW5971prn2XjlPvGyqJ6BO|1||1|1|...........
+|647|BLQal6Pn9oz1KmNgek5Yqd50vd2MAbqG3OV7Rp4j|1||1|1|........... +|4155|eod9aRWLVl34Gx1Dn7VoaaA2rz6qjgmpEXwQJN5Z|1||1|1|...........
+|646|4agVJ9dPQkmp1R2X3EDJKxyrK6ZlNoM0n7qxBOev|1||1|1|........... +|4149|3a5oqJN1bgnx4Ol9dk86NBAByE6jQ8mKDWMpGrLV|1||1|1|...........
+|666|9PpgLEnvk4WMV6RmOJybMGDaeAXzo2BQNG3K17Zw|1||1|1|........... +|3642|EjgWGnXaLy9opPOz20n694086BlYM3w1deVQvbKr|1||1|1|...........
+|654|Mmp93b2nvd7OoqgBeEyEZq5kjlAV1Y4ZNXwW0zLG|1||1|1|........... +|3636|wvKJdZML6mXP4DzWBAXWNW7jxNloa5g23Ve9Y1ry|1||1|1|...........
+|637|koW06PeGrLlBp2YJQE5Ogw5RmMaXKzj3wOAZg9n7|3||1|1|........... +|3991|Nzp2OoJlqn6r1ZgvdA3GWdAabBwP5G4eE3RQmyxD|1||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|........... +|3994|PLBJzmK1r3Gynd6OW0gGdM0e5wV4vx9bDEqNgYR8|1||1|1|...........
+|645|jklN4JpQAx362o9XYZDN6wDgrWw1P7GEbdBM0vRV|1||1|1|........... +|3990|52dD6ZlV1QaOpRBmbAqKZgkKnGzWMLj4eJq38Pgo|1||1|1|...........
+|660|gj7zbKV46oQ1p2e0AJ8XqZDG3YNWaRrlOEXvBxmM|3||1|1|........... +|3967|rDbQ84xzwgdqEoPm3kbJw3k9anOZY1RXyBv2LVM6|1||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|........... +|3977|E6Kg9oDnLWyzPRMva7vrwa7Jxp4VG58qO2w1lZYe|1||1|1|...........
+|671|AvVdgzYJZaPx3oMqeED4Oj8NnmKkw716bRO90jLB|3||1|1|........... +|3757|roKgWqY95V3mXMRzyAjm8D7bLjexpJPvaGDBw826|1||1|1|...........
+|638|9xgzG4Op1BrKZPmoQkDrmj8E73ndJNMjavAwX2Re|3||1|1|........... +|3633|nJL5lPMwBx23YpqRe0rlKV7damXvWVbOrD4gNzy8|1||1|1|...........
+|639|BOjEzGRZ46bnp9wa2A8z76D0JkmW1QPNdrqevXVL|3||1|1|........... +|3744|ZmRwd93QL4gaezxEbAxW5O71prn2XjlPvGyqJ6BO|1||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|........... +|4023|eod9aRWLVl34Gx1Dn7VoaMA2rz6qjgmpEXwQJN5Z|1||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|........... +|3720|3a5oqJN1bgnx4Ol9dk86NZAByE6jQ8mKDWMpGrLV|1||1|1|...........
+|698|mYnBzbeGaAL62jowRv59M35Xq9QpZ0K7O1dg4xVl|1||1|1|........... +|3734|EjgWGnXaLy9opPOz20n69V086BlYM3w1deVQvbKr|1||1|1|...........
+|640|WjBL12pg63eX4N9P7zy0XYyEJKmlbkGwZMx0avQV|2||1|1|........... +|3741|wvKJdZML6mXP4DzWBAXWN17jxNloa5g23Ve9Y1ry|1||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|........... +|3721|Nzp2OoJlqn6r1ZgvdA3GWKAabBwP5G4eE3RQmyxD|1||0|0|...........
+|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|...........

View file

@ -25,8 +25,10 @@ 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|............... +|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}|............... +|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|||............... +|local_database_is_corrupted|CRITICAL|||...............
+|electrometer_is_not_responding|ERROR|Elektromer neodpovedá|Electrometer is not responding|............... +|electrometer_nok|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_ok|NOTICE|Elektromer znovu odpovedá|Electrometer is responding again|...............
+|electrometer_is_responding_again|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_has_been_restored|NOTICE|Napätie na fáze č. ${phase} bolo obnovené|Voltage on phase no. ${phase} has been restored|............... +|voltage_on_phase_restored|NOTICE|Napätie na fáze č. ${phase} bolo obnovené|Voltage on phase no. ${phase} has been restored|...............
+|flow_start|NOTICE|FLOW bol spustený|FLOW has been started |............... +|flow_start|NOTICE|FLOW bol spustený|FLOW has been started |...............
+|twilight_sensor_nok|ERROR|Sensor súmraku neodpovedá|Twilight sensor is not responding|...............
+|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|...............

View file

@ -1,14 +1,14 @@
pin:number|type:string|line:number pin:string|type:string|line:number
*|1|state_of_main_switch|0|........... *|input1_01|door_condition|0|...........
*|2|rotary_switch_state|0|........... *|input1_02|rotary_switch_state|0|...........
*|3|rotary_switch_state|0|........... *|input1_03|rotary_switch_state|0|...........
*|4|power_supply|0|........... *|intut1_04|power_supply|0|...........
*|5|battery|0|........... *|input1_05|state_of_main_switch|0|...........
*|6|door_condition|0|........... *|input1_06|state_of_breaker|1|...........
*|8|state_of_breaker|1|........... *|input1_07|state_of_breaker|2|...........
*|9|state_of_breaker|2|........... *|input1_08|state_of_breaker|3|...........
*|10|state_of_breaker|3|........... *|relay1_02|state_of_contactor|1|...........
*|11|state_of_contactor|1|........... *|relay1_03|state_of_contactor|2|...........
*|12|state_of_contactor|2|........... *|relay1_04|state_of_contactor|3|...........
*|13|state_of_contactor|3|........... *|28F46E9D0E00008B|temperature|0|...........
*|16|twilight_sensor|0|........... *|twilight_sensor|twilight_sensor|0|...........

View file

@ -1,5 +1,5 @@
line:number|tbname:string|contactor:number|profile:string line:number|tbname:string|contactor:number|profile:string
+|0|KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV|1||........... +|0|6lQGaY9RDywdVzObj0PadOkPg4NBn3exEK51LWZq|1||...........
+|3|vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V|0||.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... +|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}|...........
+|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|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}|...........
+|2|dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7|0||................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ +|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}|...........

View file

@ -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 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|...........................................

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

View file

@ -1,9 +1,9 @@
exports.id = 'di_do_controller'; exports.id = 'dido_controller';
exports.title = 'DI_DO_Controller'; exports.title = 'DIDO_Controller';
exports.version = '1.0.0'; exports.version = '2.0.0';
exports.group = 'Worksys'; exports.group = 'Worksys';
exports.color = '#2134B0'; exports.color = '#2134B0';
exports.input = 1; exports.input = 3;
exports.output = ["red", "white", "yellow"]; exports.output = ["red", "white", "yellow"];
exports.click = false; exports.click = false;
exports.author = 'Daniel Segeš'; exports.author = 'Daniel Segeš';
@ -60,7 +60,7 @@ state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda
//globals //globals
//FIRMWARE version //FIRMWARE version
FLOW.OMS_edge_fw_version = "2022-05-12";//rok-mesiac-den FLOW.OMS_edge_fw_version = "2023-10-18";//rok-mesiac-den
FLOW.OMS_edgeName = ""; FLOW.OMS_edgeName = "";
FLOW.OMS_maintenance_mode = false; FLOW.OMS_maintenance_mode = false;
@ -142,7 +142,7 @@ exports.install = function(instance) {
let twilight_sensor_interval = 5;//minutes let twilight_sensor_interval = 5;//minutes
let twilight_sensor = []; let twilight_sensor = [];
const twilight_sensor_array = []; const twilight_sensor_array = [];
let twighlightError = false; let twilightError = false;
let edgeName = ""; let edgeName = "";
@ -178,7 +178,7 @@ exports.install = function(instance) {
deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič deviceStatuses["state_of_main_switch"] = "Off";//Hlavný istič
deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód deviceStatuses["rotary_switch_state"] = "Off";//Prevádzkový mód
deviceStatuses["door_condition"] = "closed";//Dverový kontakt deviceStatuses["door_condition"] = "closed";//Dverový kontakt
deviceStatuses["rvo"] = {status: "OK"};//elektromer rvo deviceStatuses["em"] = "OK";//elektromer rvo
deviceStatuses["temperature"] = "OK";//templomer deviceStatuses["temperature"] = "OK";//templomer
deviceStatuses["battery"] = "OK";//Batéria deviceStatuses["battery"] = "OK";//Batéria
deviceStatuses["power_supply"] = "OK";//Zdroj deviceStatuses["power_supply"] = "OK";//Zdroj
@ -187,6 +187,7 @@ exports.install = function(instance) {
deviceStatuses["state_of_breaker"] = {};//"Off";//Istič deviceStatuses["state_of_breaker"] = {};//"Off";//Istič
deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač deviceStatuses["state_of_contactor"] = {};//"Off";//Stykač
deviceStatuses["twilight_sensor"] = "OK"; //lux sensor
/* /*
dbRelays.on('change', function(doc, old) { dbRelays.on('change', function(doc, old) {
@ -289,8 +290,6 @@ exports.install = function(instance) {
edgeName = relaysData[0].tbname; edgeName = relaysData[0].tbname;
FLOW.OMS_edgeName = edgeName; FLOW.OMS_edgeName = edgeName;
logger.debug("RVO tbname:", edgeName);
dataToTb = { dataToTb = {
[edgeName]: [ [edgeName]: [
{ {
@ -304,13 +303,27 @@ exports.install = function(instance) {
let time = 3*1000; let time = 3*1000;
setTimeout(function(){ setTimeout(function(){
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "buildTasks"}); instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "buildTasks"});
sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", instanceSendTo.tb, instance ); sendNotification("rsPort.open()", edgeName, "flow_start", {}, "", instanceSendTo.tb, instance );
monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version); monitor.info("-->FLOW bol spustený", edgeName, FLOW.OMS_edge_fw_version);
}, time); }, time);
} }
// we ensure, all tasks will be rebuild every day at 11. To set correct switch off and on times
let sendRebuildTasksAt11 = null;
const checkIf11Oclock = () =>
{
const d = new Date();
const h = d.getHours();
if(h === 11)
{
instance.send(instanceSendTo.cmd_manager, {sender:"dido_controller", cmd:"buildTasks"});
}
}
sendRebuildTasksAt11 = setInterval(checkIf11Oclock, 3600000);
function handleRsPort() function handleRsPort()
@ -402,9 +415,11 @@ exports.install = function(instance) {
function handleWebSocket() { function handleWebSocket() {
console.log("handleWebSocket function called"); //to keep websocket opened, we send request every 150 seconds
ws = new WebSocket('ws:/10.0.0.38:1234/ws') let startRequests = null;
console.log("handleWebSocket function called");
ws = new WebSocket('ws:/0.0.0.0:1234/ws');
ws.onopen = function open() { ws.onopen = function open() {
@ -416,7 +431,13 @@ exports.install = function(instance) {
initialSetting(); initialSetting();
ws.send(JSON.stringify({"cmd":"all"})); ws.send(JSON.stringify({"cmd":"all"}));
// startRequests(); // we request dev info about neuron device from evok to keep websocket connection alive
// for some reason this request returns no data, but connection keeps alive
// https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket
startRequests = setInterval(() => {
// console.log(" *** data from evok requested");
ws.send(JSON.stringify({"cmd":"filter", "dev": ["neuron"]}));
}, 150000)
}; };
// SAMPLE DATA FROM WEBSOCKET // SAMPLE DATA FROM WEBSOCKET
@ -442,11 +463,9 @@ exports.install = function(instance) {
// dev: 'input', // dev: 'input',
// mode: 'Simple' // mode: 'Simple'
// }, // },
ws.onmessage = function(data) { ws.onmessage = function(data) {
data = JSON.parse(data.data); data = JSON.parse(data.data);
// console.log(data)
// data comes in array except of "temperature" ==> it comes as an object // data comes in array except of "temperature" ==> it comes as an object
if(!Array.isArray(data)) if(!Array.isArray(data))
@ -485,32 +504,36 @@ exports.install = function(instance) {
ws.on('error', (err) => { ws.on('error', (err) => {
instance.send(instanceSendTo.debug, err.message); instance.send(instanceSendTo.debug, err.message);
clearInterval(startRequests);
startRequests = null;
}) })
ws.onclose = function(){ ws.onclose = function(){
// connection closed, discard old websocket and create a new one in 5s // connection closed, discard old websocket and create a new one in 5s
// stopRequests(); // stopRequests();
clearInterval(startRequests);
ws = null; ws = null;
console.log("ws is null now, reconnecting in 5 seconds"); console.log("ws is null now, reconnecting...");
setTimeout(handleWebSocket, 5000); setTimeout(handleWebSocket, 1000);
} }
} }
//! do we need requests every minute ??? // ! do we need requests every minute ???
// const startRequests = () => { // const startRequests = () => {
// console.log("startRequest function called"); // console.log("startRequest function called");
// start = setInterval(() => { // start = setInterval(() => {
// // console.log("data from evok requested"); // // console.log("data from evok requested");
// ws.send(JSON.stringify({"cmd":"all"})); // ws.send(JSON.stringify({"cmd":"filter", "devices": "neuron"}));
// // ws.send(JSON.stringify({"cmd":"filter", "devices":["input", "relay"]})); // // ws.send(JSON.stringify({"cmd":"filter", "devices":["input", "relay"]}));
// }, 60000) // }, 150000)
// } // }
instance.on("close", () => { instance.on("close", () => {
if(rsPort) rsPort.close(); if(rsPort) rsPort.close();
if(ws) ws.close(); if(ws) ws.close();
clearInterval(sendRebuildTasksAt11);
}) })
loadAllDb(); loadAllDb();
@ -555,7 +578,7 @@ exports.install = function(instance) {
} }
else if(ws) else if(ws)
{ {
let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_08", "value": 1}; let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": 1};
ws.send(JSON.stringify(cmd)); ws.send(JSON.stringify(cmd));
logger.debug("sirena zapnuta"); logger.debug("sirena zapnuta");
} }
@ -579,7 +602,7 @@ exports.install = function(instance) {
} }
else if(ws) else if(ws)
{ {
let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_08", "value": 0}; let cmd = {"cmd": "set", "dev": "relay", "circuit": "1_01", "value": 0};
ws.send(JSON.stringify(cmd)); ws.send(JSON.stringify(cmd));
logger.debug("sirena vypnuta"); logger.debug("sirena vypnuta");
} }
@ -709,7 +732,7 @@ exports.install = function(instance) {
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1}; let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 1};
ws.send(JSON.stringify(cmd)); ws.send(JSON.stringify(cmd));
switchLogic(pin, 1) //switchLogic(pin, 1)
} }
} }
@ -724,7 +747,7 @@ exports.install = function(instance) {
let values = {}; let values = {};
values["statecode"] = calculateStateCode(); values["statecode"] = calculateStateCode();
values["power_mode"] = "Automatic"; values["power_mode"] = "automatic";
let tbname = relaysData[line].tbname; let tbname = relaysData[line].tbname;
sendTelemetry(values, tbname); sendTelemetry(values, tbname);
@ -783,71 +806,92 @@ exports.install = function(instance) {
else if(ws) else if(ws)
{ {
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
monitor.info("turnOffLine pin (relay)", pin); //monitor.info("turnOffLine pin (relay)", pin);
let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0}; let cmd = {"cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": 0};
ws.send(JSON.stringify(cmd)); ws.send(JSON.stringify(cmd));
switchLogic(pin, 0) //switchLogic(pin, 0)
} }
} }
// we expect array as flowdata.data //data from modbus_reader or temperature sensor or twilight sensor or other modbus device
instance.on("data", (flowdata) => { instance.on("0", flowdata => {
console.log(flowdata.data); if(!flowdata.data instanceof Object) return;
if(flowdata.data instanceof Object) // console.log('***********************', flowdata.data)
instance.send(instanceSendTo.debug, flowdata.data);
// we handle nok status from modbus_reader component and thermometer
if(flowdata.data?.status)
{ {
const status = flowdata.data.status;
if(flowdata.data.hasOwnProperty("sender")) if(status == "NOK-twilight_sensor")
{ {
//console.log("sender", flowdata.data); deviceStatuses["twilight_sensor"] = "NOK";
}
if(flowdata.data.sender == "gettemperature") else if(status == "NOK-em340" || status == "NOK-em111")
{ {
deviceStatuses["temperature"] = flowdata.data.status; deviceStatuses["em"] = "NOK";
} }
else if(flowdata.data.sender == "modbus_citysys") else if(status == "NOK-thermometer")
{ {
//elektromer rvo deviceStatuses["temperature"] = "NOK";
if(flowdata.data.tbdata.hasOwnProperty(edgeName))
{
//rvo
deviceStatuses["rvo"] = {status: flowdata.data.tbdata[edgeName][0]["values"]["status"], tbdata: flowdata.data.tbdata};
}
else{
//posli do tb - to je vyriesene na urovni modbus_citysys
//instance.send(instanceSendTo.tb, flowdata.data.tbdata);
}
}
instance.send(instanceSendTo.debug, flowdata.data );
return;
} }
let obj = flowdata.data;
let line = obj.line;
let force = obj.force;
let info = obj.info;
if(obj.command == "turnOn") turnOnLine(line, undefined, force, info);
else if(obj.command == "turnOff") turnOffLine(line, undefined, force, info);
else if(obj.command == "turnOnAlarm") turnOnAlarm();
else if(obj.command == "turnOffAlarm") turnOffAlarm();
return; return;
} }
const values = flowdata.data.values;
if(values.hasOwnProperty("twilight_sensor"))
{
instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: values["twilight_sensor"]});
deviceStatuses["twilight_sensor"] = "OK"
}
else if(values.hasOwnProperty("temperature"))
{
deviceStatuses["temperature"] = "OK";
}
// EM
else if(values.hasOwnProperty("total_power") || values.hasOwnProperty("total_energy") || values.hasOwnProperty("power_factor") || values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_1_current"))
{
deviceStatuses["em"] = "OK";
}
const updateStatus = checkFinalRVOStatus();
if(updateStatus) values.status = "OK";
sendTelemetry(values, FLOW.OMS_rvo_tbname);
})
// we expect array as flowdata.data
instance.on("1", flowdata => {
console.log(flowdata.data);
if(!flowdata.data instanceof Object) return;
let obj = flowdata.data;
let line = obj.line;
let force = obj.force;
let info = obj.info;
if(obj.command == "turnOn") turnOnLine(line, undefined, force, info);
else if(obj.command == "turnOff") turnOffLine(line, undefined, force, info);
else if(obj.command == "turnOnAlarm") turnOnAlarm();
else if(obj.command == "turnOffAlarm") turnOffAlarm();
//! ake data prichadzaju z cmd_manager.js ??? //! ake data prichadzaju z cmd_manager.js ???
//TODO transform to websocket //TODO transform to websocket
if (Array.isArray(flowdata.data)){ if (Array.isArray(obj)){
rsPort.write(Buffer.from(flowdata.data), function(err) { rsPort.write(Buffer.from(obj), function(err) {
switchLogic(flowdata.data); switchLogic(obj);
instance.send(instanceSendTo.debug, {"WRITE":flowdata.data} ); instance.send(instanceSendTo.debug, {"WRITE":obj} );
}); });
} }
}) })
@ -859,12 +903,17 @@ exports.install = function(instance) {
let bytes = []; let bytes = [];
let bits = []; let bits = [];
//Hlavný istič - state_of_main_switch //Hlavný istič - state_of_main_switch
if(deviceStatuses["state_of_main_switch"] == "On") bits.push(0); if(deviceStatuses["state_of_main_switch"] == "On")
else if(deviceStatuses["state_of_main_switch"] == "Off") bits.push(1); {
bits.push(0);
}
else if(deviceStatuses["state_of_main_switch"] == "Off")
{
bits.push(1);
}
//Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY
if(!FLOW.OMS_maintenance_mode) if(!FLOW.OMS_maintenance_mode)
{ {
if(deviceStatuses["rotary_switch_state"] == "Manual") if(deviceStatuses["rotary_switch_state"] == "Manual")
@ -895,48 +944,95 @@ exports.install = function(instance) {
{ {
bits.push(0); bits.push(0);
} }
else bits.push(1); else
{
bits.push(1);
}
//EM //EM
if(deviceStatuses["rvo"].status == "NOK") bits.push(1); if(deviceStatuses["em"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//Teplomer //Teplomer
if(deviceStatuses["temperature"] == "NOK") bits.push(1); if(deviceStatuses["temperature"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//Batéria //Batéria
if(deviceStatuses["battery"] == "NOK") bits.push(1); if(deviceStatuses["battery"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//Zdroj //Zdroj
if(deviceStatuses["power_supply"] == "NOK") bits.push(1); if(deviceStatuses["power_supply"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//MN //MN
if(deviceStatuses["master_node"] == "NOK") bits.push(1); if(deviceStatuses["master_node"] == "NOK")
else bits.push(0); {
bits.push(1);
}
else
{
bits.push(0);
}
//výpadok napätia na fáze //výpadok napätia na fáze
if(deviceStatuses["no_voltage"] == "NOK") bits.push(1); if(deviceStatuses["no_voltage"] == "NOK")
else bits.push(0); {
bits.push(1);
bits.push(0); }
bits.push(0); else
bits.push(0); {
bits.push(0); bits.push(0);
bits.push(0); }
bits.push(0);
//console.log("calculateStateCode - deviceStatuses", deviceStatuses); if(deviceStatuses["twilight_sensor"] == "NOK")
//console.log("calculateStateCode", bits); {
bits.push(1);
}
else
{
bits.push(0);
}
// doplnime do 16 bitov (2 byty)
for(let i = bits.length; i < 16; i++)
{
bits.push(0);
}
// console.log("calculateStateCode - deviceStatuses", deviceStatuses);
// console.log("calculateStateCode", bits);
let byte0 = bitwise.byte.write(bits.slice(0,8).reverse()); let byte0 = bitwise.byte.write(bits.slice(0,8).reverse());
let byte1 = bitwise.byte.write(bits.slice(8).reverse()); let byte1 = bitwise.byte.write(bits.slice(8).reverse());
let byte = bytesToInt([byte1, byte0]); let byte = bytesToInt([byte1, byte0]);
//console.log("calculateStateCode", byte); //console.log("calculateStateCode -------------------", byte);
return byte; return byte;
} }
@ -951,10 +1047,18 @@ exports.install = function(instance) {
let status = "OK"; let status = "OK";
if(deviceStatuses["rvo"].status == "NOK") if(deviceStatuses["em"] == "NOK")
{ {
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: rvo status is NOK"); let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: EM status is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: rvo status is NOK", deviceStatuses["rvo"].tbdata); if(writeToFile) errLogger.error("checkFinalRVOStatus: EM status is NOK");
status = "NOK";
}
if(deviceStatuses["twilight_sensor"] == "NOK")
{
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: twilight_sensor is NOK");
if(writeToFile) errLogger.error("checkFinalRVOStatus: twilight sensor is NOK");
status = "NOK"; status = "NOK";
} }
@ -971,10 +1075,15 @@ exports.install = function(instance) {
if(status == "OK") if(status == "OK")
{ {
for (const pinIndex of [1, 4, 6]) { let pinIndexes = [1, 4, 6];
if(controller_type == 'unipi') pinIndexes = ['input1_01', 'input1_04', 'input1_05'];
//console.log('-------- previousValues', previousValues);
for (const pinIndex of pinIndexes) {
if (previousValues[pinIndex] === 0) { if (previousValues[pinIndex] === 0) {
if (pinIndex === 6 && FLOW.OMS_maintenance_mode) continue; if ((pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05') && FLOW.OMS_maintenance_mode) continue;
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0"); let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: value is 0");
if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type); if(writeToFile) errLogger.error("checkFinalRVOStatus: value is 0", pinsData[pinIndex].type);
@ -986,7 +1095,7 @@ exports.install = function(instance) {
} }
} }
// battery status. If value is 1 - battery is not ok // battery status. If value is 1 - battery is NOK
if (previousValues[5] === 1) if (previousValues[5] === 1)
{ {
let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery"); let writeToFile = errorHandler.processMessage("checkFinalRVOStatus: NOK status generated by battery");
@ -995,18 +1104,6 @@ exports.install = function(instance) {
status = "NOK"; status = "NOK";
} }
//ak mame telemetriu z elektromeru, posleme
if(deviceStatuses["rvo"].tbdata != undefined)
{
//deviceStatuses["rvo"] = {status: flowdata.data.tbdata[edgeName][0]["values"]["status"], tbdata: flowdata.data.tbdata};
deviceStatuses["rvo"].tbdata[edgeName][0]["values"]["status"] = status;
instance.send(instanceSendTo.tb, deviceStatuses["rvo"].tbdata);
delete deviceStatuses["rvo"].tbdata;
}
//console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding); //console.log("FLOW.OMS_masterNodeIsResponding", FLOW.OMS_masterNodeIsResponding);
if(!FLOW.OMS_masterNodeIsResponding) if(!FLOW.OMS_masterNodeIsResponding)
@ -1033,17 +1130,7 @@ exports.install = function(instance) {
if(status == "NOK") if(status == "NOK")
{ {
sendTelemetry({status: "NOK"}, FLOW.OMS_rvo_tbname);
const dataToTb = {
[edgeName]: [
{
"ts": Date.now(),
"values": {status: "NOK"}
}
]
}
instance.send(instanceSendTo.tb, dataToTb);
return false; return false;
} }
@ -1052,21 +1139,22 @@ exports.install = function(instance) {
} }
// we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]]
// we pass array to function in case of rsPort [[55,3,0,1]] // we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1]
// we pass two values in case of websocket [3,1]
const switchLogic = (...args) => { const switchLogic = (...args) => {
let values = {status: "OK"}; let values = {status: "OK"};
let dataToTb, pinIndex, newPinValue, twighlight; let dataToTb, pinIndex, newPinValue, twilight;
//data from rsPort
if(args.length == 1) if(args.length == 1)
{ {
pinIndex = args[0][1] + 1; pinIndex = args[0][1] + 1;
if (pinIndex === 17) pinIndex--; if (pinIndex === 17) pinIndex--;
newPinValue = args[0][3]; newPinValue = args[0][3];
twighlight = args[0][2]; twilight = args[0][2];
} }
//data from websocket
else else
{ {
pinIndex = args[0]; pinIndex = args[0];
@ -1090,7 +1178,9 @@ exports.install = function(instance) {
if(newPinValue === 0) value = "Off"; if(newPinValue === 0) value = "Off";
//Hlavný istič //Hlavný istič
if(type === "state_of_main_switch") //! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri
//! takze ked pride z evoku signal pre 'input1_05', handlujeme ho ako 'door_condition'
if(type === "!!!state_of_main_switch")
{ {
if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
{ {
@ -1116,16 +1206,26 @@ exports.install = function(instance) {
{ {
pin2 = newPinValue; pin2 = newPinValue;
pin3 = previousValues[3] || previousValues["input1_03"]; pin3 = previousValues[3] || previousValues["input1_03"];
if (pin3 == undefined) pin3 = 0;
if(pin3 == undefined)
{
previousValues[pinIndex] = newPinValue;
return;
}
} }
else if(pinIndex == 3 || pinIndex == "input1_03") else if(pinIndex == 3 || pinIndex == "input1_03")
{ {
pin3 = newPinValue; pin3 = newPinValue;
pin2 = previousValues[2] || previousValues["input1_02"]; pin2 = previousValues[2] || previousValues["input1_02"];
if (pin2 == undefined) pin2 = 0;
if(pin2 == undefined)
{
previousValues[pinIndex] = newPinValue;
return;
}
} }
value = "Off"; //console.log('***********************', pin2, pin3)
if (pin2 == 1 && pin3 == 0) value = "Manual"; if (pin2 == 1 && pin3 == 0) value = "Manual";
if (pin2 == 0 && pin3 == 0) value = "Off"; if (pin2 == 0 && pin3 == 0) value = "Off";
if (pin2 == 0 && pin3 == 1) value = "Automatic"; if (pin2 == 0 && pin3 == 1) value = "Automatic";
@ -1136,7 +1236,7 @@ exports.install = function(instance) {
//ak je spracovany, a automatic - tak ho zapnem //ak je spracovany, a automatic - tak ho zapnem
//ak nie je spracovany, iba profil zapisem //ak nie je spracovany, iba profil zapisem
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "rotary_switch_state", value: value}); instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "rotary_switch_state", value: value});
//console.log("rotary_switch_state pin", pin2, pin3, value); //console.log("rotary_switch_state pin", pin2, pin3, value);
} }
@ -1179,7 +1279,9 @@ exports.install = function(instance) {
} }
} }
//Dverový kontakt - pin 6 //Dverový kontakt - pin 6
else if(type == "door_condition") //! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch"
//! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition'
else if(type == "door_condition" || type === "state_of_main_switch")
{ {
newPinValue === 0 ? value = "open" : value = "closed"; newPinValue === 0 ? value = "open" : value = "closed";
@ -1202,8 +1304,9 @@ exports.install = function(instance) {
//console.log(door_has_been_open_without_permision_alarm_is_on); //console.log(door_has_been_open_without_permision_alarm_is_on);
//zapneme sirenu // zapneme sirenu
turnOnAlarm(); // ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition")
if(type === "door_condition") turnOnAlarm();
} }
if (value === "closed") if (value === "closed")
@ -1217,73 +1320,84 @@ exports.install = function(instance) {
deviceStatuses["door_condition"] = value; deviceStatuses["door_condition"] = value;
} }
//lux sensor
else if(type == "twilight_sensor") else if(type == "twilight_sensor")
{ {
//lux sensor //! TODO - to show nok status, if lux value is not changing more then 10 times. From unipi for example comes value from 0-1000.
value = parseFloat(newPinValue + (256*twighlight)); //Daylight is far more than 1000. So most of the day, when it is sunshine comes just value 1000. But lux sensor is not NOK.
//This is not the case in LM. If value from LM is the same 10x, there is 99% possibility, that sensor is NOK.
let now = new Date();
//new Date(dusk.getTime()
let obj = {timestamp: now.getTime(), value: value};
//test
//twilight_sensor_interval = 1;
twilight_sensor.push(obj);
twilight_sensor_array.push(value);
//check if we receive just 1 constant value from lux sensor ==> error
if(twilight_sensor_array.length > 10) {
let set = new Set(twilight_sensor_array);
if(set.size === 1 && !twighlightError)
{
twighlightError = true;
values["status"] = "NOK";
let value = twilight_sensor_array.shift();
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, instanceSendTo.tb, instance );
newPinValue = 0;
}
else if (set.size !== 1 && twighlightError)
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", instanceSendTo.tb, instance );
twighlightError = false;
twilight_sensor_array.shift();
newPinValue = value;
}
else if (set.size === 1 && twighlightError)
{
values["status"] = "NOK";
twilight_sensor_array.shift();
newPinValue = 0;
}
}
let diff = twilight_sensor[ twilight_sensor.length - 1 ].timestamp - twilight_sensor[0].timestamp;
if(diff >= twilight_sensor_interval * 60 * 1000)
{
const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length;
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "lux_sensor", value: average});
twilight_sensor = [];
//console.log("lux_sensor send", average);
}
//else console.log("lux_sensor", value, diff);
values["status"] = "OK"; values["status"] = "OK";
value = newPinValue;
// if(controller_type === 'lm')
{
value = parseFloat(newPinValue + (256*twilight));
let now = new Date();
//new Date(dusk.getTime()
let obj = {timestamp: now.getTime(), value: value};
//test
//twilight_sensor_interval = 1;
twilight_sensor.push(obj);
//twilight_sensor_array.push(value);
//check if we receive just 1 constant value from lux sensor ==> error
if(twilight_sensor_array.length > 10) {
let set = new Set(twilight_sensor_array);
if(set.size === 1 && !twilightError)
{
twilightError = true;
values["status"] = "NOK";
let value = twilight_sensor_array.shift();
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, instanceSendTo.tb, instance );
newPinValue = 0;
}
else if (set.size !== 1 && twilightError)
{
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", instanceSendTo.tb, instance );
twilightError = false;
twilight_sensor_array.shift();
newPinValue = value;
}
else if (set.size === 1 && twilightError)
{
values["status"] = "NOK";
twilight_sensor_array.shift();
newPinValue = 0;
}
}
let diff = twilight_sensor[ twilight_sensor.length - 1 ].timestamp - twilight_sensor[0].timestamp;
if(diff >= twilight_sensor_interval * 60 * 1000)
{
const average = twilight_sensor.reduce((acc, c) => acc + c.value, 0) / twilight_sensor.length;
instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "lux_sensor", value: average});
twilight_sensor = [];
//console.log("lux_sensor send", average);
}
//else console.log("lux_sensor", value, diff);
}
} }
else if(type == "state_of_contactor") else if(type == "state_of_contactor")
{ {
//sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", instanceSendTo.tb, instance ); //sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", instanceSendTo.tb, instance );
sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", instanceSendTo.tb, instance );
deviceStatuses["state_of_contactor"][line] = value; if(!(deviceStatuses["state_of_contactor"][line] == value))
{
sendNotification("switchLogic", edgeName, "state_of_contactor_for_line", {line: line, value: value}, "", instanceSendTo.tb, instance );
}
else
{
deviceStatuses["state_of_contactor"][line] = value;
}
//true, false //true, false
if(value === "On") value = true; if(value === "On") value = true;
else if(value === "Off") value = false; else if(value === "Off") value = false;
@ -1319,7 +1433,7 @@ exports.install = function(instance) {
//a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor //a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor
setTimeout(function(){ setTimeout(function(){
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged}); instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "reload_relays", line: line, time: time, value: value, dataChanged: dataChanged});
}, time); }, time);
reportLineStatus(line); reportLineStatus(line);
@ -1344,7 +1458,24 @@ exports.install = function(instance) {
if(valueChanged) if(valueChanged)
{ {
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "state_of_breaker", value: value, line: line}); instance.send(instanceSendTo.cmd_manager, {sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line});
//mame 3 istice. ked je viac ako 3 linie, dalsie sa zapajaju paralelne. to znamena na istici 1 je linia 1 a 4, na istici 2 je linia 2 a 5, na istici 3 je linia 3 a 6 (key je string ("4"))
//vyreportujeme a ohandlujeme liniu na tom istom istici ako paralelna linia
const lineOnSameBraker = line + 3 + "";
if(relaysData.hasOwnProperty(lineOnSameBraker)) {
instance.send(instanceSendTo.cmd_manager, {sender: "di_do_controller", cmd: "state_of_breaker", value: value, line: line + 3});
deviceStatuses["state_of_breaker"][line + 3] = value;
reportLineStatus(line + 3);
values[type] = value;
tbname = relaysData[lineOnSameBraker].tbname;
sendTelemetry(values, tbname);
delete values[type];
}
} }
if(value == "Off") values["status"] = "NOK"; if(value == "Off") values["status"] = "NOK";
@ -1352,25 +1483,27 @@ exports.install = function(instance) {
deviceStatuses["state_of_breaker"][line] = value; deviceStatuses["state_of_breaker"][line] = value;
reportLineStatus(line); reportLineStatus(line);
} }
values[type] = value; values[type] = value;
let result = checkFinalRVOStatus();
if(!result && line == 0)
{
values["status"] = "NOK";
}
//--
//if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode(); //if(FLOW.OMS_rvo_tbname == tbname) values["statecode"] = calculateStateCode();
if(pinsData.hasOwnProperty(pinIndex)) if(pinsData.hasOwnProperty(pinIndex))
{ {
let valueChanged = false; let valueChanged = false;
if(newPinValue != previousValues[pinIndex]) valueChanged = true; if(newPinValue != previousValues[pinIndex])
{
valueChanged = true;
//pin was changed
previousValues[pinIndex] = newPinValue;
}
let result = checkFinalRVOStatus();
if(!result && line == 0)
{
values["status"] = "NOK";
}
if(type == "state_of_contactor") valueChanged = true; if(type == "state_of_contactor") valueChanged = true;
if(type == "rotary_switch_state") valueChanged = true; if(type == "rotary_switch_state") valueChanged = true;
@ -1381,15 +1514,14 @@ exports.install = function(instance) {
if(FLOW.OMS_rvo_tbname == "") if(FLOW.OMS_rvo_tbname == "")
{ {
console.log("FLOW.OMS_rvo_tbname is EMPTY"); console.log("FLOW.OMS_rvo_tbname is EMPTY");
} }
if(FLOW.OMS_rvo_tbname == tbname) if(FLOW.OMS_rvo_tbname == tbname)
{ {
values["statecode"] = calculateStateCode(); values["statecode"] = calculateStateCode();
//console.log(type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname); //console.log('**********************', type, values, valueChanged, FLOW.OMS_rvo_tbname, tbname);
} }
if(valueChanged) if(valueChanged)
{ {
@ -1412,11 +1544,8 @@ exports.install = function(instance) {
logger.debug("no pinIndex", pinsData[pinIndex], pinsData); logger.debug("no pinIndex", pinsData[pinIndex], pinsData);
} }
//pin was changed }
previousValues[pinIndex] = newPinValue;
}
function sendTelemetry(values, tbname) function sendTelemetry(values, tbname)
{ {
@ -1431,8 +1560,6 @@ exports.install = function(instance) {
instance.send(instanceSendTo.tb, dataToTb); instance.send(instanceSendTo.tb, dataToTb);
} }
} }
@ -1695,22 +1822,18 @@ exports.install = function(instance) {
//! pins.table --> from UNIPI //! pins.table --> from UNIPI
// pin:string|type:string|line:number // pin:string|type:string|line:number
// *|al_mswitch|state_of_main_switch|0|........... // *|input1_01|state_of_main_switch|0|...........
// *|al_rswitch1|rotary_switch_state|0|........... // *|input1_02|rotary_switch_state|0|...........
// *|al_rswitch2|rotary_switch_state|0|........... // *|input1_03|rotary_switch_state|0|...........
// *|al_power|power_supply|0|........... // *|intut1_04|power_supply|0|...........
// *|al_battery|battery|0|........... // *|input1_05|door_condition|0|...........
// *|al_door|door_condition|0|........... // *|input1_06|state_of_breaker|1|...........
// *|al_breaker1|state_of_breaker|1|........... // *|input1_07|state_of_breaker|2|...........
// *|al_breaker2|state_of_breaker|2|........... // *|input1_08|state_of_breaker|3|...........
// *|al_breaker3|state_of_breaker|3|........... // *|relay1_02|state_of_contactor|1|...........
// *|al_breaker4|state_of_breaker|4|........... // *|relay1_03|state_of_contactor|2|...........
// *|al_relay_1|state_of_contactor|1|........... // *|relay1_04|state_of_contactor|3|...........
// *|al_relay_2|state_of_contactor|2|........... // *|287D8776E0013CE9|temperature|0|...........
// *|al_relay_3|state_of_contactor|3|...........
// *|al_relay_4|state_of_contactor|4|...........
// *|16|twilight_sensor|0|...........
// *|28744F7791180257|temperature|0|...........
//! pins_data --> from UNIPI //! pins_data --> from UNIPI
@ -1762,3 +1885,28 @@ exports.install = function(instance) {
// } // }
// } // }
// {
// "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV": [
// {
// "ts": 1700409326353,
// "values": {
// "_event": {
// "type": "notice",
// "status": "new",
// "source": {
// "func": "rsPort.open()",
// "component": "1700343402190",
// "component_name": "DIDO_Controller",
// "edge": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV"
// },
// "message": "al_shariff_10.0.0.38: FLOW has been started ",
// "message_data": ""
// }
// }
// }
// ]
// }

View file

@ -1,18 +1,18 @@
exports.id = 'gettemperature'; exports.id = 'thermometer';
exports.title = 'Thermometer'; exports.title = 'Thermometer';
exports.group = 'Worksys'; exports.group = 'Worksys';
exports.color = '#5CB36D'; exports.color = '#5CB36D';
exports.version = '1.0.2'; exports.version = '1.0.3';
exports.output = ["red", "white", "blue"]; exports.output = ["red", "white", "blue"];
exports.author = 'Rastislav Kovac'; exports.author = 'Rastislav Kovac';
exports.icon = 'thermometer-three-quarters'; exports.icon = 'thermometer-three-quarters';
exports.readme = `# Getting temperature values from RVO`; exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`;
const instanceSendTo = { const instanceSendTo = {
debug: 0, debug: 0,
tb: 1, tb: 1,
di_do_controller: 2 dido_controller: 2
} }
//read temperature - frequency //read temperature - frequency
@ -43,7 +43,7 @@ const monitor = log4js.getLogger("monitorLogs");
//monitor.info('info'); //monitor.info('info');
//errLogger.error("some error"); //errLogger.error("some error");
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js'); const { promisifyBuilder } = require('./helper/db_helper');
const dbSettings = TABLE("settings"); const dbSettings = TABLE("settings");
let temperatureAddress = ""; let temperatureAddress = "";
@ -60,7 +60,7 @@ loadSettings();
exports.install = function(instance) { exports.install = function(instance) {
const { exec } = require('child_process'); const { exec } = require('child_process');
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter.js'); const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
let startRead; let startRead;
let dataToTb; let dataToTb;
@ -76,9 +76,9 @@ exports.install = function(instance) {
}) })
const start = function(){ const start = function() {
try{ try {
if(FLOW.OMS_controller_type === "unipi") if(FLOW.OMS_controller_type === "unipi")
{ {
@ -130,8 +130,8 @@ exports.install = function(instance) {
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready); monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
instance.send(instanceSendTo.tb, dataToTb); // instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: status}); instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
} }
else parseData(stdout); else parseData(stdout);
} }
@ -162,10 +162,9 @@ exports.install = function(instance) {
logger.debug("gettemperature", data); logger.debug("gettemperature", data);
if (!isNaN(data)){ if(!isNaN(data)) {
if(counter > 290)
//if ( counter > 290 )
{ {
instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break"); instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break");
@ -174,21 +173,22 @@ exports.install = function(instance) {
} }
logger.debug("gettemperature", data); logger.debug("gettemperature", data);
const values = {
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
dataToTb = { dataToTb = {
[edgeName]: [ [edgeName]: [
{ {
"ts": Date.now(), "ts": Date.now(),
"values": { "values":values
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
} }
] ]
} }
instance.send(instanceSendTo.tb, dataToTb); instance.send(instanceSendTo.tb, dataToTb);
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "OK"}); instance.send(instanceSendTo.dido_controller, values);
counter = 0; counter = 0;
@ -204,9 +204,8 @@ exports.install = function(instance) {
sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer"); sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer");
instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day"); instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day");
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "NOK"}); instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
} }
} }
} }
@ -216,5 +215,4 @@ exports.install = function(instance) {
}, 3000); }, 3000);
startRead = setInterval(start, timeoutMin * 1000 * 60); startRead = setInterval(start, timeoutMin * 1000 * 60);
}; };

View file

@ -1,124 +1,124 @@
const { MD5 } = require('./md5.js'); const { MD5 } = require('./md5.js');
const { networkInterfaces } = require('os'); const { networkInterfaces } = require('os');
class ErrorToServiceHandler class ErrorToServiceHandler
{ {
constructor() { constructor() {
this.previousValues = {}; this.previousValues = {};
this.projects_id = undefined; this.projects_id = undefined;
this.message_type = "error_message";
const nets = networkInterfaces();
const nets = networkInterfaces(); this.ipAddresses = Object.create(null); // Or just '{}', an empty object
this.ipAddresses = Object.create(null); // Or just '{}', an empty object
for (const name of Object.keys(nets)) {
for (const name of Object.keys(nets)) { for (const net of nets[name]) {
for (const net of nets[name]) { // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses if (net.family === 'IPv4' && !net.internal) {
if (net.family === 'IPv4' && !net.internal) { if (!this.ipAddresses[name]) {
if (!this.ipAddresses[name]) { this.ipAddresses[name] = [];
this.ipAddresses[name] = []; }
} this.ipAddresses[name].push(net.address);
this.ipAddresses[name].push(net.address); }
} }
} }
}
//console.log(this.ipAddresses);
//console.log(this.ipAddresses);
}
}
setProjectsId(projects_id)
setProjectsId(projects_id) {
{ this.projects_id = projects_id;
this.projects_id = projects_id; }
}
processMessage(message, seconds, message_type)
processMessage(message, seconds, message_type) {
{ if(message_type == undefined) message_type = "error_message";
if(Array.isArray(message)) message = message.join(', '); if(Array.isArray(message)) message = message.join(', ');
//keep in memory - default value is 1h
if (seconds === undefined) seconds = 60*60; let key = MD5(message);
if(message_type) this.message_type = message_type; let timestamp = new Date().getTime();
let key = MD5(message); //keep in memory - default value is 1h
let timestamp = new Date().getTime(); if (seconds === undefined) seconds = 60*60;
if(!this.previousValues.hasOwnProperty(key)) if(!this.previousValues.hasOwnProperty(key))
{ {
this.previousValues[key] = {ts: timestamp, duration: seconds}; this.previousValues[key] = {ts: timestamp, duration: seconds};
} }
let diff = (timestamp - this.previousValues[key].ts); let diff = (timestamp - this.previousValues[key].ts);
if(diff < this.previousValues[key].duration*1000) return false; if(diff < this.previousValues[key].duration*1000) return false;
this.previousValues[key].ts = timestamp; this.previousValues[key].ts = timestamp;
return true; return true;
} }
sendMessageToService(message, seconds, message_type) sendMessageToService(message, seconds, message_type)
{ {
let f = this.processMessage(message, seconds, message_type); let f = this.processMessage(message, seconds, message_type);
if(!f) return; if(!f) return;
/* /*
//------------- //-------------
if(message_type == undefined) message_type = "error_message"; if(message_type == undefined) message_type = "error_message";
if(Array.isArray(message)) message = message.join(', '); if(Array.isArray(message)) message = message.join(', ');
let key = MD5(message); let key = MD5(message);
let timestamp = new Date().getTime(); let timestamp = new Date().getTime();
//keep in memory //keep in memory
if (seconds === undefined) seconds = 60*60; if (seconds === undefined) seconds = 60*60;
if(!this.previousValues.hasOwnProperty(key)) if(!this.previousValues.hasOwnProperty(key))
{ {
this.previousValues[key] = {ts: timestamp, duration: seconds}; this.previousValues[key] = {ts: timestamp, duration: seconds};
} }
let diff = (timestamp - this.previousValues[key].ts); let diff = (timestamp - this.previousValues[key].ts);
if(diff < this.previousValues[key].duration*1000) return; if(diff < this.previousValues[key].duration*1000) return;
this.previousValues[key].ts = timestamp; this.previousValues[key].ts = timestamp;
*/ */
//------------------------- //-------------------------
//send to service //send to service
let dataToInfoSender = {id: this.projects_id}; let dataToInfoSender = {id: this.projects_id};
//js_error || error_message //js_error || error_message
dataToInfoSender[this.message_type] = message; dataToInfoSender[message_type] = message;
dataToInfoSender.ipAddresses = this.ipAddresses; dataToInfoSender.ipAddresses = this.ipAddresses;
console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender); console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender);
//TODO UGLY!!! //TODO UGLY!!!
if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id; if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id;
/* /*
if(this.projects_id === undefined) if(this.projects_id === undefined)
{ {
console.log("this.projects_id is undefined"); console.log("this.projects_id is undefined");
return; return;
} }
*/ */
RESTBuilder.make(function(builder) { RESTBuilder.make(function(builder) {
builder.method('POST'); builder.method('POST');
builder.post(dataToInfoSender); builder.post(dataToInfoSender);
builder.url('http://192.168.252.2:8004/sentmessage'); builder.url('http://192.168.252.2:8004/sentmessage');
builder.callback(function(err, response, output) { builder.callback(function(err, response, output) {
console.log("process.on error send", err, response, output, dataToInfoSender); console.log("process.on error send", err, response, output, dataToInfoSender);
}); });
}); });
} }
} }
module.exports = ErrorToServiceHandler; module.exports = ErrorToServiceHandler;

View file

@ -1,135 +1,137 @@
const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js'); const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js');
const dbNotifications = TABLE("notifications"); const dbNotifications = TABLE("notifications");
//key is device, value = str //key is device, value = str
let sentValues= {}; let sentValues= {};
let notificationsData = {}; let notificationsData = {};
let ERRWEIGHT = { let ERRWEIGHT = {
EMERGENCY: "emergency", // System unusable EMERGENCY: "emergency", // System unusable
ALERT: "alert", // Action must be taken immidiately ALERT: "alert", // Action must be taken immidiately
CRITICAL: "critical", // Component unable to function CRITICAL: "critical", // Component unable to function
ERROR: "error", // Error, but component able to recover from it ERROR: "error", // Error, but component able to recover from it
WARNING: "warning", // Possibility of error, system running futher WARNING: "warning", // Possibility of error, system running futher
NOTICE: "notice", // Significant message but not an error, things user might want to know about NOTICE: "notice", // Significant message but not an error, things user might want to know about
INFO: "informational", // Info INFO: "informational", // Info
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
}; };
function getKey(map, val) { function getKey(map, val) {
return Object.keys(map).findItem(key => map[key] === val); return Object.keys(map).findItem(key => map[key] === val);
} }
//https://stackoverflow.com/questions/41117799/string-interpolation-on-variable //https://stackoverflow.com/questions/41117799/string-interpolation-on-variable
var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]); var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);
async function initNotifications() async function initNotifications()
{ {
let response = await promisifyBuilder(dbNotifications.find()); let response = await promisifyBuilder(dbNotifications.find());
notificationsData = makeMapFromDbResult(response, "key"); notificationsData = makeMapFromDbResult(response, "key");
console.log("initNotifications done" ); console.log("initNotifications done" );
} }
function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) { function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) {
let storeToSendValues = true; // return;
if(saveKey == undefined) storeToSendValues = false;
let storeToSendValues = true;
let lang = FLOW.OMS_language; if(saveKey == undefined) storeToSendValues = false;
if(lang != "en" || lang != "sk") lang = "en";
let lang = FLOW.OMS_language;
let tpl = key; if(lang != "en" || lang != "sk") lang = "en";
let weight = "";
let tpl = key;
if(notificationsData[key]) let weight = "";
{
weight = notificationsData[key].weight; if(notificationsData[key])
weight = weight.toLowerCase(); {
weight = notificationsData[key].weight;
tpl = notificationsData[key][lang]; weight = weight.toLowerCase();
tpl = template(tpl, params);
} tpl = notificationsData[key][lang];
else tpl = template(tpl, params);
{ }
console.error("sendNotification: Notifications: undefined key", key, func, notificationsData); else
return false; {
} console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
return false;
//detect invalid err weight }
if(getKey(ERRWEIGHT, weight) == undefined)
{ //detect invalid err weight
console.error("sendNotification: Notifications: undefined weight", weight, key, func); if(getKey(ERRWEIGHT, weight) == undefined)
return false; {
} console.error("sendNotification: Notifications: undefined weight", weight, key, func);
return false;
if(sentValues.hasOwnProperty(saveKey)) }
{
if(sentValues[saveKey] == tpl) if(sentValues.hasOwnProperty(saveKey))
{ {
return false; if(sentValues[saveKey] == tpl)
} {
} return false;
}
if(sentValues[saveKey] == undefined) }
{
if(storeToSendValues) if(sentValues[saveKey] == undefined)
{ {
//do not send - flow is was started if(storeToSendValues)
sentValues[saveKey] = tpl; {
return false; //do not send - flow is was started
} sentValues[saveKey] = tpl;
} return false;
}
if(saveKey == "rvo_door") }
{
//console.log("******", saveKey, sentValues[saveKey], tpl); if(saveKey == "rvo_door")
} {
//console.log("******", saveKey, sentValues[saveKey], tpl);
if(storeToSendValues) sentValues[saveKey] = tpl; }
let str = FLOW.OMS_rvo_name; if(storeToSendValues) sentValues[saveKey] = tpl;
if(str != "") str = str + ": ";
str = str + tpl; let str = FLOW.OMS_rvo_name;
if(str != "") str = str + ": ";
let content = { str = str + tpl;
"type": weight,
"status": "new", let content = {
"source": { "type": weight,
"func":func, "status": "new",
"component":instance.id, "source": {
"component_name":instance.name, "func":func,
"edge":device "component":instance.id,
}, "component_name":instance.name,
"message":str, "edge":device
"message_data": extra },
}; "message":str,
"message_data": extra
let msg = {}; };
msg[device] = [
{ let msg = {};
"ts": Date.now(), msg[device] = [
"values": { {
"_event":content "ts": Date.now(),
} "values": {
} "_event":content
]; }
}
// Msg can be outputted from components only after configuration ];
/*if (canSendErrData()){
sendBufferedErrors(); // Msg can be outputted from components only after configuration
} else { /*if (canSendErrData()){
bufferError(msg); sendBufferedErrors();
}*/ } else {
instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections bufferError(msg);
}*/
return true; 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, module.exports = {
ERRWEIGHT sendNotification,
initNotifications,
ERRWEIGHT
} }

View file

@ -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)`; 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'); const fs = require('fs');
var path = require('path'); var path = require('path');
exports.install = async function(instance) { exports.install = async function(instance) {
let id;
let allValues = {}; let allValues = {};
let dbSettings;
let sendAllValuesInterval; let sendAllValuesInterval;
let now = new Date(); 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() function sendValues()
{ {
const id = FLOW.OMS_projects_id;
if(Object.keys(allValues).length > 0) if(Object.keys(allValues).length > 0)
{ {
if(id !== undefined) if(id !== undefined)

File diff suppressed because it is too large Load diff

336
flow/modbus_reader.js Normal file
View file

@ -0,0 +1,336 @@
exports.id = 'modbus_reader';
exports.title = 'Modbus reader';
exports.version = '2.0.0';
exports.group = 'Worksys';
exports.color = '#2134B0';
exports.output = ["red", "white"];
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 ErrorToServiceHandler = require('./helper/ErrorToServiceHandler');
const errorHandler = new ErrorToServiceHandler();
const { sendNotification } = require('./helper/notification_reporter');
const instanceSendTo = {
debug: 0,
dido_controller: 1,
};
//to handle NOK and OK sendNotifications s
const numberOfNotResponding = {};
let tbName = null;
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;
// kedze potrebujeme ist stale dookola pre jednotlive zariadenia, potrebujeme ci uz index ako aj adresu zariadenia, a aj pocet registrov na vycitanie
this.deviceAddress = null; // adresa zariadenia (1 ma EM340 a 2 ma twilight_sensor)
this.indexInDeviceConfig = 0; // prvy item v deviceConfig
this.lengthOfActualDeviceStream = null;
this.device = null;
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);
}
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;
})
};
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, 2000);
}
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, {}, "", instanceSendTo.tb, instance);
delete numberOfNotResponding[obj.device];
}
obj.transformResponse(resp, register, obj.deviceAddress);
obj.error = 0;
obj.index++;
if(obj.index < obj.lengthOfActualDeviceStream) setTimeout(obj.readRegisters, 0);
else obj.setNewStream();
}).catch (function () {
console.log("error pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute);
obj.error++;
if(obj.error == obj.lengthOfActualDeviceStream)
{
instance.send(instanceSendTo.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, {}, "", instanceSendTo.tb, instance);
numberOfNotResponding[obj.device] = 1;
}
obj.error = 0;
numberOfNotResponding[obj.device] += 1;
}
console.error(require('util').inspect(arguments, {
depth: null
}))
obj.index++;
if(obj.index < obj.lengthOfActualDeviceStream) setTimeout(obj.readRegisters, 0);
else obj.setNewStream();
})
};
transformResponse = (response, register, deviceAddress) => {
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(deviceAddress, register, tbName, tbAttribute, response, a.multiplier, value);
// if(tbName == undefined) return;
if(this.index + 1 + this.errors < this.lengthOfActualDeviceStream)
{
this.allValues[tbAttribute] = value;
return;
}
const values = {
...this.allValues,
[tbAttribute]: value,
};
this.checkNullVoltage(values);
instance.send(instanceSendTo.dido_controller, {values: values});
this.allValues = {};
break;
}
}
}
setNewStream = () =>
{
// console.log('------------',this.lengthOfActualDeviceStream, this.index);
// console.log('------------',this.indexInDeviceConfig, deviceConfig.length);
if(this.lengthOfActualDeviceStream == this.index)
{
if(this.indexInDeviceConfig + 1 == deviceConfig.length)
{
this.indexInDeviceConfig = 0;
}
else
{
this.indexInDeviceConfig += 1;
}
this.getActualStreamAndDevice();
}
}
// sendFinalObjects = (values) =>
// {
// const date = Date.now();
// // values["status"] = "OK";
// const dataToTB = {
// [tbName]: [
// {
// "ts": date,
// "values": values
// }
// ]
// };
// instance.send(instanceSendTo.tb, dataToTB);
// const dataToDiDo = {
// values: values
// }
// instance.send(instanceSendTo.dido_controller, dataToDiDo);
// }
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_citys: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase );
// console.log('no voltage')
}
else
{
FLOW.OMS_no_voltage.delete(phase);
// console.log('voltage detected')
sendNotification("modbus_citys: checkNullVoltage", tbName, "voltage_on_phase_restored", {phase: phase}, "", instanceSendTo.tb, instance, "voltage" + phase);
}
}
})
}
// we use dataToTbHandler. Therefore we need to check, if objects we send to dido_controller are not empty
isObjectEmpty = (objectName) => {
return Object.keys(objectName).length === 0 && objectName.constructor === Object;
}
}
setTimeout(() => {
const newSocket = new SocketWithClients();
tbName = FLOW.OMS_rvo_tbname;
}, 25000);
}

View file

@ -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);
};

View file

@ -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();
};

View file

@ -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();
};

View file

@ -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();
};

View file

@ -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;
}

View file

@ -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);
}
});
}
});
};

View file

@ -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 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 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;
}
}

View file

@ -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());

220
flow/thermometer.js Normal file
View file

@ -0,0 +1,220 @@
exports.id = 'thermometer';
exports.title = 'Thermometer';
exports.group = 'Worksys';
exports.color = '#5CB36D';
exports.version = '1.0.3';
exports.output = ["red", "white", "blue"];
exports.author = 'Rastislav Kovac';
exports.icon = 'thermometer-three-quarters';
exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`;
const instanceSendTo = {
debug: 0,
tb: 1,
dido_controller: 2
}
//read temperature - frequency
let timeoutMin = 5;//minutes
var path = require('path');
var log4js = require("log4js");
log4js.configure({
appenders: {
errLogs: { type: 'file', filename: path.join(__dirname + "/../", 'err.txt') },
monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') },
console: { type: 'console' }
},
categories: {
errLogs: { appenders: ['console', 'errLogs'], level: 'error' },
monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' },
//another: { appenders: ['console'], level: 'trace' },
default: { appenders: ['console'], level: 'trace' }
}
});
const errLogger = log4js.getLogger("errLogs");
const logger = log4js.getLogger();
const monitor = log4js.getLogger("monitorLogs");
//logger.debug("text")
//monitor.info('info');
//errLogger.error("some error");
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper');
const dbSettings = TABLE("settings");
let temperatureAddress = "";
async function loadSettings()
{
//todo global FLOW.OMS_edgeName is making problem, so we load it here as well, it should not be
let responseSettings = await promisifyBuilder(dbSettings.find());
temperatureAddress = responseSettings[0]["temperature_adress"];
}
loadSettings();
exports.install = function(instance) {
const { exec } = require('child_process');
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
let startRead;
let dataToTb;
let counter = 0;
let edgeName = "";
logger.debug(exports.title, "installed");
instance.on("close", function(){
clearInterval(startRead);
})
const start = function() {
try {
if(FLOW.OMS_controller_type === "unipi")
{
clearInterval(startRead);
return;
}
if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined";
logger.debug("FLOW.OMS_temperature_adress", FLOW.OMS_temperature_adress);
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
edgeName = FLOW.OMS_edgeName;
if(edgeName !== "")
{
if(error)
{
if(FLOW.OMS_brokerready == undefined)
{
logger.debug("gettemparature - FLOW.OMS_brokerready is undefined");
setTimeout(function(){
start();
}, 3000);
return;
}
if(FLOW.OMS_brokerready)
{
//sendNotification("start", edgeName, ERRWEIGHT.WARNING, "Thermometer is not responding", {"Error": error}, instanceSendTo.tb, instance, "thermometer");
sendNotification("start", edgeName, "thermometer_is_not_responding", {}, {"Error": error}, instanceSendTo.tb, instance, "thermometer");
}
let status = "NOK";
dataToTb = {
[edgeName]: [
{
"ts": Date.now(),
"values": {
"status": status
}
}
]
}
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
// instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
}
else parseData(stdout);
}
else
{
monitor.info("gettemperature: edgeName is not defined", FLOW.OMS_edgeName);
setTimeout(function(){
start();
}, 3000);
return;
}
//instance.send({"Temp":stdout,"stderr":stderr,"err":error});
});
}
catch(err) {
errLogger.error(exports.title, err);
}
}
const parseData = function(data) {
data = parseFloat(data);
logger.debug("gettemperature", data);
if(!isNaN(data)) {
if(counter > 290)
{
instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break");
//sendNotification("parseData", edgeName, ERRWEIGHT.NOTICE, "Thermometer is working again", "", instanceSendTo.tb, instance, "thermometer");
if(FLOW.OMS_brokerready) sendNotification("parseData", edgeName, "thermometer_is_responding_again", {}, "", instanceSendTo.tb, instance, "thermometer");
}
logger.debug("gettemperature", data);
const values = {
"temperature": Number(data.toFixed(2)),
"status": "OK"
}
dataToTb = {
[edgeName]: [
{
"ts": Date.now(),
"values":values
}
]
}
instance.send(instanceSendTo.tb, dataToTb);
instance.send(instanceSendTo.dido_controller, values);
counter = 0;
} else {
counter++;
monitor.info("gettemperature err", counter, data);
//ked je problem 1 den
let day = 24 * 60 / timeoutMin;
if ( counter > day && counter < day + 2 ) {
//sendNotification("parseData", edgeName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", instanceSendTo.tb, instance, "thermometer");
sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer");
instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day");
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
}
}
}
setTimeout(function(){
start();
}, 15000);
startRead = setInterval(start, timeoutMin * 1000 * 60);
};

View file

@ -119,6 +119,7 @@ process.on('uncaughtException', function (err) {
const nosql = NOSQL('tbdata'); const nosql = NOSQL('tbdata');
const nosqlBackup = NOSQL('/backup/tbdata'); const nosqlBackup = NOSQL('/backup/tbdata');
exports.install = function(instance) { exports.install = function(instance) {
var broker; var broker;
@ -186,7 +187,7 @@ exports.install = function(instance) {
let mqtt_clientid = responseSettings[0]["mqtt_clientid"]; let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
let mqtt_username = responseSettings[0]["mqtt_username"]; let mqtt_username = responseSettings[0]["mqtt_username"];
let mqtt_port = responseSettings[0]["mqtt_port"]; let mqtt_port = responseSettings[0]["mqtt_port"];
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]); console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
opts = { opts = {
@ -203,8 +204,7 @@ exports.install = function(instance) {
} }
connectToTbServer(); connectToTbServer();
}
}
function connectToTbServer() function connectToTbServer()
{ {
@ -215,16 +215,18 @@ exports.install = function(instance) {
broker.on('connect', function() { broker.on('connect', function() {
instance.status("Connected", "green"); instance.status("Connected", "green");
brokerready = true; monitor.info("MQTT broker connected");
brokerready = true;
FLOW.OMS_brokerready = brokerready; FLOW.OMS_brokerready = brokerready;
wsmqtt_status = 'connected'; wsmqtt_status = 'connected';
}); });
broker.on('reconnect', function() { broker.on('reconnect', function() {
instance.status("Reconnecting", "yellow"); instance.status("Reconnecting", "yellow");
brokerready = false; brokerready = false;
FLOW.OMS_brokerready = brokerready; FLOW.OMS_brokerready = brokerready;
}); });
broker.on('message', function(topic, message) { broker.on('message', function(topic, message) {
@ -243,7 +245,6 @@ exports.install = function(instance) {
} }
instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message }); instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message });
}); });
broker.on('close', function(err) { broker.on('close', function(err) {
@ -258,11 +259,14 @@ exports.install = function(instance) {
instance.status("Disconnected", "red"); instance.status("Disconnected", "red");
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts }); instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
} }
broker.reconnect();
}); });
broker.on('error', function(err) { broker.on('error', function(err) {
instance.status("Err: "+ err.code, "red"); instance.status("Err: "+ err.code, "red");
instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts }); instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts });
monitor.info('MQTT broker error', err);
brokerready = false; brokerready = false;
FLOW.OMS_brokerready = brokerready; 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.on('data', function(data) {
/*
instance.reconfigure = function() {
if (brokerready)
var o = instance.options; {
opts = { //do we have some data in backup file?
host: o.host, //if any, process data from database
port: o.port, if(saveTelemetryOnError)
keepalive: 10,
clientId: o.clientid,
username: o.username,
rejectUnauthorized: false,
resubscribe: false
};
//connectToTbServer();
};
*/
instance.on('data', function(data) {
if (brokerready)
{ {
//do we have some data in backup file? //read telemetry data and send back to server
//if any, process data from database if(!processingData) processDataFromDatabase();
if(saveTelemetryOnError) }
}
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 let options = {compress: true};
if(!processingData) processDataFromDatabase(); 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) { instance.close = function(done) {
if (brokerready){ if (brokerready){
@ -449,7 +430,6 @@ exports.install = function(instance) {
fs.truncateSync(source, 0); fs.truncateSync(source, 0);
} }
} }
const processDataFromDatabase = async () => { const processDataFromDatabase = async () => {

View file

@ -4,12 +4,14 @@
"version": "1.0.0", "version": "1.0.0",
"main": "debug.js", "main": "debug.js",
"dependencies": { "dependencies": {
"easy-crc": "0.0.2",
"mqtt": "^4.2.6",
"serialport": "^9.0.0",
"total.js": "^3.4.5",
"bitwise": "^2.1.0", "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": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"

View file

@ -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
}
]
}]
}
}