Actual running flowserver on test panel LM 10.0.0.5
This commit is contained in:
commit
67c503d980
57 changed files with 16678 additions and 0 deletions
11
config
Normal file
11
config
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
name : Total.js Flow
|
||||
|
||||
// Packages settings
|
||||
package#flow (Object) : { url: '/' }
|
||||
|
||||
|
||||
table.relays : line:number|tbname:string|contactor:number|profile:string
|
||||
table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
||||
table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
|
||||
table.pins : pin:number|type:string|line:number
|
||||
table.notifications : key:string|weight:string|sk:string|en:string
|
||||
0
databases/backup/tbdata.nosql
Normal file
0
databases/backup/tbdata.nosql
Normal file
48
databases/nodes.table
Normal file
48
databases/nodes.table
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
||||
+|697|joqRYBVL30k9eQWOlZ5qwpD2KJpNEmA6gPxXzwaM|3||1|1|...........
|
||||
+|659|Ymn9oleRxJ0vw17WzAyGwdyEBk4ObdMXj2VgpNLG|3||1|1|...........
|
||||
+|636|M6ogKQW09bOXewAYvZyvJqyJrV1aRnPGE37p42Nx|2||1|1|...........
|
||||
+|648|gaMGN4x1e9JlZz0QPRDd9Rym6dVr3OpvqKnoWBbk|2||1|1|...........
|
||||
+|664|oGVzxNWP9lrjaQ7vKODQ7g51gqp62YZREmdw3XBM|1||1|1|...........
|
||||
+|634|NGWamnYqlP1wbgrZQxDAWm5e2X7OVAK69koR04vL|2||1|1|...........
|
||||
+|670|dlE1VQjYrNx9gZRmb38g1YyoLBO4qaAk2M6JPnG7|2||1|1|...........
|
||||
+|641|vnmG4kJxaXWNBgMQq0D7Mz5e9oZzOAlr6LdR3w2V|2||1|1|...........
|
||||
+|632|LpkVlmq4b3jMwJQxBZ8aM78rXAP6o97Ke0aOYEg2|2||1|1|...........
|
||||
+|667|MzXBoWbEZjO0lrpqnRyoJ4DkmVeaNAGdL9g4QKxP|1||1|1|...........
|
||||
+|682|vnreBJ6PMqgz20pYEL82XQyG1jkWwdQxZVNAOlmK|1||1|1|...........
|
||||
+|643|oZmYXEbw9lVWRv1jLxDe9bDdgAMz4PKQnNJ6eB23|1||1|1|...........
|
||||
+|642|pEonaKBOGbj9034MgJ8W3G8qXvxNWVkAPQz21R6L|1||1|1|...........
|
||||
+|647|BLQal6Pn9oz1KmNgek5Yqd50vd2MAbqG3OV7Rp4j|1||1|1|...........
|
||||
+|646|4agVJ9dPQkmp1R2X3EDJKxyrK6ZlNoM0n7qxBOev|1||1|1|...........
|
||||
+|666|9PpgLEnvk4WMV6RmOJybMGDaeAXzo2BQNG3K17Zw|1||1|1|...........
|
||||
+|654|Mmp93b2nvd7OoqgBeEyEZq5kjlAV1Y4ZNXwW0zLG|1||1|1|...........
|
||||
+|637|koW06PeGrLlBp2YJQE5Ogw5RmMaXKzj3wOAZg9n7|3||1|1|...........
|
||||
+|680|KL2jNOVpdARa9XvoeJDPga8bkmPBxqn7Ww3gzGQ1|1|{"intervals":[{"cct":3000,"value":100,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":-1,"end_time":"13:50","start_time":"13:40"},{"cct":3000,"value":100,"end_time":"13:00","start_time":"13:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|0|...........
|
||||
+|645|jklN4JpQAx362o9XYZDN6wDgrWw1P7GEbdBM0vRV|1||1|1|...........
|
||||
+|660|gj7zbKV46oQ1p2e0AJ8XqZDG3YNWaRrlOEXvBxmM|3||1|1|...........
|
||||
+|669|Y9aLW03wOZkABvKXbMyL0lyV1xdNj72r4egqGRzJ|3|{"intervals":[{"cct":3000,"value":0,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"07:20","start_time":"13:50"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"07:20"},{"cct":3000,"value":0,"end_time":"13:50","start_time":"13:40"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|671|AvVdgzYJZaPx3oMqeED4Oj8NnmKkw716bRO90jLB|3||1|1|...........
|
||||
+|638|9xgzG4Op1BrKZPmoQkDrmj8E73ndJNMjavAwX2Re|3||1|1|...........
|
||||
+|639|BOjEzGRZ46bnp9wa2A8z76D0JkmW1QPNdrqevXVL|3||1|1|...........
|
||||
+|693|KjbN4q7JPZmexgdnz2yKdn5YAWwO0Q3BMX6ERLoV|2|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"22:10","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":30,"end_time":"02:40","start_time":"22:10"},{"cct":3000,"value":90,"end_time":"05:30","start_time":"02:40"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|649|0p2rwdP7aGoOQLJNgAynJNy6xWXbmMe3nvZqlzkV|1|{"intervals":[{"cct":3000,"value":100,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":-1,"end_time":"13:50","start_time":"13:40"},{"cct":3000,"value":100,"end_time":"13:00","start_time":"13:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|698|mYnBzbeGaAL62jowRv59M35Xq9QpZ0K7O1dg4xVl|1||1|1|...........
|
||||
+|640|WjBL12pg63eX4N9P7zy0XYyEJKmlbkGwZMx0avQV|2||1|1|...........
|
||||
+|656|BrQx3NGKgVMRaXYAo9y1GE8ZzkWnj1le6bdOLE20|1|{"intervals":[{"cct":3000,"value":100,"end_time":"13:40","start_time":"13:00"},{"cct":3000,"value":-1,"end_time":"13:50","start_time":"13:40"},{"cct":3000,"value":100,"end_time":"13:00","start_time":"13:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|651|qaAOzENGrvpbe0VoK7D6Ld519PZmdg3nl24JLQMk|2||1|1|...........
|
||||
+|691|lekrmdvO0BQG1ZW4AV8jzq8M39xnN2wEbRgPjXLp|1||1|1|...........
|
||||
+|661|laYK7Pomn2bNZXEpedDxAqyOJkQ3WwV49gqxLrAR|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":60,"end_time":"05:30","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|665|gbv4nzqxW0XGAPKVNk8kr25ZQ2l3O6LRBprM97ew|3||1|1|...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|662|gbv4nzqxW0XGAPKVNk8kW48ZQ2l3O6LRBprM97ew|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|668|lekrmdvO0BQG1ZW4AV8jeZ5M39xnN2wEbRgPjXLp|3|{"intervals":[{"cct":3000,"value":0,"end_time":"13:10","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"12:50","start_time":"13:10"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"12:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|689|q0rElBPdL6kxMAjnzVDRl95emNZY7oOv2wK9gb31|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|683|XKQbz3WAwY21dGa0R453rWyJm9PZOjqlvpr6Nkeo|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|688|PaGbQ3wBAZWOmRvK9VDpvz5endLJYopEqlkzNMxX|3|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|672|0XYElWeKBNJn1gdoMG8lON5ALkPvj4V3xra2q6mO|2||1|1|.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|690|wGjQobgOK0n2YqBZmVDVR3DR9ep6EXA1ka3vzlP7|2||1|1|..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|692|l9YkRpoB2vVa0mKqEO8ZGGDjW43eXnJML6GxzbwQ|2|{"intervals":[{"cct":3000,"value":0,"end_time":"13:10","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"12:50","start_time":"13:10"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"12:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|655|RMgnK93rkoAazbqdQ4yBYpDZ1YXGx6pmwBeVEP2O|2||1|1|..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|694|Jm32GR1qpwQxlZza0N5mE15AP96YbOKLogrXVW4e|2|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|635|Vq2JaWpw1OdBKmNeoj8w605XE40l3kgL76Azb9QP|2|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|650|0XYElWeKBNJn1gdoMG8lYdDALkPvj4V3xra2q6mO|3|{"intervals":[{"cct":3000,"value":0,"end_time":"13:10","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"12:50","start_time":"13:10"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"12:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|1|1|...........
|
||||
+|663|LpkVlmq4b3jMwJQxBZ8akayrXAP6o97Ke0aOYEg2|1|{"intervals":[{"cct":3000,"value":0,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":100,"end_time":"21:20","start_time":"20:00"},{"cct":3000,"value":0,"end_time":"13:00","start_time":"05:30"},{"cct":3000,"value":80,"end_time":"22:20","start_time":"21:50"},{"cct":3000,"value":70,"end_time":"22:50","start_time":"22:20"},{"cct":3000,"value":60,"end_time":"23:20","start_time":"22:50"},{"cct":3000,"value":50,"end_time":"23:50","start_time":"23:20"},{"cct":3000,"value":40,"end_time":"00:20","start_time":"23:50"},{"cct":3000,"value":100,"end_time":"05:30","start_time":"03:20"},{"cct":3000,"value":30,"end_time":"00:50","start_time":"00:20"},{"cct":3000,"value":20,"end_time":"01:20","start_time":"00:50"},{"cct":3000,"value":90,"end_time":"21:50","start_time":"21:20"},{"cct":3000,"value":30,"end_time":"01:50","start_time":"01:20"},{"cct":3000,"value":40,"end_time":"02:20","start_time":"01:50"},{"cct":3000,"value":50,"end_time":"02:50","start_time":"02:20"},{"cct":3000,"value":60,"end_time":"03:20","start_time":"02:50"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|0|1|...........
|
||||
32
databases/notifications.table
Normal file
32
databases/notifications.table
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
key:string|weight:string|sk:string|en:string
|
||||
+|switching_profile_point_applied_to_line|INFORMATIONAL|Aplikovaný bod spínacieho profilu na línií č. ${line} : ${value}|Switching profile point applied to line no. ${line} : ${value}|...............
|
||||
+|dusk_has_occured|INFORMATIONAL|Nastal súmrak: ${value}|Dusk has occured: ${value}|...............
|
||||
+|dawn_has_occured|INFORMATIONAL|Nastal úsvit: ${value}|Dawn has occured: ${value}|...............
|
||||
+|dimming_profile_was_successfully_received_by_node|NOTICE|Stmievací profil bol úspešne prijatý nodom č. ${node}|Dimming profile was successfully received by node no. ${node}|...............
|
||||
+|master_node_is_responding_again|NOTICE|Master node začal znovu odpovedať|Master node is responding again|...............
|
||||
+|command_was_sent_from_terminal_interface|DEBUG|Z terminálu bol odoslaný príkaz|A command was sent from terminal interface|...............
|
||||
+|master_node_is_not_responding|ALERT|Master node neodpovedá|Master node is not responding|...............
|
||||
+|configuration_of_dimming_profile_to_node_failed|ALERT|Konfigurácia stmievacieho profilu pre node č. ${node} zlyhala|Configuration of dimming profile to node no. ${node} has failed|...............
|
||||
+|circuit_breaker_was_turned_on_line|NOTICE|Zapnutie ističa na línii č. ${line}|Circuit breaker was turned on - line no. ${line}|...............
|
||||
+|circuit_breaker_was_turned_off_line|ERROR|Vypnutie ističa na línií č. ${line}|Circuit breaker was turned off - line no. ${line}|...............
|
||||
+|dimming_profile_was_processed_for_node|INFORMATIONAL|Stmievací profil bol spracovaný pre node č. ${node}|Dimming profile was processed for node no. ${node}|...............
|
||||
+|switching_profile_was_processed_for_line|INFORMATIONAL|Spínací profil bol spracovaný pre líniu č. ${line}|Switching profile was processed for line no. ${line}|...............
|
||||
+|thermometer_is_not_responding|WARNING|Teplomer neodpovedá|Thermometer is not responding|...............
|
||||
+|thermometer_is_responding_again|NOTICE|Teplomer znovu odpovedá|Thermometer is responding again|...............
|
||||
+|thermometer_sends_invalid_data|WARNING|Teplomer posiela neplatné hodnoty|Thermometer sends invalid data|...............
|
||||
+|main_switch_has_been_turned_off|CRITICAL|Hlavný vypínač bol vypnutý|Main switch has been turned off|...............
|
||||
+|main_switch_has_been_turned_on|NOTICE|Hlavný vypínač bol zapnutý|Main switch has been turned on|...............
|
||||
+|power_supply_has_disconnected_input|ALERT|Napájací zdroj nemá napätie na vstupe|Power supply has disconnected input|...............
|
||||
+|power_supply_works_correctly|NOTICE|Napájací zdroj pracuje správne|Power supply works correctly|...............
|
||||
+|battery_level_is_low|ERROR|Batéria má nízku úroveň napätia|Battery level is low|...............
|
||||
+|battery_level_is_ok|NOTICE|Batéria má správnu úroveň napätia|Battery level is OK|...............
|
||||
+|door_has_been_open|NOTICE|Dvere boli otvorené|Door has been open|...............
|
||||
+|door_has_been_closed|NOTICE|Dvere boli zatvorené|Door has been closed|...............
|
||||
+|door_has_been_open_without_permision_alarm_is_on|WARNING|Dvere boli otvorené bez povolania - zapnutá siréna|Door has been open without permision - alarm is on|...............
|
||||
+|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|...............
|
||||
+|local_database_is_corrupted|CRITICAL|||...............
|
||||
+|electrometer_is_not_responding|ERROR|Elektromer neodpovedá|Electrometer is not responding|...............
|
||||
+|no_voltage_detected_on_phase|CRITICAL|Na fáze č. ${phase} nie je napätie|No voltage detected on phase no. ${phase}|...............
|
||||
+|electrometer_is_responding_again|NOTICE|Elektromer znovu odpovedá|Electrometer is responding again|...............
|
||||
+|voltage_on_phase_has_been_restored|NOTICE|Napätie na fáze č. ${phase} bolo obnovené|Voltage on phase no. ${phase} has been restored|...............
|
||||
+|flow_start|NOTICE|FLOW bol spustený|FLOW has been started |...............
|
||||
14
databases/pins.table
Normal file
14
databases/pins.table
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
pin:number|type:string|line:number
|
||||
*|1|state_of_main_switch|0|...........
|
||||
*|2|rotary_switch_state|0|...........
|
||||
*|3|rotary_switch_state|0|...........
|
||||
*|4|power_supply|0|...........
|
||||
*|5|battery|0|...........
|
||||
*|6|door_condition|0|...........
|
||||
*|8|state_of_breaker|1|...........
|
||||
*|9|state_of_breaker|2|...........
|
||||
*|10|state_of_breaker|3|...........
|
||||
*|11|state_of_contactor|1|...........
|
||||
*|12|state_of_contactor|2|...........
|
||||
*|13|state_of_contactor|3|...........
|
||||
*|16|twilight_sensor|0|...........
|
||||
5
databases/relays.table
Normal file
5
databases/relays.table
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
line:number|tbname:string|contactor:number|profile:string
|
||||
+|0|KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV|1||...........
|
||||
+|3|vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V|0||....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|1|RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O|0|{"intervals":[{"value":0,"end_time":"13:00","start_time":"13:00"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........................................................................................................................................................................................................................................................................................................................................................................................
|
||||
+|2|dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7|0||................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
||||
2
databases/settings.table
Normal file
2
databases/settings.table
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
|
||||
+|testpanel|en|28.427B45920702|48.70826502|17.28455203|192.168.252.4|showroom_test_panel_led|xmRd6RJxW53WZe4vMFLU|1883|0|1|lm|ttymxc4|1|20|5|...........................................
|
||||
0
databases/tbdata.nosql
Normal file
0
databases/tbdata.nosql
Normal file
0
err.txt
Normal file
0
err.txt
Normal file
48
flow/audit_test_panel.csv
Normal file
48
flow/audit_test_panel.csv
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
LumDimm;NODE (HEX);NODE (DEC);Line;TB name
|
||||
1;299;665;3;gbv4nzqxW0XGAPKVNk8kr25ZQ2l3O6LRBprM97ew
|
||||
2;28A;650;3;0XYElWeKBNJn1gdoMG8lYdDALkPvj4V3xra2q6mO
|
||||
3;296;662;3;gbv4nzqxW0XGAPKVNk8kW48ZQ2l3O6LRBprM97ew
|
||||
4;297;663;1;LpkVlmq4b3jMwJQxBZ8akayrXAP6o97Ke0aOYEg2
|
||||
5;29C;668;3;lekrmdvO0BQG1ZW4AV8jeZ5M39xnN2wEbRgPjXLp
|
||||
6;2B1;689;3;q0rElBPdL6kxMAjnzVDRl95emNZY7oOv2wK9gb31
|
||||
7;2AB;683;3;XKQbz3WAwY21dGa0R453rWyJm9PZOjqlvpr6Nkeo
|
||||
8;2B0;688;3;PaGbQ3wBAZWOmRvK9VDpvz5endLJYopEqlkzNMxX
|
||||
9;2B9;697;3;joqRYBVL30k9eQWOlZ5qwpD2KJpNEmA6gPxXzwaM
|
||||
10;293;659;3;Ymn9oleRxJ0vw17WzAyGwdyEBk4ObdMXj2VgpNLG
|
||||
11;294;660;3;gj7zbKV46oQ1p2e0AJ8XqZDG3YNWaRrlOEXvBxmM
|
||||
12;295;661;3;laYK7Pomn2bNZXEpedDxAqyOJkQ3WwV49gqxLrAR
|
||||
13;2A0;672;2;0XYElWeKBNJn1gdoMG8lON5ALkPvj4V3xra2q6mO
|
||||
14;2B4;692;2;l9YkRpoB2vVa0mKqEO8ZGGDjW43eXnJML6GxzbwQ
|
||||
15;2B2;690;2;wGjQobgOK0n2YqBZmVDVR3DR9ep6EXA1ka3vzlP7
|
||||
16;27C;636;2;M6ogKQW09bOXewAYvZyvJqyJrV1aRnPGE37p42Nx
|
||||
17;27B;635;2;Vq2JaWpw1OdBKmNeoj8w605XE40l3kgL76Azb9QP
|
||||
18;2B6;694;2;Jm32GR1qpwQxlZza0N5mE15AP96YbOKLogrXVW4e
|
||||
19;2B5;693;2;KjbN4q7JPZmexgdnz2yKdn5YAWwO0Q3BMX6ERLoV
|
||||
20;2B3;691;1;lekrmdvO0BQG1ZW4AV8jzq8M39xnN2wEbRgPjXLp
|
||||
21;27F;639;3;BOjEzGRZ46bnp9wa2A8z76D0JkmW1QPNdrqevXVL
|
||||
22;27E;638;3;9xgzG4Op1BrKZPmoQkDrmj8E73ndJNMjavAwX2Re
|
||||
23;27D;637;3;koW06PeGrLlBp2YJQE5Ogw5RmMaXKzj3wOAZg9n7
|
||||
24;28F;655;2;RMgnK93rkoAazbqdQ4yBYpDZ1YXGx6pmwBeVEP2O
|
||||
25;288;648;2;gaMGN4x1e9JlZz0QPRDd9Rym6dVr3OpvqKnoWBbk
|
||||
26;298;664;1;oGVzxNWP9lrjaQ7vKODQ7g51gqp62YZREmdw3XBM
|
||||
27;29F;671;3;AvVdgzYJZaPx3oMqeED4Oj8NnmKkw716bRO90jLB
|
||||
28;280;640;2;WjBL12pg63eX4N9P7zy0XYyEJKmlbkGwZMx0avQV
|
||||
29;28B;651;2;qaAOzENGrvpbe0VoK7D6Ld519PZmdg3nl24JLQMk
|
||||
30;27A;634;2;NGWamnYqlP1wbgrZQxDAWm5e2X7OVAK69koR04vL
|
||||
31;29E;670;2;dlE1VQjYrNx9gZRmb38g1YyoLBO4qaAk2M6JPnG7
|
||||
32;281;641;2;vnmG4kJxaXWNBgMQq0D7Mz5e9oZzOAlr6LdR3w2V
|
||||
33;278;632;2;LpkVlmq4b3jMwJQxBZ8aM78rXAP6o97Ke0aOYEg2
|
||||
34;29D;669;3;Y9aLW03wOZkABvKXbMyL0lyV1xdNj72r4egqGRzJ
|
||||
35;2A8;680;1;KL2jNOVpdARa9XvoeJDPga8bkmPBxqn7Ww3gzGQ1
|
||||
36;2BA;698;1;mYnBzbeGaAL62jowRv59M35Xq9QpZ0K7O1dg4xVl
|
||||
37;29B;667;1;MzXBoWbEZjO0lrpqnRyoJ4DkmVeaNAGdL9g4QKxP
|
||||
38;289;649;1;0p2rwdP7aGoOQLJNgAynJNy6xWXbmMe3nvZqlzkV
|
||||
39;290;656;1;BrQx3NGKgVMRaXYAo9y1GE8ZzkWnj1le6bdOLE20
|
||||
40;2AA;682;1;vnreBJ6PMqgz20pYEL82XQyG1jkWwdQxZVNAOlmK
|
||||
41;285;645;1;jklN4JpQAx362o9XYZDN6wDgrWw1P7GEbdBM0vRV
|
||||
42;283;643;1;oZmYXEbw9lVWRv1jLxDe9bDdgAMz4PKQnNJ6eB23
|
||||
43;282;642;1;pEonaKBOGbj9034MgJ8W3G8qXvxNWVkAPQz21R6L
|
||||
44;287;647;1;BLQal6Pn9oz1KmNgek5Yqd50vd2MAbqG3OV7Rp4j
|
||||
45;286;646;1;4agVJ9dPQkmp1R2X3EDJKxyrK6ZlNoM0n7qxBOev
|
||||
46;29A;666;1;9PpgLEnvk4WMV6RmOJybMGDaeAXzo2BQNG3K17Zw
|
||||
47;28E;654;1;Mmp93b2nvd7OoqgBeEyEZq5kjlAV1Y4ZNXwW0zLG
|
||||
|
4169
flow/cmd_manager.js
Normal file
4169
flow/cmd_manager.js
Normal file
File diff suppressed because it is too large
Load diff
90
flow/code.js
Normal file
90
flow/code.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
exports.id = 'code';
|
||||
exports.title = 'Code';
|
||||
exports.group = 'Common';
|
||||
exports.color = '#656D78';
|
||||
exports.input = true;
|
||||
exports.output = 1;
|
||||
exports.author = 'Peter Širka';
|
||||
exports.icon = 'code';
|
||||
exports.version = '1.2.0';
|
||||
exports.options = { outputs: 1, code: 'send(0, value);', keepmessage: true };
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div data-jc="textbox" data-jc-path="outputs" data-jc-config="type:number;validation:value > 0;increment:true;maxlength:3">@(Number of outputs)</div>
|
||||
<div class="help m">@(Minimum is 1)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-jc="codemirror" data-jc-path="code" data-jc-config="type:javascript;required:true;height:500;tabs:true;trim:true" class="m">@(Code)</div>
|
||||
<div data-jc="checkbox" data-jc-path="keepmessage">@(Keep message instance)</div>
|
||||
</div>
|
||||
<script>
|
||||
var code_outputs_count;
|
||||
|
||||
ON('open.code', function(component, options) {
|
||||
code_outputs_count = options.outputs = options.outputs || 1;
|
||||
});
|
||||
|
||||
ON('save.code', function(component, options) {
|
||||
if (code_outputs_count !== options.outputs) {
|
||||
if (flow.version < 511) {
|
||||
component.connections = {};
|
||||
setState(MESSAGES.apply);
|
||||
}
|
||||
component.output = options.outputs || 1;
|
||||
}
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.readme = `# Code
|
||||
|
||||
This component executes custom JavaScript code as it is and it doesn't contain any secure scope.
|
||||
|
||||
\`\`\`javascript
|
||||
// value {Object} contains received data
|
||||
// send(outputIndex, newValue) sends a new value
|
||||
// error(value) sends an error
|
||||
// instance {Object} a current component instance
|
||||
// flowdata {Object} a current flowdata
|
||||
// repository {Object} a current repository of flowdata
|
||||
// Example:
|
||||
|
||||
// send() can be execute multiple times
|
||||
send(0, value);
|
||||
\`\`\``;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var fn;
|
||||
|
||||
instance.on('data', function(response) {
|
||||
if (fn) {
|
||||
try {
|
||||
fn(response.data, instance, response, instance.options, response.repository, require);
|
||||
} catch (e) {
|
||||
response.data = e;
|
||||
instance.throw(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
instance.reconfigure = function() {
|
||||
try {
|
||||
if (instance.options.code) {
|
||||
instance.status('');
|
||||
var code = 'var send = function(index, value) { if (options.keepmessage) { flowdata.data = value; instance.send2(index, flowdata); } else instance.send2(index, value);}; var error = function(err) { instance.throw(err); }; ' + instance.options.code;
|
||||
fn = new Function('value', 'instance', 'flowdata', 'options', 'repository', 'require', code);
|
||||
} else {
|
||||
instance.status('Not configured', 'red');
|
||||
fn = null;
|
||||
}
|
||||
} catch (e) {
|
||||
fn = null;
|
||||
instance.error('Code: ' + e.message);
|
||||
}
|
||||
};
|
||||
|
||||
instance.on('options', instance.reconfigure);
|
||||
instance.reconfigure();
|
||||
};
|
||||
11
flow/comment.js
Normal file
11
flow/comment.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
exports.id = 'comment';
|
||||
exports.title = 'Comment';
|
||||
exports.group = 'Common';
|
||||
exports.color = '#704cff';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.icon = 'comment';
|
||||
exports.traffic = false;
|
||||
exports.version = '1.0.0';
|
||||
exports.readme = '# Comment';
|
||||
|
||||
exports.install = function() {};
|
||||
175
flow/csv_import.js
Normal file
175
flow/csv_import.js
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
exports.id = 'csv_import';
|
||||
exports.title = 'CsvImport';
|
||||
exports.version = '1.0.0';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#2134B0';
|
||||
exports.input = 1;
|
||||
exports.output = ["red", "white"];
|
||||
exports.click = false;
|
||||
exports.author = 'Daniel Segeš';
|
||||
exports.icon = 'file-import';
|
||||
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">CSV Import</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.readme = `# load csv to table db`;
|
||||
|
||||
//config
|
||||
let delimiter = ";";
|
||||
let uniqueColumn = "node";
|
||||
let path = "flow/audit_test_panel.csv";
|
||||
let startFrom = 1;
|
||||
let table = "nodes";
|
||||
let mapImport = {
|
||||
2: "node",
|
||||
4: "tbname",
|
||||
3: "line"
|
||||
};
|
||||
|
||||
//10.0.0.62
|
||||
delimiter = ";";
|
||||
uniqueColumn = "node";
|
||||
path = "flow/audit_rvo14_lampy.csv";
|
||||
startFrom = 1;
|
||||
table = "nodes";
|
||||
mapImport = {
|
||||
1: "node",
|
||||
3: "tbname",
|
||||
2: "line"
|
||||
};
|
||||
|
||||
//notification
|
||||
delimiter = ";";
|
||||
uniqueColumn = undefined;
|
||||
path = "flow/notifikacie.csv";
|
||||
startFrom = 1;
|
||||
table = "notifications";
|
||||
mapImport = {
|
||||
0: "key",
|
||||
1: "weight",
|
||||
2: "en",
|
||||
3: "sk"
|
||||
};
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
//console.log("csv import installed");
|
||||
|
||||
instance.on("close", () => {
|
||||
|
||||
})
|
||||
|
||||
|
||||
instance.on("data", (flowdata) => {
|
||||
|
||||
instance.send(0, "start import");
|
||||
console.log("csv import", flowdata.data);
|
||||
|
||||
//{table: "nodes", startFrom: 1, delimiter: ";", uniqueColumn: "node", path: "flow/audit_rvo14_lampy.csv", mapImport: {1: "node", 3: "tbname", 2: "line"}}
|
||||
|
||||
|
||||
if(typeof flowdata.data === 'object')
|
||||
{
|
||||
console.log("*******************", flowdata.data);
|
||||
|
||||
if(!flowdata.data.hasOwnProperty("table"))
|
||||
{
|
||||
instance.send(0, "!!!!csv import - nedefinovana tabulka");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!flowdata.data.hasOwnProperty("uniqueColumn"))
|
||||
{
|
||||
//instance.send(0, "!!!!csv import - nedefinovane uniqueColumn");
|
||||
//return;
|
||||
}
|
||||
|
||||
if(!flowdata.data.hasOwnProperty("path"))
|
||||
{
|
||||
instance.send(0, "!!!!csv import - nedefinovana cesta k suboru");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!flowdata.data.hasOwnProperty("mapImport"))
|
||||
{
|
||||
instance.send(0, "!!!!csv import - nedefinovany mapImport");
|
||||
return;
|
||||
}
|
||||
|
||||
table = flowdata.data.table;
|
||||
uniqueColumn = flowdata.data.uniqueColumn;
|
||||
if(uniqueColumn === "") uniqueColumn = undefined;
|
||||
|
||||
path = flowdata.data.path;
|
||||
mapImport = flowdata.data.mapImport;
|
||||
|
||||
if(flowdata.data.hasOwnProperty("delimiter")) delimiter = flowdata.data.delimiter;
|
||||
if(flowdata.data.hasOwnProperty("startFrom")) startFrom = flowdata.data.startFrom;
|
||||
}
|
||||
|
||||
|
||||
var db = TABLE(table);
|
||||
db.clear();
|
||||
|
||||
let keys = Object.keys(mapImport);
|
||||
|
||||
try {
|
||||
const data = fs.readFileSync(path, 'utf8')
|
||||
|
||||
let lines = data.split("\n");
|
||||
|
||||
for(let i = startFrom; i < lines.length; i++)
|
||||
{
|
||||
let line = lines[i];
|
||||
if(line === "") continue;
|
||||
|
||||
let data = line.split(delimiter);
|
||||
if(data.length == 0) continue;
|
||||
|
||||
let insertData = {};
|
||||
|
||||
keys.map(function(key){
|
||||
let k = mapImport[key];
|
||||
|
||||
//console.log("importineg", i, key, k);
|
||||
|
||||
if(data[key] != undefined) insertData[k] = data[key].trim();
|
||||
else{
|
||||
console.log("undefined", key, data);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("insertData", insertData);
|
||||
|
||||
if(uniqueColumn != undefined)
|
||||
{
|
||||
db.insert(insertData, true).where(uniqueColumn, insertData[uniqueColumn]);
|
||||
}
|
||||
else
|
||||
{
|
||||
db.insert(insertData);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
console.log("csv import finished");
|
||||
instance.send(0, "csv import finished");
|
||||
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
instance.send(0, err);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
286
flow/db_connector.js
Normal file
286
flow/db_connector.js
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
exports.id = 'db_connector';
|
||||
exports.title = 'DbConnector';
|
||||
exports.version = '1.0.0';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#2134B0';
|
||||
exports.input = 1;
|
||||
exports.output = ["red", "white"];
|
||||
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">DbConnector</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.readme = `# read/write data to tables`;
|
||||
|
||||
const instanceSendTo = {
|
||||
debug: 0,
|
||||
http_response: 1,
|
||||
}
|
||||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||||
|
||||
function extractWhereParams(params)
|
||||
{
|
||||
let name = params[0];
|
||||
let operator = '=';
|
||||
let value = params[1];
|
||||
|
||||
if(params.length == 3)
|
||||
{
|
||||
operator = params[1];
|
||||
value = params[2];
|
||||
}
|
||||
|
||||
return {name: name, operator: operator, value: value};
|
||||
}
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
let refFlowdata = null;//holds reference to httprequest flowdata
|
||||
|
||||
instance.on("close", () => {
|
||||
|
||||
})
|
||||
|
||||
|
||||
instance.on("data", async function(flowdata) {
|
||||
|
||||
let params = flowdata.data.body;
|
||||
console.log("DbConnector", params);
|
||||
|
||||
refFlowdata = flowdata;
|
||||
|
||||
if(refFlowdata != undefined)
|
||||
{
|
||||
//make http response
|
||||
let responseObj = {};
|
||||
responseObj["type"] = "SUCESS";
|
||||
|
||||
try{
|
||||
|
||||
let table = params.table;
|
||||
let action = params.action;
|
||||
|
||||
|
||||
if(params.data != undefined)
|
||||
{
|
||||
let className = params.data.className;
|
||||
|
||||
if(className == "SqlQueryBuilder")
|
||||
{
|
||||
let type = params.data.type;
|
||||
|
||||
console.log("SqlQueryBuilder---->", params.data);
|
||||
|
||||
if(type == "SELECT")
|
||||
{
|
||||
let table = params.data.queryData.tables[0].table;
|
||||
|
||||
const db = TABLE(table);
|
||||
var builder = db.find();
|
||||
|
||||
let result = await promisifyBuilder(builder);
|
||||
|
||||
let response = {};
|
||||
response["result"] = result;
|
||||
response["success"] = true;
|
||||
|
||||
responseObj["data"] = response;
|
||||
|
||||
//console.log(responseObj);
|
||||
|
||||
refFlowdata.data = responseObj;
|
||||
instance.send(instanceSendTo.http_response, refFlowdata);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log("db_connector---->", table, action);
|
||||
|
||||
//actions: read, replace, insert, delete, update...
|
||||
|
||||
if(action == "read")
|
||||
{
|
||||
const db = TABLE(params.table);
|
||||
|
||||
//where builder.where('age', '<', 15);
|
||||
//builder.where('id', 3403);
|
||||
|
||||
var builder = db.find();
|
||||
|
||||
//https://docs.totaljs.com/latest/en.html#api~DatabaseBuilder~builder.where
|
||||
if(params.hasOwnProperty("where"))
|
||||
{
|
||||
//optionalCan contain "=", "<=", "<", ">=", ">".
|
||||
//Default value: '='
|
||||
|
||||
//1.["production_line", 1]
|
||||
//2. ["or", ["production_line", 1], ["production_line", 2], "end"]
|
||||
|
||||
if (Array.isArray(params.where)) {
|
||||
|
||||
let multipleConditions = false;
|
||||
|
||||
if(params.where[0] == "or") multipleConditions = true;
|
||||
if (Array.isArray(params.where[0])) multipleConditions = true;
|
||||
|
||||
if(multipleConditions)
|
||||
{
|
||||
|
||||
for(var i = 0; i < params.where.length; i++)
|
||||
{
|
||||
const item = params.where[i];
|
||||
|
||||
if(item === "or") builder.or();
|
||||
|
||||
if (Array.isArray(item))
|
||||
{
|
||||
const { name, operator, value } = extractWhereParams(item);
|
||||
builder.where(name, operator, value);
|
||||
}
|
||||
|
||||
if(item === "end") builder.end();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
const { name, operator, value } = extractWhereParams(params.where);
|
||||
builder.where(name, operator, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
if(params.where.length >=2 )
|
||||
{
|
||||
let name = params.where[0];
|
||||
let operator = '=';
|
||||
let value = params.where[1];
|
||||
|
||||
if(params.where.length == 3)
|
||||
{
|
||||
operator = params.where[1];
|
||||
value = params.where[2];
|
||||
}
|
||||
|
||||
builder.where(name, operator, value);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if(params.hasOwnProperty("between"))
|
||||
{
|
||||
builder.between(params.between[0], params.between[1], params.between[2]);
|
||||
}
|
||||
|
||||
let response = await promisifyBuilder(builder);
|
||||
responseObj["data"] = response;
|
||||
|
||||
//console.log(responseObj);
|
||||
|
||||
refFlowdata.data = responseObj;
|
||||
instance.send(instanceSendTo.http_response, refFlowdata);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(action == "delete")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(action == "update")
|
||||
{
|
||||
//! data receiving from terminal (params)
|
||||
// {
|
||||
// hostname: 'localhost',
|
||||
// table: 'settings',
|
||||
// action: 'update',
|
||||
// body: {
|
||||
// rvo_name: 'terrrr',
|
||||
// lang: 'en',
|
||||
// temperature_adress: '28.427B45920702',
|
||||
// latitude: 48.70826502,
|
||||
// longitude: 17.28455203,
|
||||
// mqtt_host: '192.168.252.4',
|
||||
// mqtt_clientid: 'showroom_test_panel_led',
|
||||
// mqtt_username: 'xmRd6RJxW53WZe4vMFLU',
|
||||
// mqtt_port: 1883,
|
||||
// maintanace_mode: false
|
||||
// }
|
||||
// }
|
||||
const tableToModify = TABLE(params.table);
|
||||
const newValues = params.body;
|
||||
|
||||
tableToModify.modify(newValues).make(function(builder) {
|
||||
|
||||
builder.callback(function(err, response) {
|
||||
|
||||
if(!err)
|
||||
{
|
||||
responseObj["data"] = response;
|
||||
responseObj["tableUpdated"] = true;
|
||||
refFlowdata.data = responseObj;
|
||||
instance.send(instanceSendTo.http_response, refFlowdata);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if(action == "replace")
|
||||
{
|
||||
//truncate table
|
||||
const db = TABLE(params.table);
|
||||
var builder = db.remove();
|
||||
db.clean();
|
||||
|
||||
//insert data
|
||||
let data = params.data;
|
||||
|
||||
for(let i = 0; i < data.length; i++)
|
||||
{
|
||||
//console.log(data[i]);
|
||||
db.insert(data[i]);
|
||||
}
|
||||
|
||||
console.log("insert done");
|
||||
|
||||
let responseObj = {};
|
||||
responseObj["type"] = "SUCESS";
|
||||
|
||||
refFlowdata.data = responseObj;
|
||||
instance.send(instanceSendTo.http_response, refFlowdata);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
//console.log(error);
|
||||
responseObj["type"] = "ERROR";
|
||||
responseObj["message"] = error;
|
||||
|
||||
refFlowdata.data = responseObj;
|
||||
instance.send(instanceSendTo.http_response, refFlowdata);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
100
flow/debug.js
Normal file
100
flow/debug.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
exports.id = 'debug';
|
||||
exports.title = 'Debug';
|
||||
exports.author = 'Peter Širka';
|
||||
exports.color = '#967ADC';
|
||||
exports.click = true;
|
||||
exports.input = true;
|
||||
exports.icon = 'bug';
|
||||
exports.version = '2.0.4';
|
||||
exports.options = { enabled: true, repository: false, type: 'data' };
|
||||
exports.readme = `# Debug
|
||||
|
||||
Prints data to the debug tab.`;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div data-jc="dropdown" data-jc-path="type" data-jc-config="items:Message data|data,Message repository|repository,Message data + Message repository|both;required:true" class="m">@(Output type)</div>
|
||||
<div data-jc="textbox" data-jc-path="property" data-jc-config="placeholder: @(e.g. address.street)" class="m">@(Path to the property (leave empty to show the whole data object))</div>
|
||||
<div data-jc="textbox" data-jc-path="group" data-jc-config="placeholder: @(e.g. Temperature)" class="m">@(A group name)</div>
|
||||
<div data-jc="checkbox" data-jc-path="enabled">@(Enabled)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
instance.on('data', function(response) {
|
||||
if (instance.options.enabled) {
|
||||
|
||||
var opt = instance.options;
|
||||
var rep = response.repository;
|
||||
var val = response.data;
|
||||
var id = response.id;
|
||||
|
||||
switch (instance.options.type){
|
||||
case 'both':
|
||||
var data = {};
|
||||
data.repository = rep;
|
||||
data.data = val instanceof Error ? { error: val.message, stack: val.stack } : val;
|
||||
instance.debug(safeparse(opt.property ? U.get(data, opt.property) : data), undefined, opt.group, id);
|
||||
break;
|
||||
case 'repository':
|
||||
instance.debug(safeparse(opt.property ? U.get(rep, opt.property) : rep), undefined, opt.group, id);
|
||||
break;
|
||||
case 'data':
|
||||
default:
|
||||
if (val instanceof Error)
|
||||
instance.debug({ error: val.message, stack: val.stack }, undefined, opt.group, id);
|
||||
else
|
||||
instance.debug(safeparse(opt.property ? U.get(val, opt.property) : val), undefined, opt.group, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
instance.on('click', function() {
|
||||
instance.options.enabled = !instance.options.enabled;
|
||||
instance.custom.status();
|
||||
instance.save();
|
||||
});
|
||||
|
||||
instance.on('options', function() {
|
||||
instance.custom.status();
|
||||
});
|
||||
|
||||
instance.custom.status = function() {
|
||||
instance.status(instance.options.enabled ? 'Enabled' : 'Disabled');
|
||||
};
|
||||
|
||||
instance.custom.status();
|
||||
|
||||
function safeparse(o) {
|
||||
|
||||
if (o instanceof Buffer)
|
||||
return o;
|
||||
|
||||
if (o === undefined)
|
||||
return 'undefined';
|
||||
|
||||
if (o === null)
|
||||
return 'null';
|
||||
|
||||
var cache = [];
|
||||
var str = JSON.stringify(o, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
cache = null;
|
||||
return JSON.parse(str);
|
||||
}
|
||||
};
|
||||
2172
flow/designer.json
Normal file
2172
flow/designer.json
Normal file
File diff suppressed because it is too large
Load diff
1516
flow/designer_deploy.json
Normal file
1516
flow/designer_deploy.json
Normal file
File diff suppressed because it is too large
Load diff
1764
flow/di_do_controller.js
Normal file
1764
flow/di_do_controller.js
Normal file
File diff suppressed because it is too large
Load diff
220
flow/gettemperature.js
Normal file
220
flow/gettemperature.js
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
exports.id = 'gettemperature';
|
||||
exports.title = 'Thermometer';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#5CB36D';
|
||||
exports.version = '1.0.2';
|
||||
exports.output = ["red", "white", "blue"];
|
||||
exports.author = 'Rastislav Kovac';
|
||||
exports.icon = 'thermometer-three-quarters';
|
||||
|
||||
exports.readme = `# Getting temperature values from RVO`;
|
||||
|
||||
const instanceSendTo = {
|
||||
debug: 0,
|
||||
tb: 1,
|
||||
di_do_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.js');
|
||||
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.js');
|
||||
|
||||
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);
|
||||
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: status});
|
||||
}
|
||||
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);
|
||||
|
||||
dataToTb = {
|
||||
[edgeName]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": {
|
||||
"temperature": Number(data.toFixed(2)),
|
||||
"status": "OK"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
instance.send(instanceSendTo.tb, dataToTb);
|
||||
instance.send(instanceSendTo.di_do_controller, {sender: "gettemperature", status: "OK"});
|
||||
|
||||
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.di_do_controller, {sender: "gettemperature", status: "NOK"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setTimeout(function(){
|
||||
start();
|
||||
}, 3000);
|
||||
|
||||
startRead = setInterval(start, timeoutMin * 1000 * 60);
|
||||
|
||||
};
|
||||
163
flow/helper/DataToTbHandler.js
Normal file
163
flow/helper/DataToTbHandler.js
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
class DataToTbHandler
|
||||
{
|
||||
constructor(index) {
|
||||
this.index = index;
|
||||
|
||||
this.previousValues = {};
|
||||
this.debug = false;
|
||||
this.messageCounter = 0;
|
||||
|
||||
this.sender = "";
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
console.log("----------------------------");
|
||||
console.log("previousValues", this.previousValues);
|
||||
console.log("----------------------------");
|
||||
}
|
||||
|
||||
setSender(sender)
|
||||
{
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
isEmptyObject( obj ) {
|
||||
for ( var name in obj ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sendToTb(dataToTb, instance)
|
||||
{
|
||||
|
||||
if(!FLOW.OMS_brokerready)
|
||||
{
|
||||
return dataToTb;
|
||||
}
|
||||
|
||||
let keys = Object.keys(dataToTb);
|
||||
|
||||
if(keys.length == 0)
|
||||
{
|
||||
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let tbname = keys[0];
|
||||
let ts;
|
||||
|
||||
let arrayOfValues = dataToTb[tbname];
|
||||
let arrayOfValuesToSend = [];
|
||||
|
||||
for(let i = 0; i < arrayOfValues.length; i++)
|
||||
{
|
||||
ts = arrayOfValues[i].ts;
|
||||
|
||||
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
||||
|
||||
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
||||
|
||||
//console.log("sendToTb------------>after", values);
|
||||
|
||||
if(!this.isEmptyObject(values))
|
||||
{
|
||||
arrayOfValuesToSend.push({ts: ts, values: values});
|
||||
}
|
||||
}
|
||||
|
||||
if(arrayOfValuesToSend.length == 0)
|
||||
{
|
||||
//if(this.debug) console.log("data not sent - empty array");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
let dataToTb = {
|
||||
[tbname]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": values
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
this.messageCounter++;
|
||||
|
||||
let dataToTbModified = {
|
||||
[tbname]: arrayOfValuesToSend
|
||||
}
|
||||
|
||||
//console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance);
|
||||
if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend);
|
||||
|
||||
instance.send(this.index, dataToTbModified);
|
||||
}
|
||||
|
||||
getDiffTimestamp(key)
|
||||
{
|
||||
let seconds = 60*60;//1h
|
||||
//seconds = 1;//for testing
|
||||
|
||||
//TODO set different value for given key!!!
|
||||
//if(key == "status") seconds = 2*60*60;//2h
|
||||
|
||||
let timestampDiffToRemoveKey = seconds*1000;
|
||||
|
||||
return timestampDiffToRemoveKey;
|
||||
}
|
||||
|
||||
prepareValuesForTb(tbname, timestamp, values)
|
||||
{
|
||||
let keys = Object.keys(values);
|
||||
if(!this.previousValues.hasOwnProperty(tbname))
|
||||
{
|
||||
this.previousValues[tbname] = {};
|
||||
}
|
||||
|
||||
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
||||
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
let value = values[key];
|
||||
|
||||
if(!this.previousValues[tbname].hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
||||
continue;
|
||||
}
|
||||
|
||||
if(this.previousValues[tbname][key].value === value)
|
||||
{
|
||||
let diff = timestamp - this.previousValues[tbname][key].ts;
|
||||
|
||||
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
||||
if(diff > timestampDiffToRemoveKey)
|
||||
{
|
||||
this.previousValues[tbname][key].ts = Date.now();
|
||||
//if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
delete values[key];
|
||||
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.previousValues[tbname][key].value = value;
|
||||
this.previousValues[tbname][key].ts = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DataToTbHandler;
|
||||
124
flow/helper/ErrorToServiceHandler.js
Normal file
124
flow/helper/ErrorToServiceHandler.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const { MD5 } = require('./md5.js');
|
||||
const { networkInterfaces } = require('os');
|
||||
|
||||
class ErrorToServiceHandler
|
||||
{
|
||||
constructor() {
|
||||
this.previousValues = {};
|
||||
|
||||
this.projects_id = undefined;
|
||||
this.message_type = "error_message";
|
||||
|
||||
const nets = networkInterfaces();
|
||||
this.ipAddresses = Object.create(null); // Or just '{}', an empty object
|
||||
|
||||
for (const name of Object.keys(nets)) {
|
||||
for (const net of nets[name]) {
|
||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||
if (net.family === 'IPv4' && !net.internal) {
|
||||
if (!this.ipAddresses[name]) {
|
||||
this.ipAddresses[name] = [];
|
||||
}
|
||||
this.ipAddresses[name].push(net.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(this.ipAddresses);
|
||||
|
||||
}
|
||||
|
||||
setProjectsId(projects_id)
|
||||
{
|
||||
this.projects_id = projects_id;
|
||||
}
|
||||
|
||||
processMessage(message, seconds, message_type)
|
||||
{
|
||||
if(Array.isArray(message)) message = message.join(', ');
|
||||
//keep in memory - default value is 1h
|
||||
if (seconds === undefined) seconds = 60*60;
|
||||
if(message_type) this.message_type = message_type;
|
||||
|
||||
let key = MD5(message);
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
if(!this.previousValues.hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||
}
|
||||
|
||||
let diff = (timestamp - this.previousValues[key].ts);
|
||||
if(diff < this.previousValues[key].duration*1000) return false;
|
||||
|
||||
this.previousValues[key].ts = timestamp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sendMessageToService(message, seconds, message_type)
|
||||
{
|
||||
|
||||
let f = this.processMessage(message, seconds, message_type);
|
||||
if(!f) return;
|
||||
|
||||
/*
|
||||
//-------------
|
||||
if(message_type == undefined) message_type = "error_message";
|
||||
if(Array.isArray(message)) message = message.join(', ');
|
||||
|
||||
let key = MD5(message);
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
//keep in memory
|
||||
if (seconds === undefined) seconds = 60*60;
|
||||
|
||||
if(!this.previousValues.hasOwnProperty(key))
|
||||
{
|
||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||
}
|
||||
|
||||
let diff = (timestamp - this.previousValues[key].ts);
|
||||
if(diff < this.previousValues[key].duration*1000) return;
|
||||
|
||||
this.previousValues[key].ts = timestamp;
|
||||
*/
|
||||
|
||||
//-------------------------
|
||||
|
||||
//send to service
|
||||
|
||||
let dataToInfoSender = {id: this.projects_id};
|
||||
|
||||
//js_error || error_message
|
||||
dataToInfoSender[this.message_type] = message;
|
||||
dataToInfoSender.ipAddresses = this.ipAddresses;
|
||||
|
||||
console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender);
|
||||
|
||||
//TODO UGLY!!!
|
||||
if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id;
|
||||
|
||||
/*
|
||||
if(this.projects_id === undefined)
|
||||
{
|
||||
console.log("this.projects_id is undefined");
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
RESTBuilder.make(function(builder) {
|
||||
builder.method('POST');
|
||||
builder.post(dataToInfoSender);
|
||||
builder.url('http://192.168.252.2:8004/sentmessage');
|
||||
|
||||
builder.callback(function(err, response, output) {
|
||||
console.log("process.on error send", err, response, output, dataToInfoSender);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ErrorToServiceHandler;
|
||||
44
flow/helper/db_helper.js
Normal file
44
flow/helper/db_helper.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
function promisifyBuilder(builder)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
try{
|
||||
|
||||
builder.callback(function(err, response) {
|
||||
|
||||
if(err != null) reject(err);
|
||||
resolve(response);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function makeMapFromDbResult(response, ...keys)
|
||||
{
|
||||
let s = "-";
|
||||
let data = {};
|
||||
|
||||
for(let i = 0; i < response.length; i++)
|
||||
{
|
||||
let record = response[i];
|
||||
|
||||
let k = [];
|
||||
for(let j = 0; j < keys.length; j++)
|
||||
{
|
||||
k.push( record[keys[j]] );
|
||||
}
|
||||
|
||||
let key = k.join(s);
|
||||
data[ key ] = record;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
promisifyBuilder,
|
||||
makeMapFromDbResult
|
||||
}
|
||||
72
flow/helper/error_reporter.js
Normal file
72
flow/helper/error_reporter.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
//key is device, value = str
|
||||
let sentValues= {};
|
||||
|
||||
function sendError(func, device, weight, str, extra, tb_output, instance, type) {
|
||||
// if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){
|
||||
// return; // Allow debug messages only if CONFIG.debug is active
|
||||
// }
|
||||
|
||||
let key = device;
|
||||
if(type != undefined) key = type;
|
||||
|
||||
if(sentValues.hasOwnProperty(key))
|
||||
{
|
||||
if(sentValues[key] == str)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sentValues[key] = str;
|
||||
|
||||
let content = {
|
||||
"type": weight,
|
||||
"status": "new",
|
||||
"source": {
|
||||
"func":func,
|
||||
"component":instance.id,
|
||||
"component_name":instance.name,
|
||||
"edge":device
|
||||
},
|
||||
"message":str,
|
||||
"message_data": extra
|
||||
};
|
||||
|
||||
let msg = {};
|
||||
msg[device] = [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": {
|
||||
"_event":content
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Msg can be outputted from components only after configuration
|
||||
/*if (canSendErrData()){
|
||||
sendBufferedErrors();
|
||||
} else {
|
||||
bufferError(msg);
|
||||
}*/
|
||||
instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||
|
||||
}
|
||||
|
||||
|
||||
let ERRWEIGHT = {
|
||||
EMERGENCY: "emergency", // System unusable
|
||||
ALERT: "alert", // Action must be taken immidiately
|
||||
CRITICAL: "critical", // Component unable to function
|
||||
ERROR: "error", // Error, but component able to recover from it
|
||||
WARNING: "warning", // Possibility of error, system running futher
|
||||
NOTICE: "notice", // Significant message but not an error, things user might want to know about
|
||||
INFO: "informational", // Info
|
||||
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
sendError,
|
||||
ERRWEIGHT
|
||||
}
|
||||
56
flow/helper/error_reporting.js
Normal file
56
flow/helper/error_reporting.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
const ERRWEIGHT = {
|
||||
EMERGENCY: "emergency", // System unusable
|
||||
ALERT: "alert", // Action must be taken immidiately
|
||||
CRITICAL: "critical", // Component unable to function
|
||||
ERROR: "error", // Error, but component able to recover from it
|
||||
WARNING: "warning", // Possibility of error, system running futher
|
||||
NOTICE: "notice", // Significant message but not an error, things user might want to know about
|
||||
INFO: "informational", // Info
|
||||
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function sendError(func, device, weight, str, extra){
|
||||
if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){
|
||||
return; // Allow debug messages only if CONFIG.debug is active
|
||||
}
|
||||
|
||||
let content = {
|
||||
"type": weight,
|
||||
"status": "new",
|
||||
"source": {
|
||||
"func":func,
|
||||
"component":instance.id,
|
||||
"component_name":instance.name,
|
||||
"edge":myEdge
|
||||
},
|
||||
"message":str,
|
||||
"message_data": extra
|
||||
};
|
||||
let msg = {};
|
||||
msg[device] = [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": {
|
||||
"_event":content
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Msg can be outputted from components only after configuration
|
||||
/*if (canSendErrData()){
|
||||
sendBufferedErrors();
|
||||
} else {
|
||||
bufferError(msg);
|
||||
}*/
|
||||
instance.send(0, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
sendError,
|
||||
ERRWEIGHT,
|
||||
}
|
||||
5
flow/helper/md5.js
Normal file
5
flow/helper/md5.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
function MD5(d){var r = M(V(Y(X(d),8*d.length)));return r.toLowerCase()};function M(d){for(var _,m="0123456789ABCDEF",f="",r=0;r<d.length;r++)_=d.charCodeAt(r),f+=m.charAt(_>>>4&15)+m.charAt(15&_);return f}function X(d){for(var _=Array(d.length>>2),m=0;m<_.length;m++)_[m]=0;for(m=0;m<8*d.length;m+=8)_[m>>5]|=(255&d.charCodeAt(m/8))<<m%32;return _}function V(d){for(var _="",m=0;m<32*d.length;m+=8)_+=String.fromCharCode(d[m>>5]>>>m%32&255);return _}function Y(d,_){d[_>>5]|=128<<_%32,d[14+(_+64>>>9<<4)]=_;for(var m=1732584193,f=-271733879,r=-1732584194,i=271733878,n=0;n<d.length;n+=16){var h=m,t=f,g=r,e=i;f=md5_ii(f=md5_ii(f=md5_ii(f=md5_ii(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_ff(f=md5_ff(f=md5_ff(f=md5_ff(f,r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+0],7,-680876936),f,r,d[n+1],12,-389564586),m,f,d[n+2],17,606105819),i,m,d[n+3],22,-1044525330),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+4],7,-176418897),f,r,d[n+5],12,1200080426),m,f,d[n+6],17,-1473231341),i,m,d[n+7],22,-45705983),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+8],7,1770035416),f,r,d[n+9],12,-1958414417),m,f,d[n+10],17,-42063),i,m,d[n+11],22,-1990404162),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+12],7,1804603682),f,r,d[n+13],12,-40341101),m,f,d[n+14],17,-1502002290),i,m,d[n+15],22,1236535329),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+1],5,-165796510),f,r,d[n+6],9,-1069501632),m,f,d[n+11],14,643717713),i,m,d[n+0],20,-373897302),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+5],5,-701558691),f,r,d[n+10],9,38016083),m,f,d[n+15],14,-660478335),i,m,d[n+4],20,-405537848),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+9],5,568446438),f,r,d[n+14],9,-1019803690),m,f,d[n+3],14,-187363961),i,m,d[n+8],20,1163531501),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+13],5,-1444681467),f,r,d[n+2],9,-51403784),m,f,d[n+7],14,1735328473),i,m,d[n+12],20,-1926607734),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+5],4,-378558),f,r,d[n+8],11,-2022574463),m,f,d[n+11],16,1839030562),i,m,d[n+14],23,-35309556),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+1],4,-1530992060),f,r,d[n+4],11,1272893353),m,f,d[n+7],16,-155497632),i,m,d[n+10],23,-1094730640),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+13],4,681279174),f,r,d[n+0],11,-358537222),m,f,d[n+3],16,-722521979),i,m,d[n+6],23,76029189),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+9],4,-640364487),f,r,d[n+12],11,-421815835),m,f,d[n+15],16,530742520),i,m,d[n+2],23,-995338651),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+0],6,-198630844),f,r,d[n+7],10,1126891415),m,f,d[n+14],15,-1416354905),i,m,d[n+5],21,-57434055),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+12],6,1700485571),f,r,d[n+3],10,-1894986606),m,f,d[n+10],15,-1051523),i,m,d[n+1],21,-2054922799),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+8],6,1873313359),f,r,d[n+15],10,-30611744),m,f,d[n+6],15,-1560198380),i,m,d[n+13],21,1309151649),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+4],6,-145523070),f,r,d[n+11],10,-1120210379),m,f,d[n+2],15,718787259),i,m,d[n+9],21,-343485551),m=safe_add(m,h),f=safe_add(f,t),r=safe_add(r,g),i=safe_add(i,e)}return Array(m,f,r,i)}function md5_cmn(d,_,m,f,r,i){return safe_add(bit_rol(safe_add(safe_add(_,d),safe_add(f,i)),r),m)}function md5_ff(d,_,m,f,r,i,n){return md5_cmn(_&m|~_&f,d,_,r,i,n)}function md5_gg(d,_,m,f,r,i,n){return md5_cmn(_&f|m&~f,d,_,r,i,n)}function md5_hh(d,_,m,f,r,i,n){return md5_cmn(_^m^f,d,_,r,i,n)}function md5_ii(d,_,m,f,r,i,n){return md5_cmn(m^(_|~f),d,_,r,i,n)}function safe_add(d,_){var m=(65535&d)+(65535&_);return(d>>16)+(_>>16)+(m>>16)<<16|65535&m}function bit_rol(d,_){return d<<_|d>>>32-_}
|
||||
|
||||
module.exports = {
|
||||
MD5
|
||||
}
|
||||
135
flow/helper/notification_reporter.js
Normal file
135
flow/helper/notification_reporter.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js');
|
||||
const dbNotifications = TABLE("notifications");
|
||||
|
||||
//key is device, value = str
|
||||
let sentValues= {};
|
||||
let notificationsData = {};
|
||||
|
||||
let ERRWEIGHT = {
|
||||
EMERGENCY: "emergency", // System unusable
|
||||
ALERT: "alert", // Action must be taken immidiately
|
||||
CRITICAL: "critical", // Component unable to function
|
||||
ERROR: "error", // Error, but component able to recover from it
|
||||
WARNING: "warning", // Possibility of error, system running futher
|
||||
NOTICE: "notice", // Significant message but not an error, things user might want to know about
|
||||
INFO: "informational", // Info
|
||||
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
|
||||
};
|
||||
|
||||
function getKey(map, val) {
|
||||
return Object.keys(map).findItem(key => map[key] === val);
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/41117799/string-interpolation-on-variable
|
||||
var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);
|
||||
|
||||
async function initNotifications()
|
||||
{
|
||||
let response = await promisifyBuilder(dbNotifications.find());
|
||||
notificationsData = makeMapFromDbResult(response, "key");
|
||||
|
||||
console.log("initNotifications done" );
|
||||
}
|
||||
|
||||
function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) {
|
||||
|
||||
let storeToSendValues = true;
|
||||
if(saveKey == undefined) storeToSendValues = false;
|
||||
|
||||
let lang = FLOW.OMS_language;
|
||||
if(lang != "en" || lang != "sk") lang = "en";
|
||||
|
||||
let tpl = key;
|
||||
let weight = "";
|
||||
|
||||
if(notificationsData[key])
|
||||
{
|
||||
weight = notificationsData[key].weight;
|
||||
weight = weight.toLowerCase();
|
||||
|
||||
tpl = notificationsData[key][lang];
|
||||
tpl = template(tpl, params);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
|
||||
return false;
|
||||
}
|
||||
|
||||
//detect invalid err weight
|
||||
if(getKey(ERRWEIGHT, weight) == undefined)
|
||||
{
|
||||
console.error("sendNotification: Notifications: undefined weight", weight, key, func);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sentValues.hasOwnProperty(saveKey))
|
||||
{
|
||||
if(sentValues[saveKey] == tpl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(sentValues[saveKey] == undefined)
|
||||
{
|
||||
if(storeToSendValues)
|
||||
{
|
||||
//do not send - flow is was started
|
||||
sentValues[saveKey] = tpl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(saveKey == "rvo_door")
|
||||
{
|
||||
//console.log("******", saveKey, sentValues[saveKey], tpl);
|
||||
}
|
||||
|
||||
if(storeToSendValues) sentValues[saveKey] = tpl;
|
||||
|
||||
let str = FLOW.OMS_rvo_name;
|
||||
if(str != "") str = str + ": ";
|
||||
str = str + tpl;
|
||||
|
||||
let content = {
|
||||
"type": weight,
|
||||
"status": "new",
|
||||
"source": {
|
||||
"func":func,
|
||||
"component":instance.id,
|
||||
"component_name":instance.name,
|
||||
"edge":device
|
||||
},
|
||||
"message":str,
|
||||
"message_data": extra
|
||||
};
|
||||
|
||||
let msg = {};
|
||||
msg[device] = [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": {
|
||||
"_event":content
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Msg can be outputted from components only after configuration
|
||||
/*if (canSendErrData()){
|
||||
sendBufferedErrors();
|
||||
} else {
|
||||
bufferError(msg);
|
||||
}*/
|
||||
instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendNotification,
|
||||
initNotifications,
|
||||
ERRWEIGHT
|
||||
}
|
||||
144
flow/helper/register.js
Normal file
144
flow/helper/register.js
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
0 - cislo registra / prikaz
|
||||
1 - recepient - 0 = master, 1 = slave (slave je automaticky group a broadcast)
|
||||
2 - r/rw - read/write
|
||||
3- register name - nazov registra / prikazu (len pre info - zobrazenie v aplikacii)
|
||||
|
||||
4,5,6,7, - jednotlive byte-y - nazov byte-u
|
||||
4-7 - RES. a prazdny string "" sa nezobrazia!!!
|
||||
*/
|
||||
|
||||
//124 zaznamov
|
||||
const registers = [
|
||||
["0","1","R","Status","","",""],
|
||||
["1","1","RW","Dimming","R-Channel ","G-Channel","B-Channel ","W - Channel"],
|
||||
["2","1","R","Device types","","",".",""],
|
||||
["3","1","RW","Group addresses 1-4","Groups Add. 4","Groups Add. 3","Groups Add. 2","Groups Add. 1"],
|
||||
["4","1","RW","Group addresses 5-8","Groups Add. 8","Groups Add. 7","Groups Add. 6","Groups Add. 5"],
|
||||
["5","1","RW","Serial number (MAC)","","","",""],
|
||||
["6","1","RW","Time of dusk","HH","MM","SS","EXTRA"],
|
||||
["7","1","RW","Time of dawn","HH","MM","SS","EXTRA"],
|
||||
["8","1","RW","Time Schedule settings","TBD","TBD","Movement sensor","Time Schedule"],
|
||||
["9","1","RW","TS1 Time point 1","HH","MM","SS","Ext. Device"],
|
||||
["10","1","RW","TS1 Time point 1 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["11","1","RW","TS1 Time point 2","HH","MM","SS","Ext. Device"],
|
||||
["12","1","RW","TS1 Time point 2 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["13","1","RW","TS1 Time point 3","HH","MM","SS","Ext. Device"],
|
||||
["14","1","RW","TS1 Time point 3 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["15","1","RW","TS1 Time point 4","HH","MM","SS","Ext. Device"],
|
||||
["16","1","RW","TS1 Time point 4 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["17","1","RW","TS1 Time point 5","HH","MM","SS","Ext. Device"],
|
||||
["18","1","RW","TS1 Time point 5 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["19","1","RW","TS1 Time point 6","HH","MM","SS","Ext. Device"],
|
||||
["20","1","RW","TS1 Time point 6 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["21","1","RW","TS1 Time point 7","HH","MM","SS","Ext. Device"],
|
||||
["22","1","RW","TS1 Time point 7 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["23","1","RW","TS1 Time point 8","HH","MM","SS","Ext. Device"],
|
||||
["24","1","RW","TS1 Time point 8 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["25","1","RW","TS1 Time point 9","HH","MM","SS","Ext. Device"],
|
||||
["26","1","RW","TS1 Time point 9 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["27","1","RW","TS1 Time point 10","HH","MM","SS","Ext. Device"],
|
||||
["28","1","RW","TS1 Time point 10 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["29","1","RW","TS1 Time point 11","HH","MM","SS","Ext. Device"],
|
||||
["30","1","RW","TS1 Time point 11 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["31","1","RW","TS1 Time point 12","HH","MM","SS","Ext. Device"],
|
||||
["32","1","RW","TS1 Time point 12 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["33","1","RW","TS1 Time point 13","HH","MM","SS","Ext. Device"],
|
||||
["34","1","RW","TS1 Time point 13 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["35","1","RW","TS1 Time point 14","HH","MM","SS","Ext. Device"],
|
||||
["36","1","RW","TS1 Time point 14 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["37","1","RW","TS1 Time point 15","HH","MM","SS","Ext. Device"],
|
||||
["38","1","RW","TS1 Time point 15 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["39","1","RW","TS1 Time point 16","HH","MM","SS","Ext. Device"],
|
||||
["40","1","RW","TS1 Time point 16 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["41","1","RW","TS2 Time point 1","HH","MM","SS","Ext. Device"],
|
||||
["42","1","RW","TS2 Time point 1 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["43","1","RW","TS2 Time point 2","HH","MM","SS","Ext. Device"],
|
||||
["44","1","RW","TS2 Time point 2 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["45","1","RW","TS2 Time point 3","HH","MM","SS","Ext. Device"],
|
||||
["46","1","RW","TS2 Time point 3 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["47","1","RW","TS2 Time point 4","HH","MM","SS","Ext. Device"],
|
||||
["48","1","RW","TS2 Time point 4 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["49","1","RW","TS2 Time point 5","HH","MM","SS","Ext. Device"],
|
||||
["50","1","RW","TS2 Time point 5 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["51","1","RW","TS2 Time point 6","HH","MM","SS","Ext. Device"],
|
||||
["52","1","RW","TS2 Time point 6 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["53","1","RW","TS2 Time point 7","HH","MM","SS","Ext. Device"],
|
||||
["54","1","RW","TS2 Time point 7 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["55","1","RW","TS2 Time point 8","HH","MM","SS","Ext. Device"],
|
||||
["56","1","RW","TS2 Time point 8 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["57","1","RW","TS2 Time point 9","HH","MM","SS","Ext. Device"],
|
||||
["58","1","RW","TS2 Time point 9 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["59","1","RW","TS2 Time point 10","HH","MM","SS","Ext. Device"],
|
||||
["60","1","RW","TS2 Time point 10 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["61","1","RW","TS2 Time point 11","HH","MM","SS","Ext. Device"],
|
||||
["62","1","RW","TS2 Time point 11 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["63","1","RW","TS2 Time point 12","HH","MM","SS","Ext. Device"],
|
||||
["64","1","RW","TS2 Time point 12 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["65","1","RW","TS2 Time point 13","HH","MM","SS","Ext. Device"],
|
||||
["66","1","RW","TS2 Time point 13 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["67","1","RW","TS2 Time point 14","HH","MM","SS","Ext. Device"],
|
||||
["68","1","RW","TS2 Time point 14 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["69","1","RW","TS2 Time point 15","HH","MM","SS","Ext. Device"],
|
||||
["70","1","RW","TS2 Time point 15 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["71","1","RW","TS2 Time point 16","HH","MM","SS","Ext. Device"],
|
||||
["72","1","RW","TS2 Time point 16 Levels","R-Channel","G-Channel","B-Channel","W - Channel"],
|
||||
["73","1","RW","Power meter status","TBD","","",""],
|
||||
["74","1","R","Input Voltage","","","",""],
|
||||
["75","1","R","Input Current","","","",""],
|
||||
["76","1","R","Input Power","","","",""],
|
||||
["77","1","R","Cos phi","","","",""],
|
||||
["78","1","R","Frequency","","","",""],
|
||||
["79","1","RW","Energy","","","",""],
|
||||
["80","1","RW","Lifetime","","","",""],
|
||||
["81","1","RW","Power on cycles (input)","","","",""],
|
||||
["82","1","RW","Power on cycles (relay)","","","",""],
|
||||
["83","1","R","Time since last power on","","","",""],
|
||||
["84","1","R","Accelerometer data","","","",""],
|
||||
["85","1","RW","GPS latitude","pos/neg","deg","min ","sec"],
|
||||
["86","1","RW","GPS longitude","pos/neg","deg","min ","sec"],
|
||||
["87","1","RW","Actual time","HH","MM","SS","RES."],
|
||||
["88","1","RW","Actual date","Day of week","Day","Month","Year"],
|
||||
["89","1","R","Production data 1","","","",""],
|
||||
["90","1","R","Production data 2","","","",""],
|
||||
["91","1","RW","Network ID","NID3","NID2","NID1","NID0"],
|
||||
["95","1","RW","Actual Lux level from cabinet","RES.","RES.","HB","LB"],
|
||||
["96","1","RW","Threshold lux level","Dusk HB","Dusk LB","Dawn HB","Dawn LB"],
|
||||
["97","1","RW","Adjust period","Dusk HB","Dusk LB","Dawn HB","Dawn LB"],
|
||||
["98","1","RW","Offset","RES.","RES.","Dusk","Dawn"],
|
||||
["99","1","RW","CCT min/max range","max-H","max-L","min-H","min-L"],
|
||||
["100","1","RW","DALI interface","Cmd ID","Add","Cmd","Resp"],
|
||||
["101","1","RW","Module FW ver","v1","v2","v3","v4"],
|
||||
["102","1","RW","Module MAC-H","unused","unused","M1","M2"],
|
||||
["103","1","RW","Module MAC-L","M3","M4","M5","M6"],
|
||||
["122","1","R","FW update status/control register","Byte3","Byte2","Byte1","Byte0"],
|
||||
["123","1","R","FW update - data index","Byte3","Byte2","Byte1","Byte0"],
|
||||
["124","1","R","FW update - data","Byte3","Byte2","Byte1","Byte0"],
|
||||
["0","0","R","Status","","","",""],
|
||||
["1","0","RW","Control register","RES.","RES.","RES.","init mode enable"],
|
||||
["2","0","R","Device types","","","",""],
|
||||
["3","0","R","Serial number (MAC)","","","",""],
|
||||
["4","0","R","Production data 1","","","",""],
|
||||
["5","0","R","Production data 2","","","",""],
|
||||
["6","0","RW","Network ID","NID3","NID2","NID1","NID0"],
|
||||
["7","0","RW","RS232 settings","param.","param.","Baudrate H","Baudrate L"],
|
||||
["8","0","R","Accelerometer data","","","",""],
|
||||
["9","0","RW","Module FW ver","v1","v2","v3","v4"],
|
||||
["10","0","RW","Module MAC-H","unused","unused","M1","M2"],
|
||||
["11","0","RW","Module MAC-L","M3","M4","M5","M6"],
|
||||
["32","0","RW","FW update status/control register","Byte3","Byte2","Byte1","Byte0"],
|
||||
["33","0","RW","FW update - data index","Byte3","Byte2","Byte1","Byte0"],
|
||||
["34","0","RW","FW update - data","Byte3","Byte2","Byte1","Byte0"],
|
||||
["125","0","RW","Debug Register","Byte3","Byte2","Byte1","Byte0"],
|
||||
["126","0","RW","Network Control Register","Byte3","Byte2","Byte1","Byte0"],
|
||||
["127","0","R","Network Status Register","Byte3","Byte2","Byte1","Byte0"],
|
||||
["128","0","RW","Node XX Serial Number Register","SER3","SER2","SER1","SER0"],
|
||||
["256","0","R","Node XX Network Status Register","","","",""]
|
||||
];
|
||||
|
||||
let register = {};
|
||||
|
||||
module.exports = {
|
||||
registers,
|
||||
register
|
||||
}
|
||||
94
flow/helper/serialport_helper.js
Normal file
94
flow/helper/serialport_helper.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
const { exec } = require('child_process');
|
||||
|
||||
function openPort(port){
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
var callbackError = function(err) {
|
||||
port.removeListener('error', callbackError);
|
||||
port.removeListener('open', callbackError);
|
||||
|
||||
reject(err.message);
|
||||
};
|
||||
|
||||
var callbackOpen = function(data) {
|
||||
port.removeListener('error', callbackError);
|
||||
port.removeListener('open', callbackOpen);
|
||||
|
||||
resolve("port open: ok");
|
||||
};
|
||||
|
||||
port.on('error', callbackError);
|
||||
port.on('open', callbackOpen);
|
||||
|
||||
port.open();
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
function runSyncExec(command){
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if(error == null) resolve(stdout);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
async function writeData(port, data, readbytes, timeout){
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
//readbytes = 0 = broadcast
|
||||
if(readbytes == undefined) readbytes = 0;
|
||||
if(timeout == undefined) timeout = 10000;//10s, default timeout MASTERA je 3s
|
||||
|
||||
//cmd-manager mame http route POST / terminal a tomu sa tiez nastavuje timeout!!!
|
||||
|
||||
var callback = function(data) {
|
||||
rsPortReceivedData.push(...data);
|
||||
let l = rsPortReceivedData.length;
|
||||
|
||||
if(l >= readbytes)
|
||||
{
|
||||
port.removeListener('data', callback);
|
||||
|
||||
clearTimeout(t);
|
||||
resolve(rsPortReceivedData);
|
||||
}
|
||||
};
|
||||
|
||||
port.removeListener('data', callback);
|
||||
|
||||
let t = setTimeout(() => {
|
||||
port.removeListener('data', callback);
|
||||
|
||||
console.log("serialport helper: writeData TIMEOUT READING", rsPortReceivedData);
|
||||
|
||||
reject("TIMEOUT READING");
|
||||
}, timeout);
|
||||
|
||||
let rsPortReceivedData = [];
|
||||
|
||||
if(readbytes > 0) port.on('data', callback);
|
||||
|
||||
port.write(Buffer.from(data), function(err) {
|
||||
if (err) {
|
||||
port.removeListener('data', callback);
|
||||
reject(err.message);
|
||||
}
|
||||
|
||||
if(readbytes == 0)
|
||||
{
|
||||
resolve(rsPortReceivedData);
|
||||
}
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
openPort,
|
||||
runSyncExec,
|
||||
writeData
|
||||
}
|
||||
317
flow/helper/suncalc.js
Normal file
317
flow/helper/suncalc.js
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
(c) 2011-2015, Vladimir Agafonkin
|
||||
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
|
||||
https://github.com/mourner/suncalc
|
||||
*/
|
||||
|
||||
(function () { 'use strict';
|
||||
|
||||
// shortcuts for easier to read formulas
|
||||
|
||||
var PI = Math.PI,
|
||||
sin = Math.sin,
|
||||
cos = Math.cos,
|
||||
tan = Math.tan,
|
||||
asin = Math.asin,
|
||||
atan = Math.atan2,
|
||||
acos = Math.acos,
|
||||
rad = PI / 180;
|
||||
|
||||
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
|
||||
|
||||
|
||||
// date/time constants and conversions
|
||||
|
||||
var dayMs = 1000 * 60 * 60 * 24,
|
||||
J1970 = 2440588,
|
||||
J2000 = 2451545;
|
||||
|
||||
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
|
||||
function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); }
|
||||
function toDays(date) { return toJulian(date) - J2000; }
|
||||
|
||||
|
||||
// general calculations for position
|
||||
|
||||
var e = rad * 23.4397; // obliquity of the Earth
|
||||
|
||||
function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
|
||||
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
|
||||
|
||||
function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
|
||||
function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
|
||||
|
||||
function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
|
||||
|
||||
function astroRefraction(h) {
|
||||
if (h < 0) // the following formula works for positive altitudes only.
|
||||
h = 0; // if h = -0.08901179 a div/0 would occur.
|
||||
|
||||
// formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
||||
// 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
|
||||
return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
|
||||
}
|
||||
|
||||
// general sun calculations
|
||||
|
||||
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
|
||||
|
||||
function eclipticLongitude(M) {
|
||||
|
||||
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
|
||||
P = rad * 102.9372; // perihelion of the Earth
|
||||
|
||||
return M + C + P + PI;
|
||||
}
|
||||
|
||||
function sunCoords(d) {
|
||||
|
||||
var M = solarMeanAnomaly(d),
|
||||
L = eclipticLongitude(M);
|
||||
|
||||
return {
|
||||
dec: declination(L, 0),
|
||||
ra: rightAscension(L, 0)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var SunCalc = {};
|
||||
|
||||
|
||||
// calculates sun position for a given date and latitude/longitude
|
||||
|
||||
SunCalc.getPosition = function (date, lat, lng) {
|
||||
|
||||
var lw = rad * -lng,
|
||||
phi = rad * lat,
|
||||
d = toDays(date),
|
||||
|
||||
c = sunCoords(d),
|
||||
H = siderealTime(d, lw) - c.ra;
|
||||
|
||||
return {
|
||||
azimuth: azimuth(H, phi, c.dec),
|
||||
altitude: altitude(H, phi, c.dec)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// sun times configuration (angle, morning name, evening name)
|
||||
|
||||
var times = SunCalc.times = [
|
||||
[-0.833, 'sunrise', 'sunset' ],
|
||||
[ -0.3, 'sunriseEnd', 'sunsetStart' ],
|
||||
[ -6, 'dawn', 'dusk' ],
|
||||
[ -12, 'nauticalDawn', 'nauticalDusk'],
|
||||
[ -18, 'nightEnd', 'night' ],
|
||||
[ 6, 'goldenHourEnd', 'goldenHour' ]
|
||||
];
|
||||
|
||||
// adds a custom time to the times config
|
||||
|
||||
SunCalc.addTime = function (angle, riseName, setName) {
|
||||
times.push([angle, riseName, setName]);
|
||||
};
|
||||
|
||||
|
||||
// calculations for sun times
|
||||
|
||||
var J0 = 0.0009;
|
||||
|
||||
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
|
||||
|
||||
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
|
||||
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
|
||||
|
||||
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
|
||||
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
|
||||
|
||||
// returns set time for the given sun altitude
|
||||
function getSetJ(h, lw, phi, dec, n, M, L) {
|
||||
|
||||
var w = hourAngle(h, phi, dec),
|
||||
a = approxTransit(w, lw, n);
|
||||
return solarTransitJ(a, M, L);
|
||||
}
|
||||
|
||||
|
||||
// calculates sun times for a given date, latitude/longitude, and, optionally,
|
||||
// the observer height (in meters) relative to the horizon
|
||||
|
||||
SunCalc.getTimes = function (date, lat, lng, height) {
|
||||
|
||||
height = height || 0;
|
||||
|
||||
var lw = rad * -lng,
|
||||
phi = rad * lat,
|
||||
|
||||
dh = observerAngle(height),
|
||||
|
||||
d = toDays(date),
|
||||
n = julianCycle(d, lw),
|
||||
ds = approxTransit(0, lw, n),
|
||||
|
||||
M = solarMeanAnomaly(ds),
|
||||
L = eclipticLongitude(M),
|
||||
dec = declination(L, 0),
|
||||
|
||||
Jnoon = solarTransitJ(ds, M, L),
|
||||
|
||||
i, len, time, h0, Jset, Jrise;
|
||||
|
||||
|
||||
var result = {
|
||||
solarNoon: fromJulian(Jnoon),
|
||||
nadir: fromJulian(Jnoon - 0.5)
|
||||
};
|
||||
|
||||
for (i = 0, len = times.length; i < len; i += 1) {
|
||||
time = times[i];
|
||||
h0 = (time[0] + dh) * rad;
|
||||
|
||||
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
|
||||
Jrise = Jnoon - (Jset - Jnoon);
|
||||
|
||||
result[time[1]] = fromJulian(Jrise);
|
||||
result[time[2]] = fromJulian(Jset);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
|
||||
|
||||
function moonCoords(d) { // geocentric ecliptic coordinates of the moon
|
||||
|
||||
var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
|
||||
M = rad * (134.963 + 13.064993 * d), // mean anomaly
|
||||
F = rad * (93.272 + 13.229350 * d), // mean distance
|
||||
|
||||
l = L + rad * 6.289 * sin(M), // longitude
|
||||
b = rad * 5.128 * sin(F), // latitude
|
||||
dt = 385001 - 20905 * cos(M); // distance to the moon in km
|
||||
|
||||
return {
|
||||
ra: rightAscension(l, b),
|
||||
dec: declination(l, b),
|
||||
dist: dt
|
||||
};
|
||||
}
|
||||
|
||||
SunCalc.getMoonPosition = function (date, lat, lng) {
|
||||
|
||||
var lw = rad * -lng,
|
||||
phi = rad * lat,
|
||||
d = toDays(date),
|
||||
|
||||
c = moonCoords(d),
|
||||
H = siderealTime(d, lw) - c.ra,
|
||||
h = altitude(H, phi, c.dec),
|
||||
// formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
||||
pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
|
||||
|
||||
h = h + astroRefraction(h); // altitude correction for refraction
|
||||
|
||||
return {
|
||||
azimuth: azimuth(H, phi, c.dec),
|
||||
altitude: h,
|
||||
distance: c.dist,
|
||||
parallacticAngle: pa
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// calculations for illumination parameters of the moon,
|
||||
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
|
||||
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
|
||||
|
||||
SunCalc.getMoonIllumination = function (date) {
|
||||
|
||||
var d = toDays(date || new Date()),
|
||||
s = sunCoords(d),
|
||||
m = moonCoords(d),
|
||||
|
||||
sdist = 149598000, // distance from Earth to Sun in km
|
||||
|
||||
phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
|
||||
inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
|
||||
angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
|
||||
cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
|
||||
|
||||
return {
|
||||
fraction: (1 + cos(inc)) / 2,
|
||||
phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
|
||||
angle: angle
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
function hoursLater(date, h) {
|
||||
return new Date(date.valueOf() + h * dayMs / 24);
|
||||
}
|
||||
|
||||
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
|
||||
|
||||
SunCalc.getMoonTimes = function (date, lat, lng, inUTC) {
|
||||
var t = new Date(date);
|
||||
if (inUTC) t.setUTCHours(0, 0, 0, 0);
|
||||
else t.setHours(0, 0, 0, 0);
|
||||
|
||||
var hc = 0.133 * rad,
|
||||
h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
|
||||
h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
|
||||
|
||||
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
|
||||
for (var i = 1; i <= 24; i += 2) {
|
||||
h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
|
||||
h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
|
||||
|
||||
a = (h0 + h2) / 2 - h1;
|
||||
b = (h2 - h0) / 2;
|
||||
xe = -b / (2 * a);
|
||||
ye = (a * xe + b) * xe + h1;
|
||||
d = b * b - 4 * a * h1;
|
||||
roots = 0;
|
||||
|
||||
if (d >= 0) {
|
||||
dx = Math.sqrt(d) / (Math.abs(a) * 2);
|
||||
x1 = xe - dx;
|
||||
x2 = xe + dx;
|
||||
if (Math.abs(x1) <= 1) roots++;
|
||||
if (Math.abs(x2) <= 1) roots++;
|
||||
if (x1 < -1) x1 = x2;
|
||||
}
|
||||
|
||||
if (roots === 1) {
|
||||
if (h0 < 0) rise = i + x1;
|
||||
else set = i + x1;
|
||||
|
||||
} else if (roots === 2) {
|
||||
rise = i + (ye < 0 ? x2 : x1);
|
||||
set = i + (ye < 0 ? x1 : x2);
|
||||
}
|
||||
|
||||
if (rise && set) break;
|
||||
|
||||
h0 = h2;
|
||||
}
|
||||
|
||||
var result = {};
|
||||
|
||||
if (rise) result.rise = hoursLater(t, rise);
|
||||
if (set) result.set = hoursLater(t, set);
|
||||
|
||||
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
// export as Node module / AMD module / browser variable
|
||||
if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc;
|
||||
else if (typeof define === 'function' && define.amd) define(SunCalc);
|
||||
else window.SunCalc = SunCalc;
|
||||
|
||||
}());
|
||||
124
flow/helper/utils.js
Normal file
124
flow/helper/utils.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
function bytesToInt(bytes, numberOfBytes)
|
||||
{
|
||||
let buffer = [];
|
||||
if(Array.isArray(bytes))
|
||||
{
|
||||
buffer = bytes.slice(0);
|
||||
if(numberOfBytes != undefined)
|
||||
{
|
||||
buffer = bytes.slice(bytes.length - numberOfBytes);
|
||||
}
|
||||
}
|
||||
else buffer.push(bytes);
|
||||
|
||||
//var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
|
||||
let l = (buffer.length - 1) * 8;
|
||||
|
||||
let decimal = 0;
|
||||
for(let i = 0; i < buffer.length; i++)
|
||||
{
|
||||
var s = buffer[i] << l;
|
||||
if(l < 8) s = buffer[i]
|
||||
decimal = decimal + s;
|
||||
|
||||
l = l - 8;
|
||||
|
||||
}
|
||||
|
||||
return decimal;
|
||||
}
|
||||
|
||||
function resizeArray(arr, newSize, defaultValue) {
|
||||
while(newSize > arr.length)
|
||||
arr.push(defaultValue);
|
||||
arr.length = newSize;
|
||||
}
|
||||
|
||||
longToByteArray = function(/*long*/long) {
|
||||
// we want to represent the input as a 8-bytes array
|
||||
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
for ( var index = 0; index < byteArray.length; index ++ ) {
|
||||
var byte = long & 0xff;
|
||||
byteArray [ index ] = byte;
|
||||
long = (long - byte) / 256 ;
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
};
|
||||
|
||||
function addDays(date, days) {
|
||||
var result = new Date(date);
|
||||
result.setDate(result.getDate() + days);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
sleep(2000).then(() => {
|
||||
// Do something after the sleep!
|
||||
|
||||
|
||||
});
|
||||
*/
|
||||
|
||||
function sleep (time) {
|
||||
return new Promise((resolve) => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
function isEmptyObject( obj ) {
|
||||
for ( var name in obj ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function convertUTCDateToLocalDate(date) {
|
||||
var newDate = new Date(date);
|
||||
newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());
|
||||
return newDate;
|
||||
}
|
||||
|
||||
function addZeroBefore(n) {
|
||||
return (n < 10 ? '0' : '') + n;
|
||||
}
|
||||
|
||||
var convertBase = function () {
|
||||
|
||||
function convertBase(baseFrom, baseTo) {
|
||||
return function (num) {
|
||||
return parseInt(num, baseFrom).toString(baseTo);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// binary to decimal
|
||||
convertBase.bin2dec = convertBase(2, 10);
|
||||
|
||||
// binary to hexadecimal
|
||||
convertBase.bin2hex = convertBase(2, 16);
|
||||
|
||||
// decimal to binary
|
||||
convertBase.dec2bin = convertBase(10, 2);
|
||||
|
||||
// decimal to hexadecimal
|
||||
convertBase.dec2hex = convertBase(10, 16);
|
||||
|
||||
// hexadecimal to binary
|
||||
convertBase.hex2bin = convertBase(16, 2);
|
||||
|
||||
// hexadecimal to decimal
|
||||
convertBase.hex2dec = convertBase(16, 10);
|
||||
|
||||
return convertBase;
|
||||
}();
|
||||
|
||||
module.exports = {
|
||||
bytesToInt,
|
||||
longToByteArray,
|
||||
addDays,
|
||||
addZeroBefore,
|
||||
resizeArray,
|
||||
isEmptyObject,
|
||||
sleep,
|
||||
convertUTCDateToLocalDate
|
||||
}
|
||||
137
flow/httprequest.js
Normal file
137
flow/httprequest.js
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
exports.id = 'httprequest';
|
||||
exports.title = 'HTTP Request';
|
||||
exports.group = 'HTTP';
|
||||
exports.color = '#5D9CEC';
|
||||
exports.input = true;
|
||||
exports.version = '2.0.1';
|
||||
exports.output = 1;
|
||||
exports.author = 'Peter Širka';
|
||||
exports.icon = 'cloud-upload';
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="textbox" data-jc-path="url" class="m" data-jc-config="required:true;maxlength:500;placeholder:@(E.g. https\\://www.totaljs.com)">@(URL address)</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 m">
|
||||
<div data-jc="dropdown" data-jc-path="method" data-jc-config="required:true;items:,GET,POST,PUT,DELETE">@(HTTP method)</div>
|
||||
</div>
|
||||
<div class="col-md-6 m">
|
||||
<div data-jc="dropdown" data-jc-path="stringify" data-jc-config="required:true;items:,URL encoded|encoded,JSON|json,RAW|raw,None|none">@(Serialization)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-jc="checkbox" data-jc-path="chunks">@(Download the content <b>in chunks</b>)</div>
|
||||
<div data-jc="checkbox" data-jc-path="persistentcookies">@(Keep persistent cookies)</div>
|
||||
<div data-jc="checkbox" data-jc-path="nodns">@(Disable DNS cache)</div>
|
||||
<div data-jc="checkbox" data-jc-path="keepalive">@(Keep alive connection)</div>
|
||||
</div>
|
||||
<hr class="nmt nmb" />
|
||||
<div class="padding">
|
||||
<div data-jc="keyvalue" data-jc-path="headers" data-jc-config="placeholderkey:@(Header name);placeholdervalue:@(Header value and press enter)" class="m">@(Custom headers)</div>
|
||||
<div data-jc="keyvalue" data-jc-path="cookies" data-jc-config="placeholderkey:@(Cookie name);placeholdervalue:@(Cookie value and press enter)">@(Cookies)</div>
|
||||
</div>
|
||||
<div class="padding bg-smoke">
|
||||
<section>
|
||||
<label><i class="fa fa-lock"></i>@(HTTP basic access authentication)</label>
|
||||
<div class="padding npb">
|
||||
<div class="row">
|
||||
<div class="col-md-6 m">
|
||||
<div data-jc="textbox" data-jc-path="username">@(User)</div>
|
||||
</div>
|
||||
<div class="col-md-6 m">
|
||||
<div data-jc="textbox" data-jc-path="userpassword">@(Password)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>`;
|
||||
|
||||
exports.readme = `# Request
|
||||
|
||||
This component creates a request with received data.
|
||||
|
||||
__Response:__
|
||||
\`\`\`javascript
|
||||
{
|
||||
data: String,
|
||||
headers: Object,
|
||||
status: Number,
|
||||
host: String
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
__Dynamic arguments__:
|
||||
Are performed via FlowData repository and can be used for URL address or for custom headers/cookies/auth. Use \`repository\` component for creating of dynamic arguments. Dynamic values are replaced in the form \`{key}\`:
|
||||
|
||||
- url address e.g. \`https://.../{key}/\`
|
||||
- headers values e.g. \`{token}\`
|
||||
- cookies values e.g. \`{token}\``;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var can = false;
|
||||
var flags = null;
|
||||
var cookies2 = null;
|
||||
|
||||
instance.on('data', function(response) {
|
||||
can && instance.custom.send(response);
|
||||
});
|
||||
|
||||
instance.custom.send = function(response) {
|
||||
var options = instance.options;
|
||||
|
||||
var headers = null;
|
||||
var cookies = null;
|
||||
|
||||
options.headers && Object.keys(options.headers).forEach(function(key) {
|
||||
!headers && (headers = {});
|
||||
headers[key] = response.arg(options.headers[key]);
|
||||
});
|
||||
|
||||
if (options.username && options.userpassword) {
|
||||
!headers && (headers = {});
|
||||
headers['Authorization'] = 'Basic ' + U.createBuffer(response.arg(options.username + ':' + options.userpassword)).toString('base64');
|
||||
}
|
||||
|
||||
options.cookies && Object.keys(options.cookies).forEach(function(key) {
|
||||
!cookies && (cookies = {});
|
||||
cookies[key] = response.arg(options.cookies[key]);
|
||||
});
|
||||
|
||||
if (options.chunks) {
|
||||
U.download(response.arg(options.url), flags, options.stringify === 'none' ? null : response.data, function(err, response) {
|
||||
response.on('data', (chunks) => instance.send2(chunks));
|
||||
}, cookies || cookies2, headers);
|
||||
} else {
|
||||
U.request(response.arg(options.url), flags, options.stringify === 'none' ? null : response.data, function(err, data, status, headers, host) {
|
||||
if (response && !err) {
|
||||
response.data = { data: data, status: status, headers: headers, host: host };
|
||||
instance.send2(response);
|
||||
} else if (err)
|
||||
instance.error(err, response);
|
||||
}, cookies || cookies2, headers);
|
||||
}
|
||||
};
|
||||
|
||||
instance.reconfigure = function() {
|
||||
var options = instance.options;
|
||||
can = options.url && options.url && options.method && options.stringify ? true : false;
|
||||
instance.status(can ? '' : 'Not configured', can ? undefined : 'red');
|
||||
|
||||
if (!can)
|
||||
return;
|
||||
|
||||
flags = [];
|
||||
flags.push(options.method.toLowerCase());
|
||||
options.stringify === 'json' && flags.push('json');
|
||||
options.stringify === 'raw' && flags.push('raw');
|
||||
options.keepalive && flags.push('keepalive');
|
||||
!options.nodns && flags.push('dnscache');
|
||||
if (options.persistentcookies) {
|
||||
flags.push('cookies');
|
||||
cookies2 = {};
|
||||
} else
|
||||
cookies2 = null;
|
||||
};
|
||||
|
||||
instance.on('options', instance.reconfigure);
|
||||
instance.reconfigure();
|
||||
};
|
||||
76
flow/httpresponse.js
Normal file
76
flow/httpresponse.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
exports.id = 'httpresponse';
|
||||
exports.title = 'HTTP Response';
|
||||
exports.group = 'HTTP';
|
||||
exports.color = '#5D9CEC';
|
||||
exports.icon = 'arrow-right';
|
||||
exports.input = true;
|
||||
exports.output = ['#666D76'];
|
||||
exports.version = '2.0.0';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.readme = `# HTTP response
|
||||
|
||||
HTTP response will respond with data recieved using data-type set in Settings form or plain text if not set. Output is the message duration \`Number\` in seconds.`;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="dropdown" data-jc-path="datatype" data-jc-config="required:true;items:,Empty response|emptyresponse,JSON|json,HTML|html,Plain text|plain,XML|xml">@(Response data-type)</div>
|
||||
<div class="help"><code>JSON</code> is by default.</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var dursum = 0;
|
||||
var durcount = 0;
|
||||
|
||||
instance.on('data', function(flowdata) {
|
||||
|
||||
var ctrl = flowdata.repository.controller;
|
||||
var data = flowdata.data;
|
||||
|
||||
if (!ctrl) {
|
||||
instance.throw('No controller to use for response!');
|
||||
return;
|
||||
}
|
||||
|
||||
durcount++;
|
||||
dursum += ((new Date() - flowdata.begin) / 1000).floor(2);
|
||||
setTimeout2(instance.id, instance.custom.duration, 500, 10);
|
||||
|
||||
ctrl.$flowdata = flowdata;
|
||||
|
||||
var datatype = instance.options.datatype || 'json';
|
||||
if (datatype === 'emptyresponse')
|
||||
return ctrl.plain('');
|
||||
|
||||
if (datatype !== 'json' && typeof(data) !== 'string') {
|
||||
instance.throw('Incorect type of data, expected string, got ' + typeof(data));
|
||||
ctrl.plain(data == null ? '' : data.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
switch(datatype) {
|
||||
case 'html':
|
||||
ctrl.content(data, 'text/html');
|
||||
break;
|
||||
case 'plain':
|
||||
ctrl.plain(data);
|
||||
break;
|
||||
case 'xml':
|
||||
ctrl.content(data, 'text/xml');
|
||||
break;
|
||||
default:
|
||||
ctrl.json(data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
instance.on('service', function() {
|
||||
dursum = 0;
|
||||
durcount = 0;
|
||||
});
|
||||
|
||||
instance.custom.duration = function() {
|
||||
var avg = (dursum / durcount).floor(2);
|
||||
instance.status(avg + ' sec.');
|
||||
instance.send2(0, avg);
|
||||
};
|
||||
};
|
||||
326
flow/httproute.js
Normal file
326
flow/httproute.js
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
exports.id = 'httproute';
|
||||
exports.title = 'HTTP Route';
|
||||
exports.group = 'HTTP';
|
||||
exports.color = '#5D9CEC';
|
||||
exports.icon = 'globe';
|
||||
exports.input = false;
|
||||
exports.output = ['#6CAC5A', '#37BC9B'];
|
||||
exports.version = '1.2.3';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.cloning = false;
|
||||
exports.options = { method: 'GET', url: '', size: 5, cacheexpire: '5 minutes', cachepolicy: 0, timeout: 5 };
|
||||
exports.dateupdated = '2021-01-21 18:30d';
|
||||
exports.readme = `# HTTP route
|
||||
|
||||
__Outputs__:
|
||||
- first output: raw data (cache is empty or is disabled)
|
||||
- second output: cached data
|
||||
|
||||
If one of the outputs is disabled then automatic responce with code "503 service unavailable" is sent.
|
||||
|
||||
When a request comes in bellow object is available at \`flowdata.data\`:
|
||||
|
||||
\`\`\`javascript
|
||||
{
|
||||
params: { id: '1' }, // params for dynamic routes, e.g. /test/{id}
|
||||
query: { msg: 'Hello' }, // parsed query string, e.g. /test/1?msg=Hello
|
||||
body: { test: 'OK' }, // object if json requests otherwise string
|
||||
headers: {}, // headers data
|
||||
session: {}, // session data
|
||||
user: {}, // user data
|
||||
files: [], // uploaded files
|
||||
url: '/users/', // a relative URL address
|
||||
referrer: '/', // referrer
|
||||
mobile: false, // determines mobile device
|
||||
robot: false, // determines search robots/crawlsers
|
||||
language: 'en' // determines language
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
See [documentation for flags](https://docs.totaljs.com/latest/en.html#api~HttpRouteOptionsFlags~unauthorize). These method flags are set automatically e.g. \`get, post, put, patch or delete\`
|
||||
|
||||
---
|
||||
|
||||
\`id:ROUTE_ID\` flag cannot be used since it's already used by this component internally`;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<section>
|
||||
<label>@(Main settings)</label>
|
||||
<div class="padding npb">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 m">
|
||||
<div data-jc="dropdown" data-jc-path="method" data-jc-config="required:true;items:,GET,POST,PUT,DELETE,PATCH,OPTIONS">@(HTTP method)</div>
|
||||
</div>
|
||||
<div class="col-md-9 m">
|
||||
<div data-jc="textbox" data-jc-path="url" data-jc-config="required:true;maxlength:500;placeholder:/api/test;error:URL already in use">@(URL address)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 m">
|
||||
<div data-jc="textbox" data-jc-path="flags" data-jc-config="placeholder:json">@(Additional flags)</div>
|
||||
<div class="help m">@(Separate flags by comma e.g. <code>json, authorize</code>)</div>
|
||||
</div>
|
||||
<div class="col-md-3 m">
|
||||
<div data-jc="textbox" data-jc-path="size" data-jc-config="placeholder:@(in kB);increment:true;type:number;maxlength:10;align:center">@(Max. request size)</div>
|
||||
<div class="help m">@(In <code>kB</code> kilobytes)</div>
|
||||
</div>
|
||||
<div class="col-md-3 m">
|
||||
<div data-jc="textbox" data-jc-path="timeout" data-jc-config="placeholder:@(in seconds);increment:true;type:number;maxlength:5;align:center">@(Timeout)</div>
|
||||
<div class="help m">@(In seconds.)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<br />
|
||||
<div data-jc="checkbox" data-jc-path="emptyresponse" class="b black">@(Automatically respond with 200 OK?)</div>
|
||||
<div class="help m">@(If not checked you need to use HTTP response component to respond to the request.)</div>
|
||||
<hr />
|
||||
<div data-jc="keyvalue" data-jc-path="headers" data-jc-config="placeholderkey:@(Header name);placeholdervalue:@(Header value and press enter)" class="m">@(Custom headers)</div>
|
||||
<div data-jc="keyvalue" data-jc-path="cookies" data-jc-config="placeholderkey:@(Cookie name);placeholdervalue:@(Cookie value and press enter)">@(Cookies)</div>
|
||||
</div>
|
||||
<hr class="nmt" />
|
||||
<div class="padding npt">
|
||||
<div class="row">
|
||||
<div class="col-md-9 m">
|
||||
<div data-jc="dropdown" data-jc-path="cachepolicy" data-jc-config="type:number;items:@(no cache)|0,@(URL)|1,@(URL + query string)|2,@(URL + query string + user instance)|3">@(Cache policy)</div>
|
||||
<div class="help">@(User instance must contain <code>id</code> property.)</div>
|
||||
</div>
|
||||
<div class="col-md-3 m">
|
||||
<div data-jc="textbox" data-jc-path="cacheexpire" data-jc-config="maxlength:20;align:center">@(Expiration)</div>
|
||||
<div class="help">@(E.g. <code>5 minutes</code>)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
var httproute_currenturl = '';
|
||||
var httproute_currentmethod = 'GET';
|
||||
|
||||
ON('open.httproute', function(component, options) {
|
||||
if (options.flags instanceof Array) {
|
||||
var method = options.method.toLowerCase();
|
||||
options.flags = options.flags.remove(function(item) {
|
||||
switch (typeof(item)) {
|
||||
case 'string':
|
||||
return item.substring(0, 3) === 'id:' || item === method;
|
||||
case 'number': // timeout
|
||||
return true;
|
||||
}
|
||||
}).join(', ');
|
||||
}
|
||||
if (component.isnew) {
|
||||
options.url = '';
|
||||
options.name = '';
|
||||
} else {
|
||||
httproute_currenturl = options.url;
|
||||
httproute_currentmethod = options.method;
|
||||
}
|
||||
});
|
||||
|
||||
WATCH('settings.httproute.url', httproutecheckurl);
|
||||
WATCH('settings.httproute.method', httproutecheckurl);
|
||||
|
||||
function httproutecheckurl() {
|
||||
if (httproute_currenturl !== settings.httproute.url || httproute_currentmethod !== settings.httproute.method) {
|
||||
TRIGGER('httproutecheckurl', { url: settings.httproute.url, method: settings.httproute.method }, function(e) {
|
||||
var p = 'settings.httproute.url';
|
||||
if (e) {
|
||||
// invalid
|
||||
INVALID(p);
|
||||
} else {
|
||||
if (!CAN(p))
|
||||
RESET(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ON('save.httproute', function(component, options) {
|
||||
!component.name && (component.name = options.method + ' ' + options.url);
|
||||
|
||||
var builder = [];
|
||||
builder.push('### @(Configuration)');
|
||||
builder.push('');
|
||||
builder.push('- __' + options.method + ' ' + options.url + '__');
|
||||
builder.push('- @(flags): ' + options.flags);
|
||||
builder.push('- @(maximum request data length): __' + options.size + ' kB__');
|
||||
builder.push('- @(empty response): __' + options.emptyresponse + '__');
|
||||
|
||||
if (options.headers) {
|
||||
var headers = [];
|
||||
Object.keys(options.headers).forEach(function(key){
|
||||
headers.push(key + ': ' + options.headers[key]);
|
||||
});
|
||||
headers.length && builder.push('- @(headers):\\n\`\`\`' + headers.join('\\n') + '\`\`\`');
|
||||
};
|
||||
|
||||
var cp = '@(no cache)';
|
||||
if (options.cachepolicy === 1)
|
||||
cp = '@(URL)';
|
||||
if (options.cachepolicy === 2)
|
||||
cp = '@(URL + query string)';
|
||||
|
||||
if (options.cachepolicy === 3)
|
||||
cp = '@(URL + query string + user instance)';
|
||||
|
||||
builder.push('- @(cache policy): __' + cp + '__');
|
||||
|
||||
options.cacheexpire && builder.push('- @(cache expire): __' + options.cacheexpire + '__');
|
||||
|
||||
component.notes = builder.join('\\n');
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var route;
|
||||
|
||||
var uninstall = function(id) {
|
||||
if (F.is4) {
|
||||
route && route.remove();
|
||||
route = null;
|
||||
} else
|
||||
UNINSTALL('route', id);
|
||||
};
|
||||
|
||||
instance.custom.emptyresponse = function(self) {
|
||||
self.plain();
|
||||
};
|
||||
|
||||
instance.reconfigure = function() {
|
||||
|
||||
var options = instance.options;
|
||||
|
||||
if (!options.url) {
|
||||
instance.status('Not configured', 'red');
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof(options.flags) === 'string')
|
||||
options.flags = options.flags.split(',').trim();
|
||||
|
||||
uninstall('id:' + instance.id);
|
||||
|
||||
var flags = options.flags || [];
|
||||
|
||||
flags.push('id:' + instance.id);
|
||||
|
||||
if (!F.is4)
|
||||
flags.push(options.method.toLowerCase());
|
||||
|
||||
options.timeout && flags.push(options.timeout * 1000);
|
||||
|
||||
// Make unique values
|
||||
flags = flags.filter(function(v, i, a) {
|
||||
if(F.is4 && v.toString().toLowerCase() === options.method.toLowerCase())
|
||||
return false; // remove method
|
||||
return a.indexOf(v) === i;
|
||||
});
|
||||
|
||||
options.flags = flags;
|
||||
var handler = function() {
|
||||
|
||||
if (instance.paused || (instance.isDisabled && (instance.isDisabled('output', 0) || instance.isDisabled('output', 1)))) {
|
||||
instance.status('503 Service Unavailable');
|
||||
this.status = 503;
|
||||
this.json();
|
||||
return;
|
||||
}
|
||||
|
||||
var key;
|
||||
var self = this;
|
||||
|
||||
if (instance.options.emptyresponse) {
|
||||
instance.status('200 OK');
|
||||
setTimeout(instance.custom.emptyresponse, 100, self);
|
||||
|
||||
if (instance.hasConnection(0)) {
|
||||
var data = instance.make({
|
||||
query: self.query,
|
||||
body: self.body,
|
||||
session: self.session,
|
||||
user: self.user,
|
||||
files: self.files,
|
||||
headers: self.req.headers,
|
||||
url: self.url,
|
||||
params: self.params
|
||||
});
|
||||
instance.send2(0, data);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (instance.options.cachepolicy) {
|
||||
case 1: // URL
|
||||
key = 'rro' + instance.id + self.url.hash();
|
||||
break;
|
||||
case 2: // URL + query
|
||||
case 3: // URL + query + user
|
||||
key = self.url;
|
||||
var keys = Object.keys(self.query);
|
||||
keys.sort();
|
||||
for (var i = 0, length = keys.length; i < length; i++)
|
||||
key += keys[i] + self.query[keys[i]] + '&';
|
||||
if (instance.options.cachepolicy === 3 && self.user)
|
||||
key += 'iduser' + self.user.id;
|
||||
key = 'rro' + instance.id + key.hash();
|
||||
break;
|
||||
}
|
||||
|
||||
if (key && F.cache.get2(key)) {
|
||||
var data = instance.make(F.cache.get2(key));
|
||||
data.repository.controller = self;
|
||||
instance.send2(1, data);
|
||||
} else {
|
||||
|
||||
var data = instance.make({
|
||||
query: self.query,
|
||||
body: self.body,
|
||||
session: self.session,
|
||||
user: self.user,
|
||||
files: self.files,
|
||||
headers: self.req.headers,
|
||||
url: self.url,
|
||||
params: self.params,
|
||||
mobile: self.mobile,
|
||||
robot: self.robot,
|
||||
referrer: self.referrer,
|
||||
language: self.language
|
||||
});
|
||||
|
||||
data.repository.controller = self;
|
||||
instance.send2(0, data);
|
||||
key && FINISHED(self.res, () => F.cache.set(key, self.$flowdata.data, instance.options.cacheexpire));
|
||||
}
|
||||
};
|
||||
|
||||
if (F.is4)
|
||||
route = ROUTE(options.method.toUpperCase() + ' ' + options.url, handler, flags, options.size || 5);
|
||||
else
|
||||
F.route(options.url, handler, flags, options.size || 5);
|
||||
|
||||
instance.status('Listening', 'green');
|
||||
};
|
||||
|
||||
instance.reconfigure();
|
||||
instance.on('options', instance.reconfigure);
|
||||
instance.on('close', function(){
|
||||
uninstall('id:' + instance.id);
|
||||
});
|
||||
};
|
||||
|
||||
// check url exists
|
||||
FLOW.trigger('httproutecheckurl', function(next, data) {
|
||||
var url = data.url;
|
||||
var method = data.method;
|
||||
if (url[url.length - 1] !== '/')
|
||||
url += '/';
|
||||
var exists = F.routes.web.findItem(r => r.urlraw === url && r.method === method);
|
||||
next(exists != null);
|
||||
});
|
||||
|
||||
exports.uninstall = function() {
|
||||
FLOW.trigger('httproutecheckurl', null);
|
||||
};
|
||||
121
flow/infosender.js
Normal file
121
flow/infosender.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
exports.id = 'infosender';
|
||||
exports.title = 'Info sender';
|
||||
exports.version = '1.0.0';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#2134B0';
|
||||
exports.input = 1;
|
||||
exports.output = 1
|
||||
exports.click = false;
|
||||
exports.author = 'oms-is';
|
||||
exports.icon = 'bolt';
|
||||
exports.options = { edge: "undefined" };
|
||||
|
||||
const { networkInterfaces } = require('os');
|
||||
|
||||
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">CSV Import</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.readme = `# send all data to projects.worksys.io, required to monitor status of controller(unipi)`;
|
||||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||||
const fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
exports.install = async function(instance) {
|
||||
|
||||
let id;
|
||||
let allValues = {};
|
||||
let dbSettings;
|
||||
|
||||
let sendAllValuesInterval;
|
||||
|
||||
let now = new Date();
|
||||
console.log(exports.title, "INSTALLED", now.toLocaleString("sk-SK"));
|
||||
|
||||
const nets = networkInterfaces();
|
||||
let ipAddresses = Object.create(null); // Or just '{}', an empty object
|
||||
|
||||
for (const name of Object.keys(nets)) {
|
||||
for (const net of nets[name]) {
|
||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||
if (net.family === 'IPv4' && !net.internal) {
|
||||
if (!ipAddresses[name]) {
|
||||
ipAddresses[name] = [];
|
||||
}
|
||||
ipAddresses[name].push(net.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if(Object.keys(allValues).length > 0)
|
||||
{
|
||||
if(id !== undefined)
|
||||
{
|
||||
delete allValues.__force__;
|
||||
let dataToSend = {...allValues};
|
||||
dataToSend.id = id;
|
||||
dataToSend.ipAddresses = ipAddresses;
|
||||
//dataToSend.notify_date = new Date().toISOString().slice(0, 19).replace('T', ' ');
|
||||
|
||||
//console.log(exports.title, "------------>sendValues", dataToSend);
|
||||
|
||||
instance.send(0, dataToSend);
|
||||
|
||||
allValues = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log(exports.title, "unable to send data, id is undefined");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
instance.on("close", () => {
|
||||
clearInterval(sendAllValuesInterval);
|
||||
})
|
||||
|
||||
instance.on("data", (flowdata) => {
|
||||
|
||||
allValues = { ...allValues, ...flowdata.data};
|
||||
|
||||
//console.log("DATA RECEIVED", flowdata.data);
|
||||
|
||||
//__force__
|
||||
if(flowdata.data.hasOwnProperty("__force__"))
|
||||
{
|
||||
if(flowdata.data.__force__)
|
||||
{
|
||||
sendValues();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
sendAllValuesInterval = setInterval(() => {
|
||||
sendValues();
|
||||
}, 60000*3);
|
||||
|
||||
}
|
||||
|
||||
1127
flow/modbus_citysys.js
Normal file
1127
flow/modbus_citysys.js
Normal file
File diff suppressed because it is too large
Load diff
156
flow/monitorconsumption.js
Normal file
156
flow/monitorconsumption.js
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
exports.id = 'monitorconsumption';
|
||||
exports.title = 'Consumption';
|
||||
exports.version = '1.0.0';
|
||||
exports.author = 'Peter Širka';
|
||||
exports.group = 'Monitoring';
|
||||
exports.color = '#967ADC';
|
||||
exports.input = 0;
|
||||
exports.output = 1;
|
||||
exports.icon = 'bug';
|
||||
exports.options = { interval: 5000, enabled: true, monitorconsumption: true, monitorsize: true, monitorconnections: true, monitorfiles: true };
|
||||
exports.click = true;
|
||||
exports.readme = `# Consumption monitoring
|
||||
|
||||
This component measure CPU and memory consumption, open files and open connections of this application. It uses these Linux commands: \`ps\`, \`lsof\`, \`netstat\` and \`df\`.
|
||||
|
||||
__Data Example__:
|
||||
|
||||
\`\`\`javascript
|
||||
{
|
||||
cpu: 0, // percentage
|
||||
memory: 4096, // in bytes
|
||||
size: 34303, // directory size in bytes
|
||||
files: 34, // count of open files
|
||||
connections: 343, // count of connections
|
||||
uptime: '1-12:34:00'
|
||||
}
|
||||
\`\`\``;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-3 m">
|
||||
<div data-jc="textbox" data-jc-path="interval" data-jc-config="placeholder:10000;increment:true;type:number;required:true;maxlength:10;align:center">@(Interval in milliseconds)</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div data-jc="checkbox" data-jc-path="monitorconsumption">Monitor: Consumption + uptime</div>
|
||||
<div data-jc="checkbox" data-jc-path="monitorfiles">Monitor: Count of open files</div>
|
||||
<div data-jc="checkbox" data-jc-path="monitorconnections">Monitor: Count of open connections</div>
|
||||
<div data-jc="checkbox" data-jc-path="monitorsize">Monitor: Directory size</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var current = { cpu: 0, memory: 0, files: 0, connections: 0, size: 0, uptime: '', counter: 0 };
|
||||
var tproc = null;
|
||||
var Exec = require('child_process').exec;
|
||||
var reg_empty = /\s{2,}/g;
|
||||
var reg_appdisksize = /^[\d.,]+/;
|
||||
|
||||
instance.custom.run = function() {
|
||||
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
|
||||
var arr = [];
|
||||
|
||||
// Get CPU and Memory consumption
|
||||
instance.options.monitorconsumption && arr.push(function(next) {
|
||||
Exec('ps -p {0} -o %cpu,rss,etime'.format(process.pid), function(err, response) {
|
||||
|
||||
if (err) {
|
||||
instance.throw(err);
|
||||
} else {
|
||||
var line = response.split('\n')[1];
|
||||
line = line.trim().replace(reg_empty, ' ').split(' ');
|
||||
var cpu = line[0].parseFloat();
|
||||
current.cpu = cpu.floor(1);
|
||||
current.memory = line[1].parseInt2() * 1024; // kB to bytes
|
||||
current.uptime = line[2];
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
// Get count of open files
|
||||
instance.options.monitorfiles && arr.push(function(next) {
|
||||
Exec('lsof -a -p {0} | wc -l'.format(process.pid), function(err, response) {
|
||||
if (err)
|
||||
instance.throw(err);
|
||||
else
|
||||
current.files = response.trim().parseInt2();
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
// Get count of opened network connections
|
||||
instance.options.monitorconnections && arr.push(function(next) {
|
||||
Exec('netstat -an | grep :{0} | wc -l'.format(F.port), function(err, response) {
|
||||
if (err) {
|
||||
instance.throw(err);
|
||||
} else {
|
||||
current.connections = response.trim().parseInt2() - 1;
|
||||
if (current.connections < 0)
|
||||
current.connections = 0;
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
// Get directory size
|
||||
instance.options.monitorsize && current.counter % 5 !== 0 && arr.push(function(next) {
|
||||
Exec('du -hsb ' + process.cwd(), function(err, response) {
|
||||
if (err) {
|
||||
instance.throw(err);
|
||||
} else {
|
||||
var match = response.trim().match(reg_appdisksize);
|
||||
match && (current.size = match.toString().trim().parseInt2());
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
arr.async(function() {
|
||||
|
||||
tproc && clearTimeout(tproc);
|
||||
|
||||
if (instance.options.enabled) {
|
||||
tproc = setTimeout(instance.custom.run, instance.options.interval);
|
||||
instance.send2(current);
|
||||
}
|
||||
|
||||
instance.custom.status();
|
||||
current.counter++;
|
||||
});
|
||||
};
|
||||
|
||||
instance.custom.status = function() {
|
||||
if (instance.options.enabled)
|
||||
instance.status('{0}% / {1}'.format(current.cpu, current.memory.filesize()));
|
||||
else
|
||||
instance.status('Disabled', 'red');
|
||||
};
|
||||
|
||||
instance.on('click', function() {
|
||||
instance.options.enabled = !instance.options.enabled;
|
||||
instance.custom.status();
|
||||
|
||||
if (instance.options.enabled) {
|
||||
current.counter = 0;
|
||||
instance.custom.run();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
instance.on('close', function() {
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(instance.custom.run, 1000);
|
||||
};
|
||||
107
flow/monitorcpu.js
Normal file
107
flow/monitorcpu.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
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);
|
||||
};
|
||||
96
flow/monitordisk.js
Normal file
96
flow/monitordisk.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
exports.id = 'monitordisk';
|
||||
exports.title = 'Disk';
|
||||
exports.version = '1.0.0';
|
||||
exports.author = 'Peter Širka';
|
||||
exports.group = 'Monitoring';
|
||||
exports.color = '#F6BB42';
|
||||
exports.output = 1;
|
||||
exports.icon = 'hdd-o';
|
||||
exports.click = true;
|
||||
exports.options = { interval: 8000, path: '/', enabled: true };
|
||||
exports.readme = `# Disk monitoring
|
||||
|
||||
This component monitors disk \`bytes\` consumption in Linux systems. It uses \`df\` command.
|
||||
|
||||
__Data Example__:
|
||||
|
||||
\`\`\`javascript
|
||||
{
|
||||
total: 474549649408,
|
||||
used: 39125245952,
|
||||
free: 411294994432
|
||||
}
|
||||
\`\`\``;
|
||||
|
||||
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 class="col-md-3 m">
|
||||
<div data---="textbox__path__placeholder:/;required:true">@(Path)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var current = { total: 0, used: 0, free: 0, path: '', type: '', percentUsed: 0 };
|
||||
var tproc = null;
|
||||
|
||||
instance.custom.run = function() {
|
||||
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
|
||||
if (!instance.options.enabled)
|
||||
return;
|
||||
|
||||
require('child_process').exec('df -hTB1 ' + instance.options.path, function(err, response) {
|
||||
|
||||
tproc = setTimeout(instance.custom.run, instance.options.interval);
|
||||
|
||||
if (err) {
|
||||
instance.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
response.parseTerminal(function(line) {
|
||||
if (line[0][0] !== '/')
|
||||
return;
|
||||
current.total = line[2].parseInt();
|
||||
current.free = line[4].parseInt();
|
||||
current.used = line[3].parseInt();
|
||||
current.path = instance.options.path || '/';
|
||||
current.type = line[1];
|
||||
current.percentUsed = line[5];
|
||||
instance.custom.status();
|
||||
instance.send2(current);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
instance.custom.status = function() {
|
||||
if (instance.options.enabled)
|
||||
instance.status(current.free.filesize() + ' / ' + current.total.filesize());
|
||||
else
|
||||
instance.status('Disabled', 'red');
|
||||
};
|
||||
|
||||
instance.on('click', function() {
|
||||
instance.options.enabled = !instance.options.enabled;
|
||||
instance.custom.status();
|
||||
instance.options.enabled && instance.custom.run();
|
||||
});
|
||||
|
||||
instance.on('close', function() {
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(instance.custom.run, 1000);
|
||||
};
|
||||
87
flow/monitormemory.js
Normal file
87
flow/monitormemory.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
exports.id = 'monitormemory';
|
||||
exports.title = 'Memory';
|
||||
exports.version = '1.0.0';
|
||||
exports.author = 'Peter Širka';
|
||||
exports.group = 'Monitoring';
|
||||
exports.color = '#F6BB42';
|
||||
exports.output = 1;
|
||||
exports.click = true;
|
||||
exports.icon = 'microchip';
|
||||
exports.options = { interval: 8000, enabled: true };
|
||||
exports.readme = `# Memory monitoring
|
||||
|
||||
This component monitors memory \`bytes\` consumption in Linux systems. It uses \`free\` command.
|
||||
|
||||
__Data Example__:
|
||||
|
||||
\`\`\`javascript
|
||||
{
|
||||
total: 33558769664,
|
||||
used: 1998868480,
|
||||
free: 2653708288
|
||||
}
|
||||
\`\`\``;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-3 m">
|
||||
<div data-jc="textbox" data-jc-path="interval" data-jc-config="placeholder:10000;increment:true;type:number;required:true;maxlength:10;align:center">@(Interval in milliseconds)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var current = { total: 0, used: 0, free: 0 };
|
||||
var tproc = null;
|
||||
|
||||
instance.custom.run = function() {
|
||||
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
|
||||
if (!instance.options.enabled)
|
||||
return;
|
||||
|
||||
require('child_process').exec('free -b -t', function(err, response) {
|
||||
|
||||
tproc = setTimeout(instance.custom.run, instance.options.interval);
|
||||
|
||||
if (err) {
|
||||
instance.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var memory = response.split('\n')[1].match(/\d+/g);
|
||||
current.total = memory[0].parseInt();
|
||||
current.used = memory[1].parseInt() - memory[3].parseInt();
|
||||
current.free = current.total - current.used;
|
||||
instance.custom.status();
|
||||
instance.send2(current);
|
||||
});
|
||||
};
|
||||
|
||||
instance.custom.status = function() {
|
||||
if (instance.options.enabled)
|
||||
instance.status(current.free.filesize() + ' / ' + current.total.filesize());
|
||||
else
|
||||
instance.status('Disabled', 'red');
|
||||
};
|
||||
|
||||
instance.on('click', function() {
|
||||
instance.options.enabled = !instance.options.enabled;
|
||||
instance.custom.status();
|
||||
instance.options.enabled && instance.custom.run();
|
||||
});
|
||||
|
||||
instance.on('close', function() {
|
||||
if (tproc) {
|
||||
clearTimeout(tproc);
|
||||
tproc = null;
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(instance.custom.run, 1000);
|
||||
};
|
||||
441
flow/mqtt.js
Normal file
441
flow/mqtt.js
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
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();
|
||||
};
|
||||
171
flow/mqtt_listener.js
Normal file
171
flow/mqtt_listener.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
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();
|
||||
};
|
||||
|
||||
|
||||
|
||||
134
flow/mqttpublish.js
Normal file
134
flow/mqttpublish.js
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
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();
|
||||
};
|
||||
168
flow/mqttsubscribe.js
Normal file
168
flow/mqttsubscribe.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
exports.id = 'mqttsubscribe';
|
||||
exports.title = 'MQTT subscribe';
|
||||
exports.group = 'MQTT';
|
||||
exports.color = '#888600';
|
||||
exports.version = '1.1.0';
|
||||
exports.icon = 'sign-in';
|
||||
exports.output = 1;
|
||||
exports.variables = true;
|
||||
exports.author = 'Martin Smola';
|
||||
exports.options = {};
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="dropdown" data-jc-path="broker" data-jc-config="datasource:mqttconfig.brokers;required:true" class="m">@(Select a broker)</div>
|
||||
<div data-jc="textbox" data-jc-path="topic" data-jc-config="placeholder:hello/world;required:true">Topic</div>
|
||||
<div class="help m">@(Supports variables, example: \`device/{device-id}\`)</div>
|
||||
<div data-jc="dropdown" data-jc-path="qos" data-jc-config="items:,0,1,2" class="m">@(QoS)</div>
|
||||
</div>
|
||||
<script>
|
||||
var mqttconfig = { brokers: [] };
|
||||
ON('open.mqttsubscribe', function(component, options) {
|
||||
TRIGGER('mqtt.brokers', 'mqttconfig.brokers');
|
||||
});
|
||||
ON('save.mqttsubscribe', function(component, options) {
|
||||
!component.name && (component.name = options.broker + (options.topic ? ' -> ' + options.topic : ''));
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.readme = `
|
||||
# MQTT subscribe
|
||||
|
||||
The data recieved are passed to the output as follows:
|
||||
\`\`\`javascript
|
||||
{
|
||||
topic: '/lights/on',
|
||||
data: 'kitchen'
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
If the topic is wildcard then there's an array of matches in flowdata repository which can be used in Function component like so:
|
||||
\`\`\`javascript
|
||||
// wildcard -> /+/status
|
||||
// topic -> /devicename/status
|
||||
|
||||
var match = flowdata.get('mqtt_wildcard');
|
||||
// match === ['devicename']
|
||||
\`\`\`
|
||||
|
||||
More on wildcard topics [here](https://mosquitto.org/man/mqtt-7.html)
|
||||
`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var old_topic;
|
||||
var ready = false;
|
||||
|
||||
instance.custom.reconfigure = function(o, old_options) {
|
||||
|
||||
|
||||
ready = false;
|
||||
|
||||
if (!MQTT.broker(instance.options.broker))
|
||||
return instance.status('No broker', 'red');
|
||||
|
||||
if (instance.options.broker && instance.options.topic) {
|
||||
|
||||
if (old_topic)
|
||||
MQTT.unsubscribe(instance.options.broker, instance.id, old_topic);
|
||||
|
||||
old_topic = instance.arg(instance.options.topic);
|
||||
MQTT.subscribe(instance.options.broker, instance.id, old_topic);
|
||||
ready = true;
|
||||
return;
|
||||
}
|
||||
|
||||
instance.status('Not configured', 'red');
|
||||
};
|
||||
|
||||
instance.on('options', instance.custom.reconfigure);
|
||||
|
||||
instance.on('close', function() {
|
||||
MQTT.unsubscribe(instance.options.broker, instance.id, instance.options.topic);
|
||||
OFF('mqtt.brokers.message', instance.custom.message);
|
||||
OFF('mqtt.brokers.status', instance.custom.brokerstatus);
|
||||
});
|
||||
|
||||
instance.custom.brokerstatus = function(status, brokerid, msg) {
|
||||
if (brokerid !== instance.options.broker)
|
||||
return;
|
||||
|
||||
switch (status) {
|
||||
case 'connecting':
|
||||
instance.status('Connecting', '#a6c3ff');
|
||||
break;
|
||||
case 'connected':
|
||||
instance.status('Connected', 'green');
|
||||
break;
|
||||
case 'disconnected':
|
||||
instance.status('Disconnected', 'red');
|
||||
break;
|
||||
case 'connectionfailed':
|
||||
instance.status('Connection failed', 'red');
|
||||
break;
|
||||
case 'new':
|
||||
!ready && instance.custom.reconfigure();
|
||||
break;
|
||||
case 'removed':
|
||||
instance.custom.reconfigure();
|
||||
break;
|
||||
case 'error':
|
||||
instance.status(msg, 'red');
|
||||
break;
|
||||
case 'reconfigured':
|
||||
instance.options.broker = msg;
|
||||
instance.reconfig();
|
||||
instance.custom.reconfigure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instance.custom.message = function(brokerid, topic, message) {
|
||||
if (brokerid !== instance.options.broker)
|
||||
return;
|
||||
|
||||
var match = mqttWildcard(topic, old_topic);
|
||||
if (match) {
|
||||
var flowdata = instance.make({ topic: topic, data: message })
|
||||
flowdata.set('mqtt_wildcard', match);
|
||||
instance.send2(flowdata);
|
||||
}
|
||||
}
|
||||
|
||||
ON('mqtt.brokers.message', instance.custom.message);
|
||||
ON('mqtt.brokers.status', instance.custom.brokerstatus);
|
||||
|
||||
instance.custom.reconfigure();
|
||||
};
|
||||
|
||||
// https://github.com/hobbyquaker/mqtt-wildcard
|
||||
function mqttWildcard(topic, wildcard) {
|
||||
if (topic === wildcard) {
|
||||
return [];
|
||||
} else if (wildcard === '#') {
|
||||
return [topic];
|
||||
}
|
||||
|
||||
var res = [];
|
||||
|
||||
var t = String(topic).split('/');
|
||||
var w = String(wildcard).split('/');
|
||||
|
||||
var i = 0;
|
||||
for (var lt = t.length; i < lt; i++) {
|
||||
if (w[i] === '+') {
|
||||
res.push(t[i]);
|
||||
} else if (w[i] === '#') {
|
||||
res.push(t.slice(i).join('/'));
|
||||
return res;
|
||||
} else if (w[i] !== t[i]) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (w[i] === '#') {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return (i === w.length) ? res : null;
|
||||
}
|
||||
191
flow/nosql.js
Normal file
191
flow/nosql.js
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
31
flow/notifikacie.csv
Normal file
31
flow/notifikacie.csv
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
key;weight;en;sk
|
||||
switching_profile_point_applied_to_line;INFO;Switching profile point applied to line no. ${line} : ${value};Aplikovaný bod spínacieho profilu na línií č. ${line} : ${value}
|
||||
dusk_has_occured;INFO;Dusk has occured;Nastal súmrak
|
||||
dawn_has_occured;INFO;Dawn has occured;Nastal úsvit
|
||||
dimming_profile_was_successfully_received_by_node;NOTICE;Dimming profile was successfully received by node no. ${node};Stmievací profil bol úspešne prijatý nodom č. ${node}
|
||||
master_node_is_responding_again;NOTICE;Master node is responding again;Master node začal znovu odpovedať
|
||||
command_was_sent_from_terminal_interface;DEBUG;A command was sent from terminal interface;Z terminálu bol odoslaný príkaz
|
||||
master_node_is_not_responding;ALERT;Master node is not responding;Master node neodpovedá
|
||||
configuration_of_dimming_profile_to_node_failed;ALERT;Configuration of dimming profile to node no. ${node} has failed;Konfigurácia stmievacieho profilu pre node č. ${node} zlyhala
|
||||
circuit_breaker_was_turned_on_line;NOTICE;Circuit breaker was turned on - line no. ${line};Zapnutie ističa na línii č. ${line}
|
||||
circuit_breaker_was_turned_off_line;ERROR;Circuit breaker was turned off - line no. ${line};Vypnutie ističa na línií č. ${line}
|
||||
dimming_profile_was_processed_for_node;INFO;Dimming profile was processed for node no. ${node};Stmievací profil bol spracovaný pre node č. ${node}
|
||||
switching_profile_was_processed_for_line;INFO;Switching profile was processed for line no. ${line};Spínací profil bol spracovaný pre líniu č. ${line}
|
||||
thermometer_is_not_responding;WARNING;Thermometer is not responding;Teplomer neodpovedá
|
||||
thermometer_is_responding_again;NOTICE;Thermometer is responding again;Teplomer znovu odpovedá
|
||||
thermometer_sends_invalid_data;WARNING;Thermometer sends invalid data;Teplomer posiela neplatné hodnoty
|
||||
main_switch_has_been_turned_off;CRITICAL;Main switch has been turned off;Hlavný vypínač bol vypnutý
|
||||
main_switch_has_been_turned_on;NOTICE;Main switch has been turned on;Hlavný vypínač bol zapnutý
|
||||
power_supply_has_disconnected_input;ALERT;Power supply has disconnected input;Napájací zdroj nemá napätie na vstupe
|
||||
power_supply_works_correctly;NOTICE;Power supply works correctly ;Napájací zdroj pracuje správne
|
||||
battery_level_is_low;ERROR;Battery level is low;Batéria má nízku úroveň napätia
|
||||
battery_level_is_ok;NOTICE;Battery level is OK;Batéria má správnu úroveň napätia
|
||||
door_has_been_open;NOTICE;Door has been open;Dvere boli otvorené
|
||||
door_has_been_closed;NOTICE;Door has been closed;Dvere boli zatvorené
|
||||
door_has_been_open_without_permision_alarm_is_on;WARNING;Door has been open without permision - alarm is on;Dvere boli otvorené bez povolania - zapnutá siréna
|
||||
state_of_contactor_for_line;INFO;State of contactor for line no. ${line} is ${value};Stav stýkača pre líniu č. ${line} je ${value}
|
||||
local_database_is_corrupted;CRITICAL;;
|
||||
electrometer_is_not_responding;ERROR;Electrometer is not responding;Elektromer neodpovedá
|
||||
no_voltage_detected_on_phase;CRITICAL;No voltage detected on phase no. ${phase};Na fáze č. ${phase} nie je napätie
|
||||
electrometer_is_responding_again;NOTICE;Electrometer is responding again;Elektromer znovu odpovedá
|
||||
voltaga_on_phase_has_been_restored;NOTICE;Voltaga on phase no. ${phase} has been restored;Napätie na fáze č. ${phase} bolo obnovené
|
||||
|
357
flow/relays.js
Normal file
357
flow/relays.js
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
exports.id = 'relay';
|
||||
exports.title = 'DI_DO_Controller';
|
||||
exports.version = '1.0.0';
|
||||
exports.group = 'Worksys';
|
||||
exports.color = '#2134B0';
|
||||
exports.input = 1;
|
||||
exports.output = ["red", "white", "yellow"];
|
||||
exports.click = false;
|
||||
exports.author = 'Daniel Segeš';
|
||||
exports.icon = 'bolt';
|
||||
exports.options = { edge: "undefined" };
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div data-jc="textbox" data-jc-path="edge" data-jc-config="placeholder:undefined;required:true" class="m">Edge TB Name</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
exports.readme = `# Sets RS232 port and all digital pins on device. Then it starts to receive data from sensors.
|
||||
It receives:
|
||||
|
||||
rotary_switch_state,
|
||||
rotary_switch_state,
|
||||
door_condition,
|
||||
state_of_breaker,
|
||||
state_of_contactor,
|
||||
twilight_sensor
|
||||
`;
|
||||
|
||||
/*
|
||||
we open rsPort "/dev/ttymxc0" and set digital input and output pins with "setRSPortData"
|
||||
Currently we are interested in pins no. 1,2,3,6,8,9,10,16
|
||||
pins number 11, 12, 13 (we receive 10,11,12 in rsPortReceivedData) are "stykace"
|
||||
When port receives data, it must be exactly 4 bytes long. Second byte is pin, that changed its value, fourth byte is value itself.
|
||||
After that, we set this value to "previousValues[allPins[whichpin]]" variable
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
RVO objekt:
|
||||
state_of_main_switch - sem sa bude reportovať stav hlavného ističa : 0-> off 1-> on (toto nie je na platforme, ale Rado to už do entity type doplnil)
|
||||
rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky:
|
||||
Manual = 1 a Auto = 0 -> vyreportuje Manual
|
||||
Manual = 0 a Auto = 0 -> vyreportuje Off
|
||||
Manual = 0 a Auto = 1 -> vyreportuje Automatic
|
||||
|
||||
door_condition - tuto ide pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open
|
||||
twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy
|
||||
|
||||
zjavne nám v jsone chýba stav hlavného ističa. Musíme to potom doplniť
|
||||
|
||||
Na každú líniu:
|
||||
state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off 1-> on
|
||||
state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off 1-> on
|
||||
momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme
|
||||
*/
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
let previousValues = {};
|
||||
let rsPortReceivedData = [];
|
||||
|
||||
console.log("DI_DO_Relay_Controller installed");
|
||||
|
||||
//key is PIN number
|
||||
const conversionTable = {
|
||||
"1": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "state_of_main_switch"}, //state_of_main_switch pin1
|
||||
"2": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "rotary_switch_state"}, //rotary_switch_state - poloha manual = pin2
|
||||
"3": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "rotary_switch_state"}, //rotary_switch_state - poloha auto = pin3
|
||||
"6": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "door_condition"}, // door_condition = pin6, 1 -> vyreportuje Closed, 0 -> vyreportuje Open
|
||||
"8": {tbname: "RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O", type: "state_of_breaker", "line": 1}, // state_of_breaker linia 1 0=off, 1=on
|
||||
"9": {tbname: "dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7", type: "state_of_breaker", "line": 2}, // state_of_breaker linia 2 0=off, 1=on
|
||||
"10": {tbname: "vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V", type: "state_of_breaker", "line": 3}, // state_of_breaker linia 3 0=off, 1=on
|
||||
"11": {tbname: "RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O", type: "state_of_contactor", "line": 1}, // state_of_contactor linia 1 0=off, 1=on
|
||||
"12": {tbname: "dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7", type: "state_of_contactor", "line": 2}, // state_of_contactor linia 2 0=off, 1=on
|
||||
"13": {tbname: "vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V", type: "state_of_contactor", "line": 3}, // state_of_contactor linia 3 0=off, 1=on
|
||||
"16": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "state_of_main_switch"}, // twilight_sensor = pin16
|
||||
};
|
||||
|
||||
const dbRelays = TABLE("relays");
|
||||
dbRelays.on('change', function(doc, old) {
|
||||
instance.send(2, "reload_relays");
|
||||
});
|
||||
|
||||
//modify
|
||||
|
||||
const SerialPort = require('serialport');
|
||||
//const { exec } = require('child_process');
|
||||
const { openPort, runSyncExec, writeData } = require('./serialport_helper.js');
|
||||
|
||||
const setRSPortData = [0xAA,6,6,6,6,0,6,0,6,6,6,1,1,1,1,0,0,10,10,10,10,0,10,0,10,10,10,0,0,0,0,0,0,5,0,0,0,15,15,15,15,0,15,0,15,15,15,0,0,0,0,0,0,30,0,0,0];
|
||||
const rsPort = new SerialPort("/dev/ttymxc0", { autoOpen: false });
|
||||
|
||||
rsPort.on('open', async function() {
|
||||
|
||||
await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function (status) {
|
||||
instance.send(0, exports.title + " runSyncExec - Promise Resolved:" + status);
|
||||
|
||||
//set port
|
||||
rsPort.write(Buffer.from(setRSPortData), function(err) {
|
||||
instance.send(0, exports.title + " Digital in_out has been set");
|
||||
|
||||
//force turn off relays
|
||||
let keys = Object.keys(conversionTable);
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
|
||||
if(conversionTable[key].type == "state_of_contactor")
|
||||
{
|
||||
let pin = key - 1;
|
||||
let line = conversionTable[key].line;
|
||||
|
||||
turnOff(line, pin);
|
||||
}
|
||||
}
|
||||
|
||||
//dbRelays.modify({ contactor: 0 });
|
||||
})
|
||||
|
||||
}).catch(function (reason) {
|
||||
instance.send(0, exports.title + " runSyncExec - promise rejected:" + reason);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
rsPort.open();
|
||||
|
||||
rsPort.on('data', function (data){
|
||||
|
||||
rsPortReceivedData = [...rsPortReceivedData, ...data];
|
||||
|
||||
if (rsPortReceivedData[0] != 85) {
|
||||
rsPortReceivedData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let l = rsPortReceivedData.length;
|
||||
|
||||
if (l < 4 ) return;
|
||||
|
||||
if (l > 4 ) {
|
||||
|
||||
// if array length is greater than 4, we take first 4 byte and do the logic, second 4 bytes, do the logic and so on
|
||||
let i, j, temparray, chunk = 4;
|
||||
for ( i = 0, j = l; i < j; i += chunk ) {
|
||||
temparray = rsPortReceivedData.slice(i, i + chunk);
|
||||
|
||||
if ( temparray.length < 4 ){
|
||||
rsPortReceivedData = [...temparray];
|
||||
return;
|
||||
}
|
||||
|
||||
switchLogic(temparray);
|
||||
}
|
||||
|
||||
rsPortReceivedData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
switchLogic(rsPortReceivedData);
|
||||
|
||||
rsPortReceivedData = [];
|
||||
|
||||
});
|
||||
|
||||
rsPort.on('error', function(err) {
|
||||
instance.send(0, err.message);
|
||||
})
|
||||
|
||||
rsPort.on("close", () => {
|
||||
rsPort.close();
|
||||
})
|
||||
|
||||
instance.on("close", () => {
|
||||
rsPort.close();
|
||||
})
|
||||
|
||||
function getPin(line)
|
||||
{
|
||||
//conversionTable
|
||||
let keys = Object.keys(conversionTable);
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
{
|
||||
let key = keys[i];
|
||||
|
||||
if(conversionTable[key].type == "state_of_contactor" && conversionTable[key].line == line)
|
||||
{
|
||||
return key - 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("no pin detected");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function turnOn(line, pin)
|
||||
{
|
||||
if( pin === undefined) pin = getPin(line);
|
||||
if( pin === undefined) return;
|
||||
|
||||
let arr = [0x55];
|
||||
arr.push( pin );
|
||||
arr.push( 0 );
|
||||
arr.push( 1 );
|
||||
|
||||
if(!rsPort.isOpen)
|
||||
{
|
||||
console.log("port is not opened");
|
||||
return;
|
||||
}
|
||||
|
||||
rsPort.write(Buffer.from(arr), function(err) {
|
||||
switchLogic(arr);
|
||||
});
|
||||
}
|
||||
|
||||
function turnOff(line, pin)
|
||||
{
|
||||
if( pin === undefined) pin = getPin(line);
|
||||
if( pin === undefined) return;
|
||||
|
||||
let arr = [0x55];
|
||||
arr.push( pin );
|
||||
arr.push( 0 );
|
||||
arr.push( 0 );
|
||||
|
||||
if(!rsPort.isOpen)
|
||||
{
|
||||
console.log("port is not opened");
|
||||
return;
|
||||
}
|
||||
|
||||
rsPort.write(Buffer.from(arr), function(err) {
|
||||
switchLogic(arr);
|
||||
});
|
||||
}
|
||||
|
||||
// we expect array as flowdata.data
|
||||
instance.on("data", (flowdata) => {
|
||||
|
||||
//console.log(flowdata.data);
|
||||
|
||||
if(flowdata.data instanceof Object)
|
||||
{
|
||||
let obj = flowdata.data;
|
||||
|
||||
let line = obj.line;
|
||||
|
||||
if(obj.command == "turnOn") turnOn(line);
|
||||
else if(obj.command == "turnOff") turnOff(line);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(flowdata.data)){
|
||||
rsPort.write(Buffer.from(flowdata.data), function(err) {
|
||||
switchLogic(flowdata.data);
|
||||
|
||||
instance.send(0,{"WRITE":flowdata.data});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
const switchLogic = (rsPortReceivedData) => {
|
||||
|
||||
let dataToTb;
|
||||
let values = {};
|
||||
|
||||
let pinIndex = rsPortReceivedData[1] + 1;
|
||||
if (pinIndex === 17) pinIndex--;
|
||||
|
||||
let newPinValue = rsPortReceivedData[3];
|
||||
|
||||
let obj = conversionTable[pinIndex];
|
||||
|
||||
if(obj == undefined)
|
||||
{
|
||||
//console.log("undefined pinIndex", pinIndex, rsPortReceivedData);
|
||||
return;
|
||||
}
|
||||
|
||||
let type = obj.type;
|
||||
let line = obj.line;
|
||||
let tbname = obj.tbname;
|
||||
|
||||
//default value
|
||||
let value = "On";
|
||||
if(newPinValue === 0) value = "Off";
|
||||
|
||||
if(type == "rotary_switch_state")
|
||||
{
|
||||
// combination of these two pins required to get result
|
||||
let pin2, pin3;
|
||||
if(pinIndex == 2)
|
||||
{
|
||||
pin2 = newPinValue;
|
||||
pin3 = previousValues[pinIndex];
|
||||
if (pin3 == undefined) pin3 = 0;
|
||||
}
|
||||
else if(pinIndex == 3)
|
||||
{
|
||||
pin3 = newPinValue;
|
||||
pin2 = previousValues[1];
|
||||
if (pin2 == undefined) pin2 = 0;
|
||||
}
|
||||
|
||||
if (pin2 == 1 && pin3 == 0) value = "Manual";
|
||||
if (pin2 == 0 && pin3 == 0) value = "Off";
|
||||
if (pin2 == 0 && pin3 == 1) value = "Automatic";
|
||||
}
|
||||
else if(type == "door_condition")
|
||||
{
|
||||
newPinValue === 0 ? value = "Open" : value = "Closed";
|
||||
}
|
||||
else if(type == "twilight_sensor")
|
||||
{
|
||||
value = parseFloat(newPinValue + (256*rsPortReceivedData[2]));
|
||||
}
|
||||
else if(type == "state_of_contactor")
|
||||
{
|
||||
//modify table relays
|
||||
dbRelays.modify({ contactor: newPinValue }).where("line", line);
|
||||
}
|
||||
|
||||
values[obj.type] = value;
|
||||
|
||||
if(conversionTable.hasOwnProperty(pinIndex))
|
||||
{
|
||||
let insertIntoTb = false;
|
||||
if(newPinValue != previousValues[pinIndex]) insertIntoTb = true;
|
||||
if(obj.hasOwnProperty("state_of_contactor")) insertIntoTb = true;
|
||||
|
||||
if(insertIntoTb)
|
||||
{
|
||||
dataToTb = {
|
||||
[tbname]: [
|
||||
{
|
||||
"ts": Date.now(),
|
||||
"values": values
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
instance.send(1, dataToTb);
|
||||
}
|
||||
}
|
||||
|
||||
//pin was changed
|
||||
previousValues[pinIndex] = newPinValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
120
flow/test.js
Normal file
120
flow/test.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
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());
|
||||
79
flow/trigger.js
Normal file
79
flow/trigger.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
exports.id = 'trigger';
|
||||
exports.title = 'Trigger';
|
||||
exports.group = 'Inputs';
|
||||
exports.color = '#F6BB42';
|
||||
exports.click = true;
|
||||
exports.output = 1;
|
||||
exports.version = '1.1.0';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.icon = 'play';
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="dropdown__datatype__items:,String|string,Integer|integer,Float|float,Boolean|boolean,Date|date,Object|object,Base64 as Buffer|buffer" class="m">@(Data type (String by default))</div>
|
||||
<div data-jc="textbox__data__placeholder:@(e.g. Hello world or { hello: 'world'} or ['hello', 'world']))" class="m">@(Data)</div>
|
||||
<div data-jc="checkbox__restart">Trigger 5s after initialization.</div>
|
||||
<div class="help">@(Useful when there's a need to run certain flow when the app restarts, etc.)</div>
|
||||
</div>`;
|
||||
|
||||
exports.readme = `# Trigger
|
||||
|
||||
- Clicking on the component starts the chain
|
||||
- Settings allows to set a data-type and a value`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var value;
|
||||
|
||||
instance.on('click', () => instance.send2(value));
|
||||
|
||||
instance.reconfigure = function() {
|
||||
var options = instance.options;
|
||||
value = null;
|
||||
switch (options.datatype) {
|
||||
case 'integer':
|
||||
value = options.data.parseInt2('error');
|
||||
value = value === 'error' ? NaN : value;
|
||||
break;
|
||||
case 'float':
|
||||
value = options.data.parseFloat2('error');
|
||||
value = value === 'error' ? NaN : value;
|
||||
break;
|
||||
case 'date':
|
||||
options.data = options.data.toString();
|
||||
var num = options.data.parseInt('error');
|
||||
num === 'error' && (num = options.data.parseDate('error'));
|
||||
num === 'error' && (num = null);
|
||||
value = num ? new Date(num).toUTCString() : num;
|
||||
break;
|
||||
case 'object':
|
||||
try {
|
||||
value = (new Function('return ' + options.data))();
|
||||
} catch (e) {
|
||||
instance.error(e);
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
value = options.data.parseBoolean();
|
||||
break;
|
||||
case 'buffer':
|
||||
try {
|
||||
value = U.createBuffer(options.data);
|
||||
} catch (e) {
|
||||
instance.error(e);
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
default:
|
||||
value = '' + (options.data || '');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
instance.on('options', instance.reconfigure);
|
||||
instance.reconfigure();
|
||||
|
||||
if (instance.options.restart)
|
||||
setTimeout(function(){
|
||||
instance.send2(value);
|
||||
}, 5000);
|
||||
};
|
||||
0
flow/variables.txt
Normal file
0
flow/variables.txt
Normal file
43
flow/virtualwirein.js
Normal file
43
flow/virtualwirein.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
exports.id = 'virtualwirein';
|
||||
exports.title = 'Virtual wire in';
|
||||
exports.version = '1.0.0';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.color = '#303E4D';
|
||||
exports.icon = 'sign-in';
|
||||
exports.input = false;
|
||||
exports.output = 1;
|
||||
exports.options = {};
|
||||
exports.readme = `# Virtual wire in
|
||||
|
||||
When the wires between the components are mess it's time to use Virtual wire.`;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="textbox" data-jc-path="wirename" data-jc-config="required:true;placeholder:@(some identifier)" class="m">@(Wire name)</div>
|
||||
</div>
|
||||
<script>
|
||||
ON('save.virtualwirein', function(component, options) {
|
||||
!component.name && (component.name = options.wirename);
|
||||
});
|
||||
WATCH('settings.virtualwirein.wirename', function(path, value, type){
|
||||
if (type === 2)
|
||||
SET('settings.virtualwirein.wirename', value.slug());
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
instance.custom.reconfigure = function(options){
|
||||
if (instance.options.wirename) {
|
||||
instance.status(instance.options.wirename);
|
||||
} else
|
||||
instance.status('Not configured', 'red');
|
||||
};
|
||||
|
||||
ON('virtualwire', function(wirename, flowdata){
|
||||
if (instance.options.wirename && instance.options.wirename === wirename)
|
||||
instance.send(flowdata);
|
||||
});
|
||||
|
||||
instance.on('options', instance.custom.reconfigure);
|
||||
instance.custom.reconfigure();
|
||||
};
|
||||
41
flow/virtualwireout.js
Normal file
41
flow/virtualwireout.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
exports.id = 'virtualwireout';
|
||||
exports.title = 'Virtual wire out';
|
||||
exports.version = '1.0.0';
|
||||
exports.author = 'Martin Smola';
|
||||
exports.color = '#303E4D';
|
||||
exports.icon = 'sign-out';
|
||||
exports.input = true;
|
||||
exports.options = {};
|
||||
exports.readme = `# Virtual wire out
|
||||
|
||||
When the wires between the components are mess it's time to use Virtual wire.`;
|
||||
|
||||
exports.html = `<div class="padding">
|
||||
<div data-jc="textbox" data-jc-path="wirename" class="m" data-jc-config="required:true;placeholder:@(some identifier)">@(Wire name)</div>
|
||||
</div>
|
||||
<script>
|
||||
ON('save.virtualwireout', function(component, options) {
|
||||
!component.name && (component.name = options.wirename);
|
||||
});
|
||||
WATCH('settings.virtualwireout.wirename', function(path, value, type){
|
||||
if (type === 2)
|
||||
SET('settings.virtualwireout.wirename', value.slug());
|
||||
});
|
||||
</script>`;
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
instance.custom.reconfigure = function(){
|
||||
if (instance.options.wirename) {
|
||||
instance.status(instance.options.wirename);
|
||||
} else
|
||||
instance.status('Not configured', 'red');
|
||||
};
|
||||
|
||||
instance.on('data', function(flowdata) {
|
||||
EMIT('virtualwire', instance.options.wirename, flowdata);
|
||||
});
|
||||
|
||||
instance.on('options', instance.custom.reconfigure);
|
||||
instance.custom.reconfigure();
|
||||
};
|
||||
551
flow/wsmqttpublish.js
Normal file
551
flow/wsmqttpublish.js
Normal file
|
|
@ -0,0 +1,551 @@
|
|||
exports.id = 'wsmqttpublish';
|
||||
exports.title = 'WS MQTT publish';
|
||||
exports.group = 'MQTT';
|
||||
exports.color = '#888600';
|
||||
exports.version = '1.0.2';
|
||||
exports.icon = 'sign-out';
|
||||
exports.input = 1;
|
||||
exports.output = ["red", "white", "blue"];
|
||||
exports.author = 'Daniel Segeš';
|
||||
exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" };
|
||||
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:false" class="m">Hostname or IP address (if not empty - setting will override db setting)</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 = `
|
||||
# WS MQTT Publish
|
||||
|
||||
Version 1.0.3.
|
||||
|
||||
Added:
|
||||
- database collections,
|
||||
- rpc response
|
||||
`;
|
||||
|
||||
const instanceSendTo = {
|
||||
debug: 0,
|
||||
rpcCall: 1,
|
||||
services: 2
|
||||
}
|
||||
|
||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||||
|
||||
//CONFIG
|
||||
let useLog4js = true;
|
||||
let createTelemetryBackup = true;
|
||||
let saveTelemetryOnError = true;//backup_on_failure overrides this value
|
||||
//------------------------
|
||||
|
||||
var fs = require('fs');
|
||||
let rollers;
|
||||
if(createTelemetryBackup) rollers = require('streamroller');
|
||||
|
||||
const noSqlFileSizeLimit = 4194304;//use 5MB - 4194304
|
||||
let insertNoSqlCounter = 0;
|
||||
let insertBackupNoSqlCounter = 0;
|
||||
let processingData = false;
|
||||
|
||||
let backup_on_failure = false;//== saveTelemetryOnError - create backup broker send failure
|
||||
let restore_from_backup = 0; //how many rows process at once?
|
||||
let restore_backup_wait = 0;//wait seconds
|
||||
let lastRestoreTime = 0;
|
||||
|
||||
let errLogger;
|
||||
let logger;
|
||||
let monitor;
|
||||
|
||||
if(useLog4js)
|
||||
{
|
||||
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' }
|
||||
}
|
||||
});
|
||||
|
||||
errLogger = log4js.getLogger("errLogs");
|
||||
logger = log4js.getLogger();
|
||||
monitor = log4js.getLogger("monitorLogs");
|
||||
|
||||
//USAGE
|
||||
//logger.debug("text");
|
||||
//monitor.info('info');
|
||||
//errLogger.error("some error");
|
||||
}
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
|
||||
if(errLogger)
|
||||
{
|
||||
errLogger.error('uncaughtException:', err.message)
|
||||
errLogger.error(err.stack);
|
||||
}
|
||||
|
||||
//TODO
|
||||
//send to service
|
||||
|
||||
//process.exit(1);
|
||||
})
|
||||
|
||||
const nosql = NOSQL('tbdata');
|
||||
const nosqlBackup = NOSQL('/backup/tbdata');
|
||||
|
||||
exports.install = function(instance) {
|
||||
|
||||
var broker;
|
||||
var opts;
|
||||
var brokerready = false;
|
||||
|
||||
instance.on('options', loadSettings);
|
||||
|
||||
mqtt = require('mqtt');
|
||||
|
||||
// wsmqtt status for notification purposes on projects.worksys.io database
|
||||
let wsmqttName = null;
|
||||
let sendWsStatusVar = null;
|
||||
let wsmqtt_status = 'disconnected';
|
||||
|
||||
function getWsmqttName(host)
|
||||
{
|
||||
if(host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo';
|
||||
else if(host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01';
|
||||
else if(host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01';
|
||||
}
|
||||
|
||||
function sendWsStatus()
|
||||
{
|
||||
instance.send(instanceSendTo.services, {[wsmqttName]: wsmqtt_status});
|
||||
}
|
||||
|
||||
sendWsStatusVar = setInterval(sendWsStatus, 180000);
|
||||
|
||||
|
||||
//set opts according to db settings
|
||||
async function loadSettings()
|
||||
{
|
||||
|
||||
if(instance.options.host !== "")
|
||||
{
|
||||
//override settings from database
|
||||
var o = instance.options;
|
||||
opts = {
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
clientId: o.clientid,
|
||||
username: o.username,
|
||||
rejectUnauthorized: false,
|
||||
resubscribe: false
|
||||
};
|
||||
|
||||
wsmqttName = getWsmqttName(o.host);
|
||||
|
||||
console.log("wsmqttpublich -> loadSettings from instance.options", instance.options);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
const dbSettings = TABLE("settings");
|
||||
let responseSettings = await promisifyBuilder(dbSettings.find());
|
||||
|
||||
backup_on_failure = responseSettings[0]["backup_on_failure"];
|
||||
saveTelemetryOnError = backup_on_failure;
|
||||
|
||||
restore_from_backup = responseSettings[0]["restore_from_backup"];
|
||||
restore_backup_wait = responseSettings[0]["restore_backup_wait"];
|
||||
|
||||
let mqtt_host = responseSettings[0]["mqtt_host"];
|
||||
let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
|
||||
let mqtt_username = responseSettings[0]["mqtt_username"];
|
||||
let mqtt_port = responseSettings[0]["mqtt_port"];
|
||||
|
||||
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
|
||||
|
||||
opts = {
|
||||
host: mqtt_host,
|
||||
port: mqtt_port,
|
||||
keepalive: 10,
|
||||
clientId: mqtt_clientid,
|
||||
username: mqtt_username,
|
||||
rejectUnauthorized: false,
|
||||
resubscribe: false
|
||||
};
|
||||
|
||||
wsmqttName = getWsmqttName(mqtt_host);
|
||||
}
|
||||
|
||||
connectToTbServer();
|
||||
|
||||
}
|
||||
|
||||
function connectToTbServer()
|
||||
{
|
||||
var url = "mqtt://" + opts.host + ":" + opts.port;
|
||||
console.log("MQTT URL: ", url);
|
||||
|
||||
broker = mqtt.connect(url, opts);
|
||||
|
||||
broker.on('connect', function() {
|
||||
instance.status("Connected", "green");
|
||||
brokerready = true;
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
wsmqtt_status = 'connected';
|
||||
});
|
||||
|
||||
broker.on('reconnect', function() {
|
||||
instance.status("Reconnecting", "yellow");
|
||||
brokerready = false;
|
||||
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
});
|
||||
|
||||
broker.on('message', function(topic, message) {
|
||||
// message is type of buffer
|
||||
message = message.toString();
|
||||
if (message[0] === '{') {
|
||||
TRY(function() {
|
||||
|
||||
message = JSON.parse(message);
|
||||
if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) {
|
||||
broker.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, {qos:1});
|
||||
instance.send(instanceSendTo.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}});
|
||||
}
|
||||
|
||||
}, () => instance.debug('MQTT: Error parsing data', message));
|
||||
}
|
||||
|
||||
instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message });
|
||||
|
||||
});
|
||||
|
||||
broker.on('close', function(err) {
|
||||
brokerready = false;
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
wsmqtt_status = 'disconnected';
|
||||
|
||||
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 });
|
||||
}
|
||||
});
|
||||
|
||||
broker.on('error', function(err) {
|
||||
instance.status("Err: "+ err.code, "red");
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts });
|
||||
|
||||
brokerready = false;
|
||||
FLOW.OMS_brokerready = brokerready;
|
||||
wsmqtt_status = 'disconnected';
|
||||
|
||||
});
|
||||
|
||||
//broker = new Broker(opts);
|
||||
//MQTT_BROKERS.push(broker);
|
||||
|
||||
//instance.status('Ready');
|
||||
}
|
||||
|
||||
//set opts accortding to options
|
||||
/*
|
||||
instance.reconfigure = function() {
|
||||
|
||||
|
||||
var o = instance.options;
|
||||
opts = {
|
||||
host: o.host,
|
||||
port: o.port,
|
||||
keepalive: 10,
|
||||
clientId: o.clientid,
|
||||
username: o.username,
|
||||
rejectUnauthorized: false,
|
||||
resubscribe: false
|
||||
};
|
||||
|
||||
//connectToTbServer();
|
||||
};
|
||||
*/
|
||||
|
||||
instance.on('data', function(data) {
|
||||
|
||||
if (brokerready)
|
||||
{
|
||||
//do we have some data in backup file?
|
||||
//if any, process data from database
|
||||
if(saveTelemetryOnError)
|
||||
{
|
||||
//read telemetry data and send back to server
|
||||
if(!processingData) processDataFromDatabase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (brokerready)
|
||||
{
|
||||
let stringifiedJson = JSON.stringify(data.data);
|
||||
broker.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1});
|
||||
|
||||
//backup telemetry
|
||||
if(createTelemetryBackup)
|
||||
{
|
||||
data.data.id = UID();
|
||||
nosqlBackup.insert(data.data);
|
||||
|
||||
insertBackupNoSqlCounter++;
|
||||
if(insertBackupNoSqlCounter > 150)
|
||||
{
|
||||
let options = {compress: true};
|
||||
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
||||
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
||||
stream.write("");
|
||||
stream.end();
|
||||
|
||||
insertBackupNoSqlCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(logger) logger.debug("Broker unavailable. Data not sent !", data.data);
|
||||
instance.send(instanceSendTo.debug, {"message":"Broker unavailable. Data not sent !", "data": data.data });
|
||||
|
||||
if(saveTelemetryOnError)
|
||||
{
|
||||
//create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql
|
||||
makeBackupFromDbFile();
|
||||
|
||||
//write to tb
|
||||
data.data.id = UID();
|
||||
nosql.insert(data.data);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
instance.close = function(done) {
|
||||
if (brokerready){
|
||||
broker.end();
|
||||
clearInterval(sendWsStatusVar);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function getDbBackupFileCounter(type)
|
||||
{
|
||||
var files = fs.readdirSync(__dirname + "/../databases");
|
||||
|
||||
let counter = 0;
|
||||
for(var i = 0; i < files.length; i++)
|
||||
{
|
||||
|
||||
if(files[i] == "tbdata.nosql") continue;
|
||||
|
||||
if(files[i].endsWith(".nosql"))
|
||||
{
|
||||
|
||||
let pos = files[i].indexOf(".");
|
||||
if(pos > -1)
|
||||
{
|
||||
|
||||
let fileCounter = counter;
|
||||
let firstDigit = files[i].slice(0, pos);
|
||||
|
||||
fileCounter = parseInt(firstDigit);
|
||||
if (isNaN(fileCounter)) fileCounter = 0;
|
||||
//console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type);
|
||||
|
||||
if(type == "max")
|
||||
{
|
||||
if(fileCounter > counter)
|
||||
{
|
||||
counter = fileCounter;
|
||||
}
|
||||
}
|
||||
else if(type == "min")
|
||||
{
|
||||
if(counter == 0) counter = fileCounter;
|
||||
|
||||
if(fileCounter < counter)
|
||||
{
|
||||
counter = fileCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(type == "max") counter++;
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
const makeBackupFromDbFile = async () => {
|
||||
|
||||
if(!saveTelemetryOnError) return;
|
||||
|
||||
//to avoid large file: tbdata.nosql
|
||||
|
||||
//init value is 0!
|
||||
if(insertNoSqlCounter > 0)
|
||||
{
|
||||
--insertNoSqlCounter;
|
||||
return;
|
||||
}
|
||||
|
||||
insertNoSqlCounter = 100;
|
||||
|
||||
let source = __dirname + "/../databases/tbdata.nosql";
|
||||
|
||||
var stats = fs.statSync(source);
|
||||
var fileSizeInBytes = stats.size;
|
||||
|
||||
if(fileSizeInBytes > noSqlFileSizeLimit)
|
||||
{
|
||||
|
||||
let counter = 1;
|
||||
counter = getDbBackupFileCounter("max");
|
||||
|
||||
let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql";
|
||||
|
||||
//make backup file
|
||||
fs.copyFileSync(source, destination);
|
||||
//fs.renameSync(p, p + "." + counter);
|
||||
|
||||
//clear tbdata.nosql
|
||||
fs.writeFileSync(source, "");
|
||||
fs.truncateSync(source, 0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const processDataFromDatabase = async () => {
|
||||
|
||||
if(restore_from_backup <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//calculate diff
|
||||
const now = new Date();
|
||||
let currentTime = now.getTime();
|
||||
let diff = currentTime - lastRestoreTime;
|
||||
|
||||
if( (diff / 1000) < restore_backup_wait)
|
||||
{
|
||||
//console.log("*********restore_backup_wait", diff, restore_backup_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
processingData = true;
|
||||
|
||||
//get filename to process
|
||||
let counter = getDbBackupFileCounter("min");
|
||||
|
||||
//we have some backup files
|
||||
let dataBase = 'tbdata';
|
||||
|
||||
var nosql;
|
||||
if(counter == 0) dataBase = 'tbdata';
|
||||
else dataBase = counter + "." + 'tbdata';
|
||||
|
||||
nosql = NOSQL(dataBase);
|
||||
|
||||
//select all data - use limit restore_from_backup
|
||||
let records = await promisifyBuilder(nosql.find().take(restore_from_backup));
|
||||
|
||||
for(let i = 0; i < records.length; i++)
|
||||
{
|
||||
if (brokerready) {
|
||||
|
||||
let item = records[i];
|
||||
let id = item.id;
|
||||
|
||||
if(id !== undefined)
|
||||
{
|
||||
//console.log("------------processDataFromDatabase - remove", id, dataBase, i);
|
||||
|
||||
try{
|
||||
|
||||
let o = JSON.parse(JSON.stringify(item));
|
||||
delete o.id;
|
||||
let message = JSON.stringify(o);
|
||||
|
||||
broker.publish("v1/gateway/telemetry", message, {qos:1});
|
||||
|
||||
//remove from database
|
||||
await promisifyBuilder(nosql.remove().where("id", id));
|
||||
|
||||
} catch (error) {
|
||||
//process error
|
||||
console.log("processDataFromDatabase", error);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
processingData = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(records.length > 0)
|
||||
{
|
||||
//clean backup file
|
||||
if(counter > 0) nosql.clean();
|
||||
}
|
||||
|
||||
//no data in db, remove
|
||||
if(records.length == 0)
|
||||
{
|
||||
if(counter > 0) nosql.drop();
|
||||
}
|
||||
|
||||
const d = new Date();
|
||||
lastRestoreTime = d.getTime();
|
||||
|
||||
processingData = false;
|
||||
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
||||
//instance.on('options', instance.reconfigure);
|
||||
//instance.reconfigure();
|
||||
};
|
||||
0
monitor.txt
Normal file
0
monitor.txt
Normal file
27
package.json
Normal file
27
package.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "emptyproject",
|
||||
"description": "Total.js empty project.",
|
||||
"version": "1.0.0",
|
||||
"main": "debug.js",
|
||||
"dependencies": {
|
||||
"easy-crc": "0.0.2",
|
||||
"mqtt": "^4.2.6",
|
||||
"serialport": "^9.0.0",
|
||||
"total.js": "^3.4.5",
|
||||
"bitwise": "^2.1.0",
|
||||
"log4js": "^6.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/totaljs/emptyproject-flow"
|
||||
},
|
||||
"keywords": [
|
||||
"empty",
|
||||
"project"
|
||||
],
|
||||
"author": "Peter Širka",
|
||||
"license": "MIT"
|
||||
}
|
||||
15
readme.md
Normal file
15
readme.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[](https://www.totaljs.com/support/)
|
||||
|
||||
- [__Live chat with professional support__](https://messenger.totaljs.com)
|
||||
- [__HelpDesk with professional support__](https://helpdesk.totaljs.com)
|
||||
- [Documentation](https://docs.totaljs.com)
|
||||
- [Wiki](https://wiki.totaljs.com)
|
||||
|
||||
# Total.js: Empty Project - Flow
|
||||
|
||||
- download example
|
||||
- open terminal / command-line
|
||||
- open app directory
|
||||
- install latest version of Total.js from NPM `$ npm install total.js`
|
||||
- run `$ node debug.js`
|
||||
- open browser `http://127.0.0.1:8000`
|
||||
135
saved_data/modbus_settings
Normal file
135
saved_data/modbus_settings
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
{
|
||||
"config": {
|
||||
"isRunning": false,
|
||||
"debug": true,
|
||||
"timeoutTime": 4000,
|
||||
"msgWaitTime": 20000,
|
||||
"port": "/dev/ttymxc1",
|
||||
"port_options": "stty -F /dev/ttymxc1 9600 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke"
|
||||
},
|
||||
"private": {
|
||||
"errBuffer": [],
|
||||
"tbBuffer": [],
|
||||
"device_index": 0,
|
||||
"cmd_index": 0,
|
||||
"devices": [{
|
||||
"name": "Elektrometer 1",
|
||||
"tb_name": "",
|
||||
"type": "EM340",
|
||||
"address": 1,
|
||||
"data": [],
|
||||
"cmd": [],
|
||||
"timeoutcount": 0,
|
||||
"status": "virtual"
|
||||
}],
|
||||
"cmd_tables": [{
|
||||
"type": "EM340",
|
||||
"cmd": [{
|
||||
"name": "Voltage L1",
|
||||
"tb_name": "Phase_1_voltage",
|
||||
"register": 0,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Voltage L2",
|
||||
"tb_name": "Phase_2_voltage",
|
||||
"register": 2,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Voltage L3",
|
||||
"tb_name": "Phase_3_voltage",
|
||||
"register": 4,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Current L1",
|
||||
"tb_name": "Phase_1_current",
|
||||
"register": 12,
|
||||
"size": 2,
|
||||
"multiplier": 0.001
|
||||
},
|
||||
{
|
||||
"name": "Current L2",
|
||||
"tb_name": "Phase_2_current",
|
||||
"register": 14,
|
||||
"size": 2,
|
||||
"multiplier": 0.001
|
||||
},
|
||||
{
|
||||
"name": "Current L3",
|
||||
"tb_name": "Phase_3_current",
|
||||
"register": 16,
|
||||
"size": 2,
|
||||
"multiplier": 0.001
|
||||
},
|
||||
{
|
||||
"name": "Power L1",
|
||||
"tb_name": "Phase_1_power",
|
||||
"register": 18,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Power L2",
|
||||
"tb_name": "Phase_2_power",
|
||||
"register": 20,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Power L3",
|
||||
"tb_name": "Phase_3_power",
|
||||
"register": 22,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Power tot",
|
||||
"tb_name": "total_power",
|
||||
"register": 40,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "Energy in",
|
||||
"tb_name": "total_energy",
|
||||
"register": 52,
|
||||
"size": 2,
|
||||
"multiplier": 0.1
|
||||
},
|
||||
{
|
||||
"name": "PowF L1",
|
||||
"tb_name": "Phase_1_pow_factor",
|
||||
"register": 46,
|
||||
"size": 1,
|
||||
"multiplier": 0.001
|
||||
},
|
||||
{
|
||||
"name": "PowF L2",
|
||||
"tb_name": "Phase_2_pow_factor",
|
||||
"register": 47,
|
||||
"size": 1,
|
||||
"multiplier": 0.001
|
||||
},
|
||||
{
|
||||
"name": "PowF L3",
|
||||
"tb_name": "Phase_3_pow_factor",
|
||||
"register": 48,
|
||||
"size": 1,
|
||||
"multiplier": 0.001
|
||||
},
|
||||
{
|
||||
"name": "PowF",
|
||||
"tb_name": "power_factor",
|
||||
"register": 49,
|
||||
"size": 1,
|
||||
"multiplier": 0.001
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue