Compare commits
27 commits
master
...
feat/OMS-1
| Author | SHA1 | Date | |
|---|---|---|---|
| 59256ebec6 | |||
| c5c5b21f47 | |||
| 7093d765ec | |||
| cf16481324 | |||
| 1012544193 | |||
| 5540cee8ec | |||
| 0c993f50b1 | |||
| 0876e73c68 | |||
| d97d90cf95 | |||
| 5233aa38af | |||
| 73a2620add | |||
| 4c59ccd095 | |||
| fad53c9c01 | |||
| 75bb2794d2 | |||
| 613d846dbe | |||
| 63504df84d | |||
| c052887a1f | |||
| ef7817bf22 | |||
| 41d1ec28dd | |||
| 31b5dbba5c | |||
| 5270a898a3 | |||
| f63ac50497 | |||
| ed0fe5b15d | |||
| 1c131d0b63 | |||
| ca39e1266c | |||
| 1b4b9ca973 | |||
| 880edfc604 |
39 changed files with 8948 additions and 5852 deletions
36
addSwitch.py
Normal file
36
addSwitch.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
def process_set_file():
|
||||||
|
"""
|
||||||
|
Checks if /root/flowserver exists, reads set.txt, and modifies the second line.
|
||||||
|
"""
|
||||||
|
default_folder = "/root/flowserver" if os.path.exists("/root/flowserver") else "/home/unipi/flowserver"
|
||||||
|
flag = 1 if default_folder == "/root/flowserver" else 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("/home/unipi/flowserver/databases/settings.table", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
if len(lines) >= 2:
|
||||||
|
lines[0] = lines[0].rstrip('\n') + "|has_main_switch:boolean\n"
|
||||||
|
second_line = lines[1].strip() # remove trailing newline
|
||||||
|
last_pipe_index = second_line.rfind("|")
|
||||||
|
|
||||||
|
if last_pipe_index != -1:
|
||||||
|
modified_line = second_line[:last_pipe_index + 1] + str(flag) + "|" + second_line[last_pipe_index + 1:]
|
||||||
|
lines[1] = modified_line
|
||||||
|
else:
|
||||||
|
print("Warning: No '|' character found in the second line of set.txt")
|
||||||
|
|
||||||
|
with open("/home/unipi/flowserver/databases/settings.table", "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
else:
|
||||||
|
print("Warning: settings.table has less than two lines.")
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("Error: settings.table not found.")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
process_set_file()
|
||||||
76
cloud_topic.py
Normal file
76
cloud_topic.py
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# import os
|
||||||
|
#
|
||||||
|
# def modify_file(file_path):
|
||||||
|
# """
|
||||||
|
# Modifies the given file by:
|
||||||
|
# 1. Appending "|cloud_topic" to the first line.
|
||||||
|
# 2. Inserting the text from the third "." to the first "|" on the second line after the last "|" character.
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# file_path (str): The path to the file to be modified.
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# with open(file_path, 'r+') as f:
|
||||||
|
# lines = f.readlines()
|
||||||
|
#
|
||||||
|
# # Modify the first line
|
||||||
|
# lines[0] += "|cloud_topic:string"
|
||||||
|
#
|
||||||
|
# # Modify the second line
|
||||||
|
# second_line = lines[1].strip() # Remove leading/trailing whitespace
|
||||||
|
# first_pipe_index = second_line.find('|')
|
||||||
|
# third_dot_index = second_line.find('.', second_line.find('.', second_line.find('.') + 1) + 1)
|
||||||
|
# text_to_insert = second_line[third_dot_index:first_pipe_index]
|
||||||
|
#
|
||||||
|
# last_pipe_index = second_line.rfind('|')
|
||||||
|
# lines[1] = second_line[:last_pipe_index + 1] + text_to_insert + "|" + second_line[last_pipe_index + 1:]
|
||||||
|
#
|
||||||
|
# print(first_pipe_index, third_dot_index, text_to_insert, last_pipe_index)
|
||||||
|
# # Write the modified lines back to the file
|
||||||
|
# # f.seek(0)
|
||||||
|
# # f.writelines(lines)
|
||||||
|
# # f.truncate()
|
||||||
|
#
|
||||||
|
# # Example usage:
|
||||||
|
# file_path = "settings.table" # Replace with the actual file path
|
||||||
|
# modify_file(file_path)
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
def modify_file(file_path):
|
||||||
|
"""
|
||||||
|
Modifies the given file by:
|
||||||
|
1. Appending "|cloud_topic" to the first line.
|
||||||
|
2. Inserting the text between the third "." and the second "|" on the second line after the last "|" character.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path (str): The path to the file to be modified.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open(file_path, 'r+') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
first_line = lines[0].strip()
|
||||||
|
first_line += "|cloud_topic:string\n"
|
||||||
|
# Modify the first line
|
||||||
|
lines[0] = first_line
|
||||||
|
|
||||||
|
# Modify the second line
|
||||||
|
second_line = lines[1].strip() # Remove leading/trailing whitespace
|
||||||
|
first_pipe_index = second_line.find('|')
|
||||||
|
second_pipe_index = second_line.find('|', first_pipe_index + 1)
|
||||||
|
third_dot_index = second_line.find('.', second_line.find('.', second_line.find('.') + 1) + 1)
|
||||||
|
text_to_insert = "u" + second_line[third_dot_index+1:second_pipe_index]
|
||||||
|
|
||||||
|
last_pipe_index = second_line.rfind('|')
|
||||||
|
lines[1] = second_line[:last_pipe_index + 1] + text_to_insert + "|" + second_line[last_pipe_index + 1:]
|
||||||
|
|
||||||
|
print(first_pipe_index, third_dot_index, text_to_insert, last_pipe_index)
|
||||||
|
# Write the modified lines back to the file
|
||||||
|
f.seek(0)
|
||||||
|
f.writelines(lines)
|
||||||
|
f.truncate()
|
||||||
|
|
||||||
|
# Example usage:
|
||||||
|
file_path = "/home/unipi/flowserver/databases/settings.table" # Replace with the actual file path
|
||||||
|
modify_file(file_path)
|
||||||
4
config
4
config
|
|
@ -6,7 +6,7 @@ package#flow (Object) : { url: '/' }
|
||||||
|
|
||||||
|
|
||||||
table.relays : line:number|tbname:string|contactor:number|profile:string
|
table.relays : line:number|tbname:string|contactor:number|profile:string
|
||||||
table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
table.nodes : node:number|pole_number:string|node_type:string|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number
|
||||||
table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
|
table.settings : rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string|has_main_switch:boolean|daily_report:boolean
|
||||||
table.pins : pin:string|type:string|line:number
|
table.pins : pin:string|type:string|line:number
|
||||||
table.notifications : key:string|weight:string|sk:string|en:string
|
table.notifications : key:string|weight:string|sk:string|en:string
|
||||||
|
|
|
||||||
43
createNode.py
Normal file
43
createNode.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
print("zaciname")
|
||||||
|
import re, json
|
||||||
|
|
||||||
|
search_str = '|'
|
||||||
|
final = []
|
||||||
|
counter = 1
|
||||||
|
with open("/home/unipi/flowserver/databases/nodes.table", 'r') as file:
|
||||||
|
# with open("/home/rasta5man/dev/oms/flowserver/databases/nodes.table", 'r') as file:
|
||||||
|
# Read each line in the file
|
||||||
|
for line in file:
|
||||||
|
# Print each line
|
||||||
|
line = line.strip()
|
||||||
|
print(line)
|
||||||
|
if counter != 1:
|
||||||
|
i = [m.start() for m in re.finditer(re.escape(search_str), line)]
|
||||||
|
node = line[ i[0] + 1 : i[1] ]
|
||||||
|
tbname = line[ i[3] + 1 : i[4] ]
|
||||||
|
final.append({node:tbname})
|
||||||
|
counter += 1
|
||||||
|
print(json.dumps(final))
|
||||||
|
f = open("/home/unipi/flowserver/databases/nodes_original/nodes_original.table", "w")
|
||||||
|
f.write(json.dumps(final))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# # ``d`` has to be replaced with a different character
|
||||||
|
# old_character = "'"
|
||||||
|
#
|
||||||
|
# # ``t`` will replace ``d`
|
||||||
|
# new_character = '"'
|
||||||
|
# resultant_string = 0;
|
||||||
|
# with open("/home/unipi/flowserver/databases/nodes_original/nodes_original.table", 'r') as file:
|
||||||
|
# for line in file:
|
||||||
|
# resultant_string = re.sub("'", '"', line)
|
||||||
|
#
|
||||||
|
# resultant_string = re.sub(" ", "", resultant_string)
|
||||||
|
# print(resultant_string)
|
||||||
|
#
|
||||||
|
# f = open("/home/unipi/flowserver/databases/nodes_original/nodes_original.table", "w")
|
||||||
|
# f.write(str(resultant_string))
|
||||||
|
# f.close()
|
||||||
|
#
|
||||||
3055
databases/accelerometer_db.js
Normal file
3055
databases/accelerometer_db.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,31 +1,2 @@
|
||||||
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number
|
||||||
+|3522|RO8rjaBDy21qPQJzW7oD96ApK3xmNleVZg9Ed4Gw|1||1|1|...........
|
+|638|rDbQ84xzwgdqEoPm3kbJQWk9anOZY1RXyBv2LVM6|3|{"intervals":[{"cct":3000,"value":20,"end_time":"20:00","start_time":"13:00"},{"cct":3000,"value":10,"end_time":"05:30","start_time":"20:00"},{"cct":3000,"value":20,"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":-20,"dusk_astro_clock_offset":20,"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|1725885127396|............................................................................................................................................................................................................................................................
|
||||||
+|4018|3JjOWdylwgNLzxVab7NEznkZ2vG64rq8PEB5QmDo|1||1|1|...........
|
|
||||||
+|4019|Z5KyJe9nEg1QNbWlX0wWRB0oDjBLdqzR83VGv624|1||1|1|...........
|
|
||||||
+|4154|1JMYvnx2RzKEo4aWQ7DmN5AL8yZV3m9NBePXbrdj|1||1|0|...........
|
|
||||||
+|3907|PjLblDgRBO6WQqnxmkJ59r0Jv3ewZN4p5a89yKdY|1||1|1|...........
|
|
||||||
+|4148|dz4ojlpP85JMgDLZWkQOoGAaKYqQexEr62GXRV1y|1||1|1|...........
|
|
||||||
+|4153|d5xjWYMwEJon6rLlK7yBYmAqgV4DaOeNB9ZX3Gzb|1||1|1|...........
|
|
||||||
+|3938|gRoJEyXVx4qD9er287LP1v7wBzGldaPjLWQKm3Mv|1||1|1|...........
|
|
||||||
+|3802|K94XLav1glVRnyQ6r01BNzkme3YJwBxM5oOzdP2j|1||1|1|...........
|
|
||||||
+|4015|d9x2V5LGYBzXp4mMRAOBDj7PloaqJwnQj6DgrNe3|1||1|0|...........
|
|
||||||
+|3929|B5EoxeMVp4zwr8nqW0GjDpARjvD1PNamOGbLg63Z|1||1|1|...........
|
|
||||||
+|3946|aw4eELG2DlPMdn1JW0B1DnAqQXOZRN3xB5yp8VKr|1||1|1|...........
|
|
||||||
+|4014|ZmRwd93QL4gaezxEbAxW5971prn2XjlPvGyqJ6BO|1||1|1|...........
|
|
||||||
+|4155|eod9aRWLVl34Gx1Dn7VoaaA2rz6qjgmpEXwQJN5Z|1||1|1|...........
|
|
||||||
+|4149|3a5oqJN1bgnx4Ol9dk86NBAByE6jQ8mKDWMpGrLV|1||1|1|...........
|
|
||||||
+|3642|EjgWGnXaLy9opPOz20n694086BlYM3w1deVQvbKr|1||1|1|...........
|
|
||||||
+|3636|wvKJdZML6mXP4DzWBAXWNW7jxNloa5g23Ve9Y1ry|1||1|1|...........
|
|
||||||
+|3991|Nzp2OoJlqn6r1ZgvdA3GWdAabBwP5G4eE3RQmyxD|1||1|1|...........
|
|
||||||
+|3994|PLBJzmK1r3Gynd6OW0gGdM0e5wV4vx9bDEqNgYR8|1||1|1|...........
|
|
||||||
+|3990|52dD6ZlV1QaOpRBmbAqKZgkKnGzWMLj4eJq38Pgo|1||1|1|...........
|
|
||||||
+|3967|rDbQ84xzwgdqEoPm3kbJw3k9anOZY1RXyBv2LVM6|1||1|1|...........
|
|
||||||
+|3977|E6Kg9oDnLWyzPRMva7vrwa7Jxp4VG58qO2w1lZYe|1||1|1|...........
|
|
||||||
+|3757|roKgWqY95V3mXMRzyAjm8D7bLjexpJPvaGDBw826|1||1|1|...........
|
|
||||||
+|3633|nJL5lPMwBx23YpqRe0rlKV7damXvWVbOrD4gNzy8|1||1|1|...........
|
|
||||||
+|3744|ZmRwd93QL4gaezxEbAxW5O71prn2XjlPvGyqJ6BO|1||1|1|...........
|
|
||||||
+|4023|eod9aRWLVl34Gx1Dn7VoaMA2rz6qjgmpEXwQJN5Z|1||1|1|...........
|
|
||||||
+|3720|3a5oqJN1bgnx4Ol9dk86NZAByE6jQ8mKDWMpGrLV|1||1|1|...........
|
|
||||||
+|3734|EjgWGnXaLy9opPOz20n69V086BlYM3w1deVQvbKr|1||1|1|...........
|
|
||||||
+|3741|wvKJdZML6mXP4DzWBAXWN17jxNloa5g23Ve9Y1ry|1||1|1|...........
|
|
||||||
+|3721|Nzp2OoJlqn6r1ZgvdA3GWKAabBwP5G4eE3RQmyxD|1||0|0|...........
|
|
||||||
|
|
|
||||||
1
databases/nodes_original/nodes_original.table
Normal file
1
databases/nodes_original/nodes_original.table
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[{"3815": "B5EoxeMVp4zwr8nqW0GjjoARjvD1PNamOGbLg63Z"}, {"3799": "roKgWqY95V3mXMRzyAjmmj7bLjexpJPvaGDBw826"}]
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
key:string|weight:string|sk:string|en:string
|
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}|...............
|
+|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}|...............
|
+|dusk_has_occured|INFORMATIONAL|Nastal súmrak: ${value}|Dusk has occured: ${value}|...............
|
||||||
+|dawn_has_occured|INFORMATIONAL|Nastal úsvit: ${value}|Dawn 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}|...............
|
+|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}|...............
|
||||||
|
|
@ -20,9 +20,12 @@ key:string|weight:string|sk:string|en:string
|
||||||
+|power_supply_works_correctly|NOTICE|Napájací zdroj pracuje správne|Power supply works correctly|...............
|
+|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_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|...............
|
+|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_main_open|NOTICE|Hlavné dvere boli otvorené|Main door has been opened|...............
|
||||||
+|door_has_been_closed|NOTICE|Dvere boli zatvorené|Door has been closed|...............
|
+|door_em_open|NOTICE|Dvere silovej časti boli otvorené|Power door has been opened|...............
|
||||||
+|door_has_been_open_without_permision_alarm_is_on|WARNING|Dvere boli otvorené bez povolania - zapnutá siréna|Door has been open without permision - alarm is on|...............
|
+|door_main_open_without_permission|WARNING|Hlavné dvere boli otvorené bez povolenia - zapnutá siréna|Main door has been opened without permission - alarm is on|...............
|
||||||
|
+|door_em_open_without_permission|WARNING|Dvere silovej časti boli otvorené bez povolenia|Power door has been opened without permission|...............
|
||||||
|
+|door_main_close|NOTICE|Hlavné dvere boli zatvorené|Main door has been closed|...............
|
||||||
|
+|door_em_close|NOTICE|Dvere silovej časti boli zatvorené|Power door has been closed|...............
|
||||||
+|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|...............
|
+|state_of_contactor_for_line|INFORMATIONAL|Stav stýkača pre líniu č. ${line} je ${value}|State of contactor for line no. ${line} is ${value}|...............
|
||||||
+|local_database_is_corrupted|CRITICAL|||...............
|
+|local_database_is_corrupted|CRITICAL|||...............
|
||||||
+|electrometer_nok|ERROR|Elektromer neodpovedá|Electrometer is not responding|...............
|
+|electrometer_nok|ERROR|Elektromer neodpovedá|Electrometer is not responding|...............
|
||||||
|
|
@ -34,4 +37,5 @@ key:string|weight:string|sk:string|en:string
|
||||||
+|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|...............
|
+|twilight_sensor_ok|NOTICE|Sensor súmraku znovu odpovedá|Twilight sensor is responding again|...............
|
||||||
+|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|...............
|
+|lamps_have_turned_on|NOTICE|Lampy sa zapli|Lamps have turned on|...............
|
||||||
+|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|...............
|
+|lamps_have_turned_off|NOTICE|Lampy sa vypli|Lamps have turned off|...............
|
||||||
+|flow_restart|NOTICE|Restart flowu|Flow has been restarted|...............
|
+|flow_restart|NOTICE|FLOW bol reštartovaný|FLOW has been restarted|...............
|
||||||
|
+|nodes_db_changed|NOTICE|Zmena v node databáze|Node db has changed|...............
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
line:number|tbname:string|contactor:number|profile:string
|
line:number|tbname:string|contactor:number|profile:string
|
||||||
+|0|6lQGaY9RDywdVzObj0PadOkPg4NBn3exEK51LWZq|1||...........
|
+|0|6lQGaY9RDywdVzObj0PadOkPg4NBn3exEK51LWZq|1||...........
|
||||||
+|1|JzwxZXOvDj1bVrN4nkWw9Qk8qdyBl3MRKLpGPgaQ|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
+|1|JzwxZXOvDj1bVrN4nkWw9Qk8qdyBl3MRKLpGPgaQ|9|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||||
+|2|g9OxBZ5KRwNznlY6pAp6mxkWXvjdEL4eGQobMDy2|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
+|2|g9OxBZ5KRwNznlY6pAp6mxkWXvjdEL4eGQobMDy2|9|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||||
+|3|OzNMgZ9n43qPbjXmy7zWMJA2DKdYvW5e6pxGRrVa|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
+|3|OzNMgZ9n43qPbjXmy7zWMJA2DKdYvW5e6pxGRrVa|9|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
|
rvo_name:string|lang:string|temperature_address:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|project_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number|node_status_nok_time:number|phases:number|cloud_topic:string|has_main_switch:boolean|daily_report:boolean
|
||||||
+|rvo_senica_39_10.0.0.132|en|28.427B45920702|48.70826502|17.28455203|192.168.252.1|rvo_senica_39_10.0.0.132|qzSNuCNrLP4OL1v47YEe|1883|0|68|unipi|ttyUSB0|1|20|5|...........................................
|
+|rvo_senica_22_ip10.0.0.109|en|28.F46E9D0E0000|48.70826502|17.28455203|192.168.252.1|rvo_senica_22_ip10.0.0.109|9excvr7yBcF3gl3kYZGY|1883|0|48|unipi|ttyUSB0|1|20|5|6|3|u109|0|1|...........................................
|
||||||
|
|
|
||||||
38
databases/total_energy.js
Normal file
38
databases/total_energy.js
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
//key is rvo_number, value is max energy when lamps are on
|
||||||
|
const total_energy = {
|
||||||
|
1: 580,
|
||||||
|
2: 1100,
|
||||||
|
3: 3700,
|
||||||
|
4: 4100,
|
||||||
|
7: 360,
|
||||||
|
12: 1700,
|
||||||
|
13: 5400,
|
||||||
|
14: 440,
|
||||||
|
15: 6100,
|
||||||
|
16: 4800,
|
||||||
|
20: 1600,
|
||||||
|
21: 1000,
|
||||||
|
22: 2600,
|
||||||
|
23: 1000,
|
||||||
|
25: 2600,
|
||||||
|
33: 240,
|
||||||
|
34: 4000,
|
||||||
|
35: 2700,
|
||||||
|
36: 820,
|
||||||
|
37: 1400,
|
||||||
|
35: 3500,
|
||||||
|
39: 1170,
|
||||||
|
41: 740,
|
||||||
|
42: 660,
|
||||||
|
43: 4900,
|
||||||
|
45: 930,
|
||||||
|
46: 700,
|
||||||
|
47: 1100,
|
||||||
|
48: 1500,
|
||||||
|
50: 3200,
|
||||||
|
53: 1250,
|
||||||
|
55: 1000,
|
||||||
|
56: 5500
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = total_energy;
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
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
|
|
||||||
|
357
flow/cloudmqttconnect.js
Normal file
357
flow/cloudmqttconnect.js
Normal file
|
|
@ -0,0 +1,357 @@
|
||||||
|
exports.id = 'cloudmqttconnect';
|
||||||
|
exports.title = 'Cloud connect mqtt';
|
||||||
|
exports.group = 'MQTT';
|
||||||
|
exports.color = '#888600';
|
||||||
|
exports.version = '1.0.2';
|
||||||
|
exports.icon = 'sign-out';
|
||||||
|
exports.input = 2;
|
||||||
|
exports.output = 2;
|
||||||
|
exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" };
|
||||||
|
|
||||||
|
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="topic" class="m">topic</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const { promisifyBuilder } = require('./helper/db_helper');
|
||||||
|
const fs = require('fs');
|
||||||
|
const mqtt = require('mqtt');
|
||||||
|
const nosql = NOSQL('tbdatacloud');
|
||||||
|
|
||||||
|
const SEND_TO = {
|
||||||
|
debug: 0,
|
||||||
|
rpcCall: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
//CONFIG
|
||||||
|
let saveTelemetryOnError = true;//backup_on_failure overrides this value
|
||||||
|
//------------------------
|
||||||
|
|
||||||
|
const noSqlFileSizeLimit = 4194304;//use 5MB - 4194304
|
||||||
|
let insertNoSqlCounter = 0;
|
||||||
|
let insertBackupNoSqlCounter = 0;
|
||||||
|
let processingData = false;
|
||||||
|
|
||||||
|
let backup_on_failure = true;//== saveTelemetryOnError - create backup client send failure
|
||||||
|
let restore_from_backup = 50; //how many rows process at once?
|
||||||
|
let restore_backup_wait = 3;//wait seconds
|
||||||
|
let lastRestoreTime = 0;
|
||||||
|
|
||||||
|
// if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable
|
||||||
|
let sendClientError = true;
|
||||||
|
|
||||||
|
|
||||||
|
exports.install = function(instance) {
|
||||||
|
|
||||||
|
var client;
|
||||||
|
var opts;
|
||||||
|
var clientReady = false;
|
||||||
|
|
||||||
|
let o = {}; //options
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
//set opts according to db settings
|
||||||
|
function loadSettings() {
|
||||||
|
|
||||||
|
o = instance.options;
|
||||||
|
if (!o.topic) o.topic = FLOW.GLOBALS.settings.cloud_topic;
|
||||||
|
|
||||||
|
opts = {
|
||||||
|
host: o.host,
|
||||||
|
port: o.port,
|
||||||
|
clientId: o.clientid,
|
||||||
|
username: o.username,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
resubscribe: false
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("wsmqttpublich -> loadSettings from instance.options", o);
|
||||||
|
|
||||||
|
if (!o.topic) {
|
||||||
|
instance.status("Not configured", "white");
|
||||||
|
console.log("Cloud mqtt connect: no topic selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectToTbServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectToTbServer() {
|
||||||
|
var url = "mqtt://" + opts.host + ":" + opts.port;
|
||||||
|
console.log("MQTT URL: ", url);
|
||||||
|
|
||||||
|
client = mqtt.connect(url, opts);
|
||||||
|
|
||||||
|
client.on('connect', function() {
|
||||||
|
client.subscribe(`${o.topic}_backward`, (err) => {
|
||||||
|
if (!err) {
|
||||||
|
console.log("MQTT subscribed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
instance.status("Connected", "green");
|
||||||
|
clientReady = true;
|
||||||
|
sendClientError = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('reconnect', function() {
|
||||||
|
instance.status("Reconnecting", "yellow");
|
||||||
|
clientReady = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.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")) {
|
||||||
|
client.publish(`${o.topic}_forward`, `{"device": "${message.device}", "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 });
|
||||||
|
instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } });
|
||||||
|
}
|
||||||
|
|
||||||
|
}, () => instance.debug('MQTT: Error parsing data', message));
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.send(SEND_TO.rpcCall, { "topic": o.topic, "content": message });
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('close', function() {
|
||||||
|
clientReady = false;
|
||||||
|
|
||||||
|
instance.status("Disconnected", "red");
|
||||||
|
instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" });
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', function(err) {
|
||||||
|
instance.status("Err: " + err.code, "red");
|
||||||
|
instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts });
|
||||||
|
if (sendClientError) {
|
||||||
|
console.log('MQTT client error', err);
|
||||||
|
sendClientError = false;
|
||||||
|
}
|
||||||
|
clientReady = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
instance.on('0', function(data) {
|
||||||
|
|
||||||
|
if (clientReady) {
|
||||||
|
//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();
|
||||||
|
}
|
||||||
|
|
||||||
|
let stringifiedJson = JSON.stringify(data.data)
|
||||||
|
client.publish(`${o.topic}_forward`, stringifiedJson, { qos: 1 });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data));
|
||||||
|
instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data });
|
||||||
|
|
||||||
|
if (saveTelemetryOnError && o.topic) {
|
||||||
|
//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.on("1", _ => {
|
||||||
|
main();
|
||||||
|
})
|
||||||
|
|
||||||
|
instance.close = function(done) {
|
||||||
|
if (clientReady) {
|
||||||
|
client.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function getDbBackupFileCounter(type) {
|
||||||
|
var files = fs.readdirSync(__dirname + "/../databases");
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
|
||||||
|
if (files[i] == "tbdatacloud.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/tbdatacloud.nosql";
|
||||||
|
|
||||||
|
var stats = fs.statSync(source);
|
||||||
|
var fileSizeInBytes = stats.size;
|
||||||
|
|
||||||
|
if (fileSizeInBytes > noSqlFileSizeLimit) {
|
||||||
|
|
||||||
|
let counter = 1;
|
||||||
|
counter = getDbBackupFileCounter("max");
|
||||||
|
|
||||||
|
let destination = __dirname + "/../databases/" + counter + "." + "tbdatacloud.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 = 'tbdatacloud';
|
||||||
|
else dataBase = counter + "." + 'tbdatacloud';
|
||||||
|
|
||||||
|
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 (clientReady) {
|
||||||
|
|
||||||
|
let item = records[i];
|
||||||
|
let id = item.id;
|
||||||
|
|
||||||
|
if (id !== undefined) {
|
||||||
|
//console.log("------------processDataFromDatabase - remove", id, dataBase, i);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
let message = JSON.parse(JSON.stringify(item));
|
||||||
|
delete message.id;
|
||||||
|
client.publish(`${o.topic}_forward`, JSON.stringify(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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.on('options', main);
|
||||||
|
|
||||||
|
};
|
||||||
5094
flow/cmd_manager.js
5094
flow/cmd_manager.js
File diff suppressed because it is too large
Load diff
60
flow/count.js
Normal file
60
flow/count.js
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
exports.id = 'count';
|
||||||
|
exports.title = 'Count';
|
||||||
|
exports.version = '1.0.1';
|
||||||
|
exports.author = 'John Graves';
|
||||||
|
exports.color = '#656D78';
|
||||||
|
exports.icon = 'plus-square';
|
||||||
|
exports.input = 2;
|
||||||
|
exports.output = 1;
|
||||||
|
exports.options = { increment: 1, initialvalue: 1 };
|
||||||
|
exports.readme = `# Counter
|
||||||
|
|
||||||
|
Counter Number of times called.`;
|
||||||
|
|
||||||
|
exports.html = `<div class="padding">
|
||||||
|
<div data-jc="textbox" data-jc-path="initialvalue" data-jc-config="placeholder:1;increment:true;type:number;align:center">@(Initial Value)</div>
|
||||||
|
<div data-jc="textbox" data-jc-path="increment" data-jc-config="placeholder:1;increment:true;type:number;align:center">@(Increment)</div>
|
||||||
|
<p><a href="https://youtu.be/NuUbTm1oRE0" target="_blank">Example Video</a></p>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
exports.readme = `# Count
|
||||||
|
|
||||||
|
This component counts the number of messages received.
|
||||||
|
|
||||||
|
__Response:__
|
||||||
|
|
||||||
|
Integer value based on the initial value and increment settings.
|
||||||
|
|
||||||
|
__Arguments:__
|
||||||
|
- Initial Value: What number should be output on the receipt of the first message.
|
||||||
|
- Increment: What should the increment be for each following message received.`;
|
||||||
|
|
||||||
|
exports.install = function(instance) {
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
var initialCall = true;
|
||||||
|
|
||||||
|
instance.on('data', function(flowdata) {
|
||||||
|
var index = flowdata.index;
|
||||||
|
if (index) {
|
||||||
|
instance.debug('Reset Count.');
|
||||||
|
count = instance.options.initialvalue;
|
||||||
|
initialCall = true;
|
||||||
|
} else {
|
||||||
|
// If this is the first time, set the value to 'initial value'
|
||||||
|
if(initialCall) {
|
||||||
|
initialCall = false;
|
||||||
|
count = instance.options.initialvalue;
|
||||||
|
} else
|
||||||
|
count = count+instance.options.increment;
|
||||||
|
instance.status('Count:' + count);
|
||||||
|
instance.send2(count);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
instance.on('options', function() {
|
||||||
|
count = instance.options.initialvalue;
|
||||||
|
initialCall = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
112
flow/db_init.js
Normal file
112
flow/db_init.js
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
exports.id = 'db_init';
|
||||||
|
exports.title = 'DB Initialization';
|
||||||
|
exports.group = 'Worksys';
|
||||||
|
exports.color = '#888600';
|
||||||
|
exports.version = '1.0.2';
|
||||||
|
exports.icon = 'sign-out';
|
||||||
|
exports.output = 2;
|
||||||
|
|
||||||
|
exports.readme = `
|
||||||
|
# DB initialization
|
||||||
|
`;
|
||||||
|
|
||||||
|
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
||||||
|
const { initNotification } = require('./helper/notification_reporter');
|
||||||
|
const errorHandler = require('./helper/ErrorToServiceHandler');
|
||||||
|
const total_energy = require('../databases/total_energy');
|
||||||
|
|
||||||
|
const SEND_TO = {
|
||||||
|
db_init: 0,
|
||||||
|
infoSender: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.install = async function(instance) {
|
||||||
|
const dbNodes = TABLE("nodes");
|
||||||
|
const dbRelays = TABLE("relays");
|
||||||
|
const dbSettings = TABLE("settings");
|
||||||
|
const dbPins = TABLE("pins");
|
||||||
|
const dbNotifications = TABLE("notifications");
|
||||||
|
|
||||||
|
FLOW.GLOBALS = {};
|
||||||
|
const dbs = FLOW.GLOBALS;
|
||||||
|
|
||||||
|
const responseSettings = await promisifyBuilder(dbSettings.find());
|
||||||
|
const responseNodes = await promisifyBuilder(dbNodes.find());
|
||||||
|
const responsePins = await promisifyBuilder(dbPins.find());
|
||||||
|
const responseRelays = await promisifyBuilder(dbRelays.find());
|
||||||
|
const response = await promisifyBuilder(dbNotifications.find());
|
||||||
|
|
||||||
|
dbs.pinsData = makeMapFromDbResult(responsePins, "pin");
|
||||||
|
dbs.relaysData = makeMapFromDbResult(responseRelays, "line");
|
||||||
|
dbs.nodesData = makeMapFromDbResult(responseNodes, "node");
|
||||||
|
dbs.notificationsData = makeMapFromDbResult(response, "key");
|
||||||
|
|
||||||
|
//+|354|nodesdata.....+|482|nodesdata....
|
||||||
|
//for some reason, if last line in nodes.table is not empty, flow wrote more nodes data in one row,
|
||||||
|
//so we have to add empty line at the bottom of nodes table to avoid this.
|
||||||
|
//now, remove empty lines from nodesData database:
|
||||||
|
if (dbs.nodesData.hasOwnProperty("0")) delete dbs.nodesData["0"];
|
||||||
|
Object.keys(dbs.nodesData).forEach(node => dbs.nodesData[node].readout = {})
|
||||||
|
|
||||||
|
let rvo_number = responseSettings[0]["rvo_name"].match(/\D+(\d{1,2})_/)[1];
|
||||||
|
|
||||||
|
dbs.settings = {
|
||||||
|
edge_fw_version: "2025-10-08", //rok-mesiac-den
|
||||||
|
language: responseSettings[0]["lang"],
|
||||||
|
rvo_name: responseSettings[0]["rvo_name"],
|
||||||
|
project_id: responseSettings[0]["project_id"],
|
||||||
|
rvoTbName: dbs.relaysData[0]["tbname"],
|
||||||
|
temperature_address: responseSettings[0]["temperature_address"],
|
||||||
|
controller_type: responseSettings[0]["controller_type"],
|
||||||
|
serial_port: responseSettings[0]["serial_port"],
|
||||||
|
node_status_nok_time: responseSettings[0]["node_status_nok_time"] * 60 * 60 * 1000,// hour * minutes *
|
||||||
|
latitude: responseSettings[0]["latitude"],
|
||||||
|
longitude: responseSettings[0]["longitude"],
|
||||||
|
no_voltage: new Set(),//modbus_citysys - elektromer
|
||||||
|
backup_on_failure: responseSettings[0]["backup_on_failure"],
|
||||||
|
restore_from_backup: responseSettings[0]["restore_from_backup"],
|
||||||
|
restore_backup_wait: responseSettings[0]["restore_backup_wait"],
|
||||||
|
mqtt_host: responseSettings[0]["mqtt_host"],
|
||||||
|
mqtt_clientid: responseSettings[0]["mqtt_clientid"],
|
||||||
|
mqtt_username: responseSettings[0]["mqtt_username"],
|
||||||
|
mqtt_port: responseSettings[0]["mqtt_port"],
|
||||||
|
phases: responseSettings[0]["phases"],
|
||||||
|
cloud_topic: responseSettings[0]["cloud_topic"],
|
||||||
|
has_main_switch: responseSettings[0]["has_main_switch"],
|
||||||
|
daily_report: responseSettings[0]["daily_report"],
|
||||||
|
rvo_number: rvo_number,
|
||||||
|
|
||||||
|
//dynamic values
|
||||||
|
masterNodeIsResponding: true, //cmd_manager
|
||||||
|
maintenance_mode: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dbs.settings.energy_to_switch_lamps = total_energy[rvo_number];
|
||||||
|
if (dbs.settings.energy_to_switch_lamps === undefined) console.log('=============== db_init.js: energy_to_switch_lamps is undefined');
|
||||||
|
|
||||||
|
FLOW.dbLoaded = true;
|
||||||
|
errorHandler.setProjectId(dbs.settings.project_id);
|
||||||
|
initNotification();
|
||||||
|
|
||||||
|
//APP START - send to data services
|
||||||
|
const toService = {
|
||||||
|
id: dbs.settings.project_id,
|
||||||
|
name: dbs.settings.rvo_name,
|
||||||
|
fw_version: dbs.settings.edge_fw_version,
|
||||||
|
startdate: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
||||||
|
js_error: "",
|
||||||
|
error_message: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.send(SEND_TO.infoSender, toService);
|
||||||
|
console.log("----------------> START - message send to service", toService);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("DB_INIT - data loaded");
|
||||||
|
instance.send(SEND_TO.db_init, "_")
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
1506
flow/designer.json
1506
flow/designer.json
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,157 +1,180 @@
|
||||||
class DataToTbHandler
|
class DataToTbHandler {
|
||||||
{
|
|
||||||
constructor(index) {
|
constructor(index) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
|
||||||
|
// time, after new value for the given key will be resend to tb (e.g. {status: "OK"})
|
||||||
|
this.timeToHoldTbValue = 30 * 60; //30 minutes
|
||||||
this.previousValues = {};
|
this.previousValues = {};
|
||||||
this.debug = false;
|
this.debug = false;
|
||||||
this.messageCounter = 0;
|
this.messageCounter = 0;
|
||||||
|
this.itIsNodeReadout = false;
|
||||||
this.sender = "";
|
this.sender = "";
|
||||||
|
|
||||||
|
// if attribute change difference is less than limit value, we do not send to tb.
|
||||||
|
this.attributeChangeLimit = {
|
||||||
|
temperature: 0.5,
|
||||||
|
Phase_1_voltage: 2,
|
||||||
|
Phase_2_voltage: 2,
|
||||||
|
Phase_3_voltage: 2,
|
||||||
|
Phase_1_current: 0.1,
|
||||||
|
Phase_2_current: 0.1,
|
||||||
|
Phase_3_current: 0.1,
|
||||||
|
Phase_1_power: 2,
|
||||||
|
Phase_2_power: 2,
|
||||||
|
Phase_3_power: 2,
|
||||||
|
total_power: 2,
|
||||||
|
total_energy: 1,
|
||||||
|
Phase_1_pow_factor: 0.1,
|
||||||
|
Phase_2_pow_factor: 0.1,
|
||||||
|
Phase_3_pow_factor: 0.1,
|
||||||
|
power_factor: 0.1,
|
||||||
|
lifetime: 2,
|
||||||
|
voltage: 2,
|
||||||
|
power: 2,
|
||||||
|
frequency: 3,
|
||||||
|
energy: 0.1,
|
||||||
|
current: 2,
|
||||||
|
inclination_x: 10,
|
||||||
|
inclination_y: 10,
|
||||||
|
inclination_z: 10
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dump()
|
dump() {
|
||||||
{
|
|
||||||
console.log("----------------------------");
|
console.log("----------------------------");
|
||||||
console.log("previousValues", this.previousValues);
|
console.log("previousValues", this.previousValues);
|
||||||
console.log("----------------------------");
|
console.log("----------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
setSender(sender)
|
setSender(sender) {
|
||||||
{
|
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmptyObject( obj ) {
|
isEmptyObject(obj) {
|
||||||
for ( var name in obj ) {
|
for (var _ in obj) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToTb(dataToTb, instance)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(!FLOW.OMS_brokerready)
|
sendToTb(data, instance) {
|
||||||
{
|
|
||||||
return dataToTb;
|
|
||||||
}
|
|
||||||
|
|
||||||
let keys = Object.keys(dataToTb);
|
//not to modify data object, we do deep copy:
|
||||||
|
let dataCopy = JSON.parse(JSON.stringify(data));
|
||||||
|
|
||||||
if(keys.length == 0)
|
let keys = Object.keys(dataCopy);
|
||||||
{
|
|
||||||
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
if (keys.length == 0) {
|
||||||
|
if (this.debug) console.log("sendToTb received empty object", dataCopy);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let tbname = keys[0];
|
let tbname = keys[0];
|
||||||
let ts;
|
let ts;
|
||||||
|
|
||||||
let arrayOfValues = dataToTb[tbname];
|
let arrayOfValues = dataCopy[tbname];
|
||||||
let arrayOfValuesToSend = [];
|
let arrayOfValuesToSend = [];
|
||||||
|
|
||||||
for(let i = 0; i < arrayOfValues.length; i++)
|
for (let i = 0; i < arrayOfValues.length; i++) {
|
||||||
{
|
|
||||||
ts = arrayOfValues[i].ts;
|
ts = arrayOfValues[i].ts;
|
||||||
|
|
||||||
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
|
||||||
|
|
||||||
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
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(!this.isEmptyObject(values))
|
|
||||||
{
|
|
||||||
arrayOfValuesToSend.push({ts: ts, values: values});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arrayOfValuesToSend.length == 0)
|
if (arrayOfValuesToSend.length == 0) {
|
||||||
{
|
|
||||||
//if(this.debug) console.log("data not sent - empty array");
|
//if(this.debug) console.log("data not sent - empty array");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
let dataToTb = {
|
|
||||||
[tbname]: [
|
|
||||||
{
|
|
||||||
"ts": Date.now(),
|
|
||||||
"values": values
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.messageCounter++;
|
this.messageCounter++;
|
||||||
|
|
||||||
let dataToTbModified = {
|
let dataToTbModified = {
|
||||||
[tbname]: arrayOfValuesToSend
|
[tbname]: arrayOfValuesToSend
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance);
|
//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);
|
//if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend);
|
||||||
|
|
||||||
instance.send(this.index, dataToTbModified);
|
instance.send(this.index, dataToTbModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDiffTimestamp(key)
|
|
||||||
{
|
|
||||||
let seconds = 60*60;//1h
|
|
||||||
//seconds = 1;//for testing
|
|
||||||
|
|
||||||
|
getDiffTimestamp(key) {
|
||||||
//TODO set different value for given key!!!
|
//TODO set different value for given key!!!
|
||||||
//if(key == "status") seconds = 2*60*60;//2h
|
//if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h
|
||||||
|
return this.timeToHoldTbValue * 1000;
|
||||||
let timestampDiffToRemoveKey = seconds*1000;
|
|
||||||
|
|
||||||
return timestampDiffToRemoveKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareValuesForTb(tbname, timestamp, values)
|
|
||||||
{
|
prepareValuesForTb(tbname, timestamp, values) {
|
||||||
|
|
||||||
let keys = Object.keys(values);
|
let keys = Object.keys(values);
|
||||||
if(!this.previousValues.hasOwnProperty(tbname))
|
|
||||||
{
|
if (keys.includes("lifetime")) this.itIsNodeReadout = true;
|
||||||
|
|
||||||
|
if (!this.previousValues.hasOwnProperty(tbname)) {
|
||||||
this.previousValues[tbname] = {};
|
this.previousValues[tbname] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
||||||
|
|
||||||
for(let i = 0; i < keys.length; i++)
|
for (let i = 0; i < keys.length; i++) {
|
||||||
{
|
|
||||||
let key = keys[i];
|
let key = keys[i];
|
||||||
let value = values[key];
|
let value = values[key];
|
||||||
|
|
||||||
if(!this.previousValues[tbname].hasOwnProperty(key))
|
if (!this.previousValues[tbname].hasOwnProperty(key)) {
|
||||||
{
|
this.previousValues[tbname][key] = { ts: timestamp, value: value };
|
||||||
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.previousValues[tbname][key].value === value)
|
// attributeData ==> {voltage: {ts:333333, value:5}}
|
||||||
{
|
let attributeData = this.previousValues[tbname][key];
|
||||||
let diff = timestamp - this.previousValues[tbname][key].ts;
|
let attributeToChange = false;
|
||||||
|
if (key in this.attributeChangeLimit) attributeToChange = true;
|
||||||
|
let limit = this.attributeChangeLimit[key];
|
||||||
|
let timestampDiffToRemoveKey;
|
||||||
|
|
||||||
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
//this will ensure "node statecode" will be sent just once an hour
|
||||||
if(diff > timestampDiffToRemoveKey)
|
if (this.itIsNodeReadout && key === "statecode") {
|
||||||
{
|
attributeData.value = value;
|
||||||
this.previousValues[tbname][key].ts = Date.now();
|
this.itIsNodeReadout = false;
|
||||||
|
timestampDiffToRemoveKey = 1 * 60 * 60 * 1000; // 1 hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === "twilight_sensor" && value > 100) {
|
||||||
|
attributeData.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if edge, master or node version do not change, send just once a day:
|
||||||
|
if (["edge_fw_version", "master_node_version", "fw_version"].includes(key)) {
|
||||||
|
timestampDiffToRemoveKey = 24 * 60 * 60 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributeData.value === value || attributeToChange && Math.abs(attributeData.value - value) < limit) {
|
||||||
|
|
||||||
|
let diff = timestamp - attributeData.ts;
|
||||||
|
if (!timestampDiffToRemoveKey) timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
||||||
|
|
||||||
|
if (diff > timestampDiffToRemoveKey) {
|
||||||
|
attributeData.ts = Date.now();
|
||||||
//if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter);
|
//if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
delete values[key];
|
delete values[key];
|
||||||
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
attributeData.value = value;
|
||||||
this.previousValues[tbname][key].value = value;
|
attributeData.ts = timestamp;
|
||||||
this.previousValues[tbname][key].ts = timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -160,4 +183,5 @@ class DataToTbHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = DataToTbHandler;
|
module.exports = DataToTbHandler;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,91 @@
|
||||||
const { MD5 } = require('./md5.js');
|
const { MD5 } = require('./md5.js');
|
||||||
const { networkInterfaces } = require('os');
|
const { networkInterfaces } = require('os');
|
||||||
|
|
||||||
class ErrorToServiceHandler
|
class ErrorToServiceHandler {
|
||||||
{
|
constructor() {
|
||||||
constructor() {
|
|
||||||
this.previousValues = {};
|
this.previousValues = {};
|
||||||
|
|
||||||
this.projects_id = undefined;
|
this.project_id = undefined;
|
||||||
|
|
||||||
const nets = networkInterfaces();
|
const nets = networkInterfaces();
|
||||||
this.ipAddresses = Object.create(null); // Or just '{}', an empty object
|
this.ipAddresses = {};
|
||||||
|
|
||||||
for (const name of Object.keys(nets)) {
|
for (const name of Object.keys(nets)) {
|
||||||
for (const net of nets[name]) {
|
for (const net of nets[name]) {
|
||||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||||
if (net.family === 'IPv4' && !net.internal) {
|
if (net.family === 'IPv4' && !net.internal) {
|
||||||
if (!this.ipAddresses[name]) {
|
if (!this.ipAddresses[name]) {
|
||||||
this.ipAddresses[name] = [];
|
this.ipAddresses[name] = [];
|
||||||
}
|
}
|
||||||
this.ipAddresses[name].push(net.address);
|
this.ipAddresses[name].push(net.address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(this.ipAddresses);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setProjectsId(projects_id)
|
setProjectId(project_id) {
|
||||||
{
|
this.project_id = project_id;
|
||||||
this.projects_id = projects_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processMessage(message, seconds, message_type)
|
processMessage(message, seconds) {
|
||||||
{
|
if (Array.isArray(message)) message = message.join(', ');
|
||||||
if(message_type == undefined) message_type = "error_message";
|
|
||||||
if(Array.isArray(message)) message = message.join(', ');
|
|
||||||
|
|
||||||
let key = MD5(message);
|
|
||||||
let timestamp = new Date().getTime();
|
|
||||||
|
|
||||||
//keep in memory - default value is 1h
|
|
||||||
if (seconds === undefined) seconds = 60*60;
|
|
||||||
|
|
||||||
if(!this.previousValues.hasOwnProperty(key))
|
|
||||||
{
|
|
||||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff = (timestamp - this.previousValues[key].ts);
|
|
||||||
if(diff < this.previousValues[key].duration*1000) return false;
|
|
||||||
|
|
||||||
this.previousValues[key].ts = timestamp;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessageToService(message, seconds, message_type)
|
|
||||||
{
|
|
||||||
|
|
||||||
let f = this.processMessage(message, seconds, message_type);
|
|
||||||
if(!f) return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
//-------------
|
|
||||||
if(message_type == undefined) message_type = "error_message";
|
|
||||||
if(Array.isArray(message)) message = message.join(', ');
|
|
||||||
|
|
||||||
let key = MD5(message);
|
let key = MD5(message);
|
||||||
let timestamp = new Date().getTime();
|
let ts = Date.now();
|
||||||
|
|
||||||
//keep in memory
|
//keep in memory - default value is 1h
|
||||||
if (seconds === undefined) seconds = 60*60;
|
if (seconds === undefined) seconds = 60 * 60;
|
||||||
|
|
||||||
if(!this.previousValues.hasOwnProperty(key))
|
if (!this.previousValues.hasOwnProperty(key)) {
|
||||||
{
|
this.previousValues[key] = { ts: ts, duration: seconds };
|
||||||
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let diff = (timestamp - this.previousValues[key].ts);
|
let diff = (ts - this.previousValues[key].ts);
|
||||||
if(diff < this.previousValues[key].duration*1000) return;
|
if (diff < this.previousValues[key].duration * 1000) return false;
|
||||||
|
|
||||||
this.previousValues[key].ts = timestamp;
|
this.previousValues[key].ts = ts;
|
||||||
*/
|
|
||||||
|
|
||||||
//-------------------------
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
//send to service
|
sendMessageToService(message, seconds, message_type) {
|
||||||
|
|
||||||
let dataToInfoSender = {id: this.projects_id};
|
// if error occures too early FLOW.GLOBALS.settings.project_id is still undefined
|
||||||
|
if (this.project_id === undefined) {
|
||||||
|
console.log("ErrorToServiceHandler.js: no project_id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = this.processMessage(message, seconds);
|
||||||
|
if (f === false) return;
|
||||||
|
|
||||||
|
if (message_type === undefined) message_type = "error_message";
|
||||||
|
|
||||||
|
let toService = {
|
||||||
|
id: this.project_id,
|
||||||
|
ipAddresses: this.ipAddresses
|
||||||
|
};
|
||||||
|
|
||||||
//js_error || error_message
|
//js_error || error_message
|
||||||
dataToInfoSender[message_type] = message;
|
toService[message_type] = message;
|
||||||
dataToInfoSender.ipAddresses = this.ipAddresses;
|
|
||||||
|
|
||||||
console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender);
|
console.log("ErrorToServiceHandler------------------------>send to service", toService);
|
||||||
|
|
||||||
//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) {
|
RESTBuilder.make(function(builder) {
|
||||||
builder.method('POST');
|
builder.method('POST');
|
||||||
builder.post(dataToInfoSender);
|
builder.post(toService);
|
||||||
builder.url('http://192.168.252.2:8004/sentmessage');
|
builder.url('http://192.168.252.2:8004/sentmessage');
|
||||||
|
|
||||||
builder.callback(function(err, response, output) {
|
builder.callback(function(err, response, output) {
|
||||||
console.log("process.on error send", err, response, output, dataToInfoSender);
|
console.log("process.on error send", err, response, output, toService);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ErrorToServiceHandler;
|
const errorHandler = new ErrorToServiceHandler();
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = errorHandler;
|
||||||
|
//module.exports = ErrorToServiceHandler;
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
|
|
||||||
//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
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
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,
|
|
||||||
}
|
|
||||||
30
flow/helper/logger.js
Normal file
30
flow/helper/logger.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
//https://github.com/log4js-node/log4js-node/blob/master/examples/example.js
|
||||||
|
//file: { type: 'file', filename: path.join(__dirname, 'log/file.log') }
|
||||||
|
|
||||||
|
var log4js = require("log4js");
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
log4js.configure({
|
||||||
|
appenders: {
|
||||||
|
errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, 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");
|
||||||
|
|
||||||
|
//USAGE
|
||||||
|
//logger.debug("text")
|
||||||
|
//monitor.info('info');
|
||||||
|
//errLogger.error("some error");
|
||||||
|
|
||||||
|
module.exports = { errLogger, logger, monitor };
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
|
//key is device, value = message {}
|
||||||
|
let sentValues = {};
|
||||||
|
let notificationsData = null;
|
||||||
|
let rvoName;
|
||||||
|
|
||||||
const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js');
|
//sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance);
|
||||||
const dbNotifications = TABLE("notifications");
|
|
||||||
|
|
||||||
//key is device, value = str
|
|
||||||
let sentValues= {};
|
|
||||||
let notificationsData = {};
|
|
||||||
|
|
||||||
let ERRWEIGHT = {
|
let ERRWEIGHT = {
|
||||||
EMERGENCY: "emergency", // System unusable
|
EMERGENCY: "emergency", // System unusable
|
||||||
|
|
@ -24,87 +23,70 @@ function getKey(map, val) {
|
||||||
//https://stackoverflow.com/questions/41117799/string-interpolation-on-variable
|
//https://stackoverflow.com/questions/41117799/string-interpolation-on-variable
|
||||||
var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);
|
var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);
|
||||||
|
|
||||||
async function initNotifications()
|
|
||||||
{
|
|
||||||
let response = await promisifyBuilder(dbNotifications.find());
|
|
||||||
notificationsData = makeMapFromDbResult(response, "key");
|
|
||||||
|
|
||||||
console.log("initNotifications done" );
|
function initNotification() {
|
||||||
|
notificationsData = FLOW.GLOBALS.notificationsData;
|
||||||
|
rvoName = FLOW.GLOBALS.settings.rvo_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) {
|
function sendNotification(func, device, key, params, extra, tb_output, instance, saveKey) {
|
||||||
|
|
||||||
// return;
|
|
||||||
|
|
||||||
let storeToSendValues = true;
|
let storeToSendValues = true;
|
||||||
if(saveKey == undefined) storeToSendValues = false;
|
if (saveKey == undefined) storeToSendValues = false;
|
||||||
|
|
||||||
let lang = FLOW.OMS_language;
|
|
||||||
if(lang != "en" || lang != "sk") lang = "en";
|
|
||||||
|
|
||||||
let tpl = key;
|
|
||||||
let weight = "";
|
let weight = "";
|
||||||
|
let message = {};
|
||||||
|
|
||||||
if(notificationsData[key])
|
let notification = notificationsData[key];
|
||||||
{
|
|
||||||
weight = notificationsData[key].weight;
|
|
||||||
weight = weight.toLowerCase();
|
|
||||||
|
|
||||||
tpl = notificationsData[key][lang];
|
if (notification) {
|
||||||
tpl = template(tpl, params);
|
weight = notification.weight.toLowerCase();
|
||||||
|
|
||||||
|
Object.keys(notification).forEach(item => {
|
||||||
|
if (["en", "sk", "de", "cz", "it", "es"].includes(item)) {
|
||||||
|
message[item] = rvoName + ": " + template(notification[item], params);
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
//console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
|
||||||
console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
|
console.error("sendNotification: Notifications: undefined key", key, func);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//detect invalid err weight
|
//detect invalid err weight
|
||||||
if(getKey(ERRWEIGHT, weight) == undefined)
|
if (getKey(ERRWEIGHT, weight) == undefined) {
|
||||||
{
|
|
||||||
console.error("sendNotification: Notifications: undefined weight", weight, key, func);
|
console.error("sendNotification: Notifications: undefined weight", weight, key, func);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sentValues.hasOwnProperty(saveKey))
|
if (sentValues.hasOwnProperty(saveKey)) {
|
||||||
{
|
if (JSON.stringify(sentValues[saveKey]) == JSON.stringify(message)) {
|
||||||
if(sentValues[saveKey] == tpl)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sentValues[saveKey] == undefined)
|
if (sentValues[saveKey] == undefined) {
|
||||||
{
|
if (storeToSendValues) {
|
||||||
if(storeToSendValues)
|
|
||||||
{
|
|
||||||
//do not send - flow is was started
|
//do not send - flow is was started
|
||||||
sentValues[saveKey] = tpl;
|
sentValues[saveKey] = message;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(saveKey == "rvo_door")
|
if (storeToSendValues) sentValues[saveKey] = message;
|
||||||
{
|
|
||||||
//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 = {
|
let content = {
|
||||||
"type": weight,
|
"type": weight,
|
||||||
"status": "new",
|
"status": "new",
|
||||||
"source": {
|
"source": {
|
||||||
"func":func,
|
"func": func,
|
||||||
"component":instance.id,
|
"component": instance.id,
|
||||||
"component_name":instance.name,
|
"component_name": instance.name,
|
||||||
"edge":device
|
"edge": device
|
||||||
},
|
},
|
||||||
"message":str,
|
"message": message,
|
||||||
"message_data": extra
|
"message_data": extra
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -113,7 +95,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance,
|
||||||
{
|
{
|
||||||
"ts": Date.now(),
|
"ts": Date.now(),
|
||||||
"values": {
|
"values": {
|
||||||
"_event":content
|
"_event": content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -124,6 +106,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance,
|
||||||
} else {
|
} else {
|
||||||
bufferError(msg);
|
bufferError(msg);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -132,6 +115,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance,
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sendNotification,
|
sendNotification,
|
||||||
initNotifications,
|
ERRWEIGHT,
|
||||||
ERRWEIGHT
|
initNotification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,94 +1,99 @@
|
||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
function openPort(port){
|
function openPort(port) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
var callbackError = function(err) {
|
var callbackError = function(err) {
|
||||||
port.removeListener('error', callbackError);
|
port.removeListener('error', callbackError);
|
||||||
port.removeListener('open', callbackError);
|
port.removeListener('open', callbackError);
|
||||||
|
|
||||||
reject(err.message);
|
reject(err.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
var callbackOpen = function(data) {
|
var callbackOpen = function(data) {
|
||||||
port.removeListener('error', callbackError);
|
port.removeListener('error', callbackError);
|
||||||
port.removeListener('open', callbackOpen);
|
port.removeListener('open', callbackOpen);
|
||||||
|
|
||||||
resolve("port open: ok");
|
resolve("port open: ok");
|
||||||
};
|
};
|
||||||
|
|
||||||
port.on('error', callbackError);
|
port.on('error', callbackError);
|
||||||
port.on('open', callbackOpen);
|
port.on('open', callbackOpen);
|
||||||
|
|
||||||
port.open();
|
port.open();
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function runSyncExec(command){
|
function runSyncExec(command) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
exec(command, (error, stdout, stderr) => {
|
|
||||||
if(error == null) resolve(stdout);
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeData(port, data, readbytes, timeout){
|
exec(command, (error, stdout, stderr) => {
|
||||||
return new Promise((resolve, reject) => {
|
if (error == null) resolve(stdout);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
//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!!!
|
async function writeData(port, data, readbytes, timeout) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
var callback = function(data) {
|
|
||||||
rsPortReceivedData.push(...data);
|
|
||||||
let l = rsPortReceivedData.length;
|
|
||||||
|
|
||||||
if(l >= readbytes)
|
// If first item in data array is 255, we just write broadcast command to rsPort
|
||||||
{
|
// We wait 3 seconds and resolve(["broadcast"])
|
||||||
port.removeListener('data', callback);
|
// It is important to resolve with array
|
||||||
|
if (data[0] == 255) {
|
||||||
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) {
|
port.write(Buffer.from(data), function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
port.removeListener('data', callback);
|
|
||||||
reject(err.message);
|
reject(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(readbytes == 0)
|
|
||||||
{
|
|
||||||
resolve(rsPortReceivedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
})
|
|
||||||
|
setTimeout(resolve, 3000, ["broadcast"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 = [];
|
||||||
|
|
||||||
|
port.on('data', callback);
|
||||||
|
|
||||||
|
port.write(Buffer.from(data), function(err) {
|
||||||
|
if (err) {
|
||||||
|
port.removeListener('data', callback);
|
||||||
|
reject(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
openPort,
|
openPort,
|
||||||
runSyncExec,
|
runSyncExec,
|
||||||
writeData
|
writeData
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,161 @@
|
||||||
function bytesToInt(bytes, numberOfBytes)
|
const fs = require('fs').promises;
|
||||||
{
|
|
||||||
let buffer = [];
|
function bytesToInt(bytes, numberOfBytes) {
|
||||||
if(Array.isArray(bytes))
|
let buffer = [];
|
||||||
{
|
if (Array.isArray(bytes)) {
|
||||||
buffer = bytes.slice(0);
|
buffer = bytes.slice(0);
|
||||||
if(numberOfBytes != undefined)
|
if (numberOfBytes != undefined) {
|
||||||
{
|
buffer = bytes.slice(bytes.length - numberOfBytes);
|
||||||
buffer = bytes.slice(bytes.length - numberOfBytes);
|
}
|
||||||
}
|
}
|
||||||
}
|
else buffer.push(bytes);
|
||||||
else buffer.push(bytes);
|
|
||||||
|
let result = 0;
|
||||||
//var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
|
for (let i = 0; i < buffer.length; i++) {
|
||||||
let l = (buffer.length - 1) * 8;
|
result = (result << 8) | buffer[i];
|
||||||
|
}
|
||||||
let decimal = 0;
|
|
||||||
for(let i = 0; i < buffer.length; i++)
|
return result >>> 0; //ensure it's an unsigned 32-bit number
|
||||||
{
|
}
|
||||||
var s = buffer[i] << l;
|
|
||||||
if(l < 8) s = buffer[i]
|
function resizeArray(arr, newSize, defaultValue) {
|
||||||
decimal = decimal + s;
|
while (newSize > arr.length)
|
||||||
|
arr.push(defaultValue);
|
||||||
l = l - 8;
|
arr.length = newSize;
|
||||||
|
}
|
||||||
}
|
|
||||||
|
longToByteArray = function(/*long*/long) {
|
||||||
return decimal;
|
// we want to represent the input as a 8-bytes array
|
||||||
}
|
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
function resizeArray(arr, newSize, defaultValue) {
|
for (var index = 0; index < byteArray.length; index++) {
|
||||||
while(newSize > arr.length)
|
var byte = long & 0xff;
|
||||||
arr.push(defaultValue);
|
byteArray[index] = byte;
|
||||||
arr.length = newSize;
|
long = (long - byte) / 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
longToByteArray = function(/*long*/long) {
|
return byteArray;
|
||||||
// we want to represent the input as a 8-bytes array
|
};
|
||||||
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
||||||
|
function addDays(date, days) {
|
||||||
for ( var index = 0; index < byteArray.length; index ++ ) {
|
var result = new Date(date);
|
||||||
var byte = long & 0xff;
|
result.setDate(result.getDate() + days);
|
||||||
byteArray [ index ] = byte;
|
return result;
|
||||||
long = (long - byte) / 256 ;
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
return byteArray;
|
sleep(2000).then(() => {
|
||||||
};
|
// Do something after the sleep!
|
||||||
|
|
||||||
function addDays(date, days) {
|
|
||||||
var result = new Date(date);
|
});
|
||||||
result.setDate(result.getDate() + days);
|
*/
|
||||||
return result;
|
|
||||||
}
|
function sleep(time) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, time));
|
||||||
/*
|
}
|
||||||
sleep(2000).then(() => {
|
|
||||||
// Do something after the sleep!
|
function isEmptyObject(obj) {
|
||||||
|
for (var name in obj) {
|
||||||
|
return false;
|
||||||
});
|
}
|
||||||
*/
|
return true;
|
||||||
|
}
|
||||||
function sleep (time) {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, time));
|
function emptyJsObject(jsObject) {
|
||||||
}
|
Object.keys(jsObject).forEach(key => delete jsObject[key]);
|
||||||
|
}
|
||||||
function isEmptyObject( obj ) {
|
|
||||||
for ( var name in obj ) {
|
function convertUTCDateToLocalDate(date) {
|
||||||
return false;
|
var newDate = new Date(date);
|
||||||
}
|
newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());
|
||||||
return true;
|
return newDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertUTCDateToLocalDate(date) {
|
function addZeroBefore(n) {
|
||||||
var newDate = new Date(date);
|
return (n < 10 ? '0' : '') + n;
|
||||||
newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());
|
}
|
||||||
return newDate;
|
|
||||||
}
|
|
||||||
|
/**
|
||||||
function addZeroBefore(n) {
|
* Asynchronously writes data to a file.
|
||||||
return (n < 10 ? '0' : '') + n;
|
*
|
||||||
}
|
* @param {string} filePath The path to the file.
|
||||||
|
* @param {string} data The data to write to the file.
|
||||||
var convertBase = function () {
|
* @param {boolean} [append=false] If true, appends the data to the file. If false, it overwrites the file.
|
||||||
|
*/
|
||||||
function convertBase(baseFrom, baseTo) {
|
async function writeToFile(filePath, data, append = false) {
|
||||||
return function (num) {
|
if (typeof data !== 'string') data = JSON.stringify(data, null, 2);
|
||||||
return parseInt(num, baseFrom).toString(baseTo);
|
try {
|
||||||
|
if (append) {
|
||||||
};
|
// Append the data to the end of the file. This is the simplest way to append.
|
||||||
}
|
await fs.appendFile(filePath, data, 'utf8');
|
||||||
|
console.log(`Successfully appended data to ${filePath} using fs.appendFile.`);
|
||||||
// binary to decimal
|
} else {
|
||||||
convertBase.bin2dec = convertBase(2, 10);
|
// Overwrite the file with the new data.
|
||||||
|
await fs.writeFile(filePath, data, 'utf8');
|
||||||
// binary to hexadecimal
|
console.log(`Successfully wrote (overwrote) data to ${filePath} using fs.writeFile.`);
|
||||||
convertBase.bin2hex = convertBase(2, 16);
|
}
|
||||||
|
} catch (error) {
|
||||||
// decimal to binary
|
console.error(`Error writing to file ${filePath}:`, error);
|
||||||
convertBase.dec2bin = convertBase(10, 2);
|
}
|
||||||
|
}
|
||||||
// decimal to hexadecimal
|
|
||||||
convertBase.dec2hex = convertBase(10, 16);
|
|
||||||
|
/**
|
||||||
// hexadecimal to binary
|
* Checks if an item is present in an array and adds it if it's not.
|
||||||
convertBase.hex2bin = convertBase(16, 2);
|
* * @param {Array} arr The array to check.
|
||||||
|
* @param {*} item The item to add.
|
||||||
// hexadecimal to decimal
|
* @returns {Array} The modified array.
|
||||||
convertBase.hex2dec = convertBase(16, 10);
|
*/
|
||||||
|
const addToArrayIfUnique = (arr, item) => {
|
||||||
return convertBase;
|
if (!arr.includes(item)) {
|
||||||
}();
|
arr.push(item);
|
||||||
|
}
|
||||||
module.exports = {
|
return arr;
|
||||||
bytesToInt,
|
};
|
||||||
longToByteArray,
|
|
||||||
addDays,
|
|
||||||
addZeroBefore,
|
var convertBase = function() {
|
||||||
resizeArray,
|
|
||||||
isEmptyObject,
|
function convertBase(baseFrom, baseTo) {
|
||||||
sleep,
|
return function(num) {
|
||||||
convertUTCDateToLocalDate
|
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,
|
||||||
|
emptyJsObject,
|
||||||
|
sleep,
|
||||||
|
convertUTCDateToLocalDate,
|
||||||
|
writeToFile,
|
||||||
|
addToArrayIfUnique
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,9 @@ exports.title = 'Info sender';
|
||||||
exports.version = '1.0.0';
|
exports.version = '1.0.0';
|
||||||
exports.group = 'Worksys';
|
exports.group = 'Worksys';
|
||||||
exports.color = '#2134B0';
|
exports.color = '#2134B0';
|
||||||
exports.input = 1;
|
exports.input = 2;
|
||||||
exports.output = 1
|
exports.output = 1
|
||||||
exports.click = false;
|
|
||||||
exports.author = 'oms-is';
|
|
||||||
exports.icon = 'bolt';
|
exports.icon = 'bolt';
|
||||||
exports.options = { edge: "undefined" };
|
|
||||||
|
|
||||||
const { networkInterfaces } = require('os');
|
const { networkInterfaces } = require('os');
|
||||||
|
|
||||||
|
|
@ -22,13 +19,12 @@ exports.html = `<div class="padding">
|
||||||
|
|
||||||
exports.readme = `# send all data to projects.worksys.io, required to monitor status of controller(unipi)`;
|
exports.readme = `# send all data to projects.worksys.io, required to monitor status of controller(unipi)`;
|
||||||
|
|
||||||
const fs = require('fs');
|
exports.install = function(instance) {
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
exports.install = async function(instance) {
|
let id;
|
||||||
|
|
||||||
let allValues = {};
|
let allValues = {};
|
||||||
let sendAllValuesInterval;
|
let sendAllValuesInterval;
|
||||||
|
let configured = false;
|
||||||
|
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
console.log(exports.title, "INSTALLED", now.toLocaleString("sk-SK"));
|
console.log(exports.title, "INSTALLED", now.toLocaleString("sk-SK"));
|
||||||
|
|
@ -37,69 +33,49 @@ exports.install = async function(instance) {
|
||||||
let ipAddresses = Object.create(null); // Or just '{}', an empty object
|
let ipAddresses = Object.create(null); // Or just '{}', an empty object
|
||||||
|
|
||||||
for (const name of Object.keys(nets)) {
|
for (const name of Object.keys(nets)) {
|
||||||
for (const net of nets[name]) {
|
for (const net of nets[name]) {
|
||||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||||
if (net.family === 'IPv4' && !net.internal) {
|
if (net.family === 'IPv4' && !net.internal) {
|
||||||
if (!ipAddresses[name]) {
|
if (!ipAddresses[name]) {
|
||||||
ipAddresses[name] = [];
|
ipAddresses[name] = [];
|
||||||
}
|
}
|
||||||
ipAddresses[name].push(net.address);
|
ipAddresses[name].push(net.address);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function sendValues()
|
|
||||||
{
|
|
||||||
const id = FLOW.OMS_projects_id;
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendValues() {
|
||||||
|
if (!configured) return;
|
||||||
|
|
||||||
|
if (Object.keys(allValues).length > 0) {
|
||||||
|
let dataToSend = { ...allValues };
|
||||||
|
dataToSend.id = id;
|
||||||
|
dataToSend.ipAddresses = ipAddresses;
|
||||||
|
|
||||||
|
instance.send(0, dataToSend);
|
||||||
|
|
||||||
|
allValues = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
instance.on("close", () => {
|
instance.on("close", () => {
|
||||||
clearInterval(sendAllValuesInterval);
|
clearInterval(sendAllValuesInterval);
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.on("data", (flowdata) => {
|
instance.on("0", _ => {
|
||||||
|
id = FLOW.GLOBALS.settings.project_id;
|
||||||
allValues = { ...allValues, ...flowdata.data};
|
if (id) configured = true;
|
||||||
|
else console.log(exports.title, "InfoSender: Unable to send data, no id");
|
||||||
|
})
|
||||||
|
|
||||||
|
instance.on("1", flowdata => {
|
||||||
|
allValues = { ...allValues, ...flowdata.data };
|
||||||
//console.log("DATA RECEIVED", flowdata.data);
|
//console.log("DATA RECEIVED", flowdata.data);
|
||||||
|
|
||||||
//__force__
|
|
||||||
if(flowdata.data.hasOwnProperty("__force__"))
|
|
||||||
{
|
|
||||||
if(flowdata.data.__force__)
|
|
||||||
{
|
|
||||||
sendValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
sendAllValuesInterval = setInterval(() => {
|
sendAllValuesInterval = setInterval(() => {
|
||||||
sendValues();
|
sendValues();
|
||||||
}, 60000*3);
|
}, 60000 * 3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ exports.title = 'Modbus reader';
|
||||||
exports.version = '2.0.0';
|
exports.version = '2.0.0';
|
||||||
exports.group = 'Worksys';
|
exports.group = 'Worksys';
|
||||||
exports.color = '#2134B0';
|
exports.color = '#2134B0';
|
||||||
|
exports.input = 1;
|
||||||
exports.output = ["red", "white", "yellow"];
|
exports.output = ["red", "white", "yellow"];
|
||||||
exports.click = false;
|
exports.click = false;
|
||||||
exports.author = 'Rastislav Kovac';
|
exports.author = 'Rastislav Kovac';
|
||||||
|
|
@ -31,13 +32,17 @@ const SEND_TO = {
|
||||||
const numberOfNotResponding = {};
|
const numberOfNotResponding = {};
|
||||||
let tbName = null;
|
let tbName = null;
|
||||||
let mainSocket;
|
let mainSocket;
|
||||||
|
//number of phases inRVO
|
||||||
|
let phases;
|
||||||
|
//phases where voltage is 0 (set)
|
||||||
|
let noVoltage;
|
||||||
|
let energyToSwitchLamps;
|
||||||
|
|
||||||
exports.install = function(instance) {
|
exports.install = function(instance) {
|
||||||
|
|
||||||
class SocketWithClients {
|
class SocketWithClients {
|
||||||
|
|
||||||
constructor () {
|
constructor() {
|
||||||
this.stream = null;
|
this.stream = null;
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.clients = {};
|
this.clients = {};
|
||||||
|
|
@ -51,42 +56,51 @@ exports.install = function(instance) {
|
||||||
this.indexInDeviceConfig = 0; // first item in deviceConfig
|
this.indexInDeviceConfig = 0; // first item in deviceConfig
|
||||||
this.lengthOfActualDeviceStream = null;
|
this.lengthOfActualDeviceStream = null;
|
||||||
this.device = null;
|
this.device = null;
|
||||||
|
|
||||||
// lampSwitchNotification helper variables
|
// lampSwitchNotification helper variables
|
||||||
this.onNotificationSent = false;
|
this.onNotificationSent = false;
|
||||||
this.offNotificationSent = false;
|
this.offNotificationSent = false;
|
||||||
|
this.phases = this.buildPhases();
|
||||||
|
|
||||||
this.startSocket();
|
this.startSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildPhases = () => {
|
||||||
|
let a = [];
|
||||||
|
for (let i = 1; i <= phases; i++) {
|
||||||
|
a.push(`Phase_${i}_voltage`)
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
startSocket = () => {
|
startSocket = () => {
|
||||||
|
|
||||||
let obj = this;
|
let obj = this;
|
||||||
|
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.removeAllListeners();
|
||||||
|
this.socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
this.socket = new SerialPort("/dev/ttymxc0", {
|
this.socket = new SerialPort("/dev/ttymxc0", {
|
||||||
baudRate: 9600,
|
baudRate: 9600,
|
||||||
})
|
})
|
||||||
|
|
||||||
// we create a client for every deviceAddress ( = address) in list and push them into dictionary
|
// we create a client for every deviceAddress ( = address) in list and push them into dictionary
|
||||||
for( let i = 0; i < deviceConfig.length; i++)
|
for (let i = 0; i < deviceConfig.length; i++) {
|
||||||
{
|
|
||||||
this.clients[deviceConfig[i].deviceAddress] = new modbus.client.RTU(this.socket, deviceConfig[i].deviceAddress, 2000); // 2000 is timeout in register request, default is 5000, which is too long
|
this.clients[deviceConfig[i].deviceAddress] = new modbus.client.RTU(this.socket, deviceConfig[i].deviceAddress, 2000); // 2000 is timeout in register request, default is 5000, which is too long
|
||||||
}
|
}
|
||||||
|
|
||||||
this.socket.on('error', function(e) {
|
this.socket.on('error', function(e) {
|
||||||
console.log('socket connection error', e);
|
console.log('Modbus_reader: Socket connection error', e); //'ECONNREFUSED' or 'ECONNRESET' ??
|
||||||
if(e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') {
|
|
||||||
console.log(exports.title + ' Waiting 10 seconds before trying to connect again');
|
|
||||||
setTimeout(obj.startSocket, 10000);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('close', function() {
|
this.socket.on('close', function() {
|
||||||
console.log('Socket connection closed ' + exports.title + ' Waiting 10 seconds before trying to connect again');
|
console.log('Modbus_reader: Socket connection closed - Waiting 10 seconds before connecting again');
|
||||||
setTimeout(obj.startSocket, 10000);
|
setTimeout(obj.startSocket, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('open', function () {
|
this.socket.on('open', function() {
|
||||||
console.log("socket connected");
|
console.log("socket connected");
|
||||||
obj.getActualStreamAndDevice();
|
obj.getActualStreamAndDevice();
|
||||||
obj.timeoutInterval = timeoutInterval - DELAY_BETWEEN_DEVICES; // to make sure readout always runs in timeoutinterval we substract DELAY_BETWEEN_DEVICES
|
obj.timeoutInterval = timeoutInterval - DELAY_BETWEEN_DEVICES; // to make sure readout always runs in timeoutinterval we substract DELAY_BETWEEN_DEVICES
|
||||||
|
|
@ -99,11 +113,12 @@ exports.install = function(instance) {
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.errors = 0;
|
this.errors = 0;
|
||||||
this.stream = dev.stream;
|
this.stream = dev.stream;
|
||||||
this.lengthOfActualDeviceStream = dev.stream.length;
|
this.lengthOfActualDeviceStream = dev.stream.length;
|
||||||
this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number
|
this.deviceAddress = dev.deviceAddress; // 1 or 2 or any number
|
||||||
this.device = dev.device; //em340, twilight_sensor
|
this.device = dev.device; //em340, twilight_sensor
|
||||||
|
|
||||||
if(this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval);
|
//if we just start to loop devices from the beginning, or there is just 1 device in config, we wait whole timeoutInterval
|
||||||
|
if (this.indexInDeviceConfig == 0 || deviceConfig.length === 1) setTimeout(this.readRegisters, this.timeoutInterval);
|
||||||
else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES);
|
else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,21 +132,18 @@ exports.install = function(instance) {
|
||||||
let obj = this;
|
let obj = this;
|
||||||
|
|
||||||
this.clients[this.deviceAddress].readHoldingRegisters(register, size)
|
this.clients[this.deviceAddress].readHoldingRegisters(register, size)
|
||||||
.then( function (resp) {
|
.then(function(resp) {
|
||||||
|
|
||||||
resp = resp.response._body.valuesAsArray; //resp is array of length 1 or 2, f.e. [2360,0]
|
resp = resp.response._body.valuesAsArray; //resp is array of length 1 or 2, f.e. [2360,0]
|
||||||
// console.log(deviceAddress, register, tbAttribute, resp);
|
// console.log(deviceAddress, register, tbAttribute, resp);
|
||||||
|
|
||||||
//device is responding again after NOK status
|
//device is responding again after NOK status
|
||||||
if(numberOfNotResponding.hasOwnProperty(obj.device))
|
if (numberOfNotResponding.hasOwnProperty(obj.device)) {
|
||||||
{
|
|
||||||
let message = "";
|
let message = "";
|
||||||
if(obj.device == "em340")
|
if (obj.device == "em340") {
|
||||||
{
|
|
||||||
message = "electrometer_ok";
|
message = "electrometer_ok";
|
||||||
}
|
}
|
||||||
else if(obj.device == "twilight_sensor")
|
else if (obj.device == "twilight_sensor") {
|
||||||
{
|
|
||||||
message = "twilight_sensor_ok";
|
message = "twilight_sensor_ok";
|
||||||
}
|
}
|
||||||
message && sendNotification("modbus_reader: readRegisters", tbName, message, {}, "", SEND_TO.tb, instance);
|
message && sendNotification("modbus_reader: readRegisters", tbName, message, {}, "", SEND_TO.tb, instance);
|
||||||
|
|
@ -144,25 +156,21 @@ exports.install = function(instance) {
|
||||||
obj.index++;
|
obj.index++;
|
||||||
obj.readAnotherRegister();
|
obj.readAnotherRegister();
|
||||||
|
|
||||||
}).catch (function () {
|
}).catch(function() {
|
||||||
|
|
||||||
|
//console.log("errors pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute);
|
||||||
|
|
||||||
console.log("errors pri citani modbus registra", register, obj.indexInDeviceConfig, tbName, tbAttribute);
|
|
||||||
|
|
||||||
obj.errors++;
|
obj.errors++;
|
||||||
if(obj.errors == obj.lengthOfActualDeviceStream)
|
if (obj.errors == obj.lengthOfActualDeviceStream) {
|
||||||
{
|
instance.send(SEND_TO.dido_controller, { status: "NOK-" + obj.device }); // NOK-em340, NOK-em111, NOK-twilight_sensor, NOK-thermometer
|
||||||
instance.send(SEND_TO.dido_controller, {status: "NOK-" + obj.device}); // NOK-em340, NOK-em111, NOK-twilight_sensor, NOK-thermometer
|
|
||||||
|
|
||||||
//todo - neposlalo notification, ked sme vypojili twilight a neposle to do tb, ale do dido ??
|
//todo - neposlalo notification, ked sme vypojili twilight a neposle to do tb, ale do dido ??
|
||||||
if(!numberOfNotResponding.hasOwnProperty(obj.device))
|
if (!numberOfNotResponding.hasOwnProperty(obj.device)) {
|
||||||
{
|
|
||||||
let message = "";
|
let message = "";
|
||||||
if(obj.device == "twilight_sensor")
|
if (obj.device == "twilight_sensor") {
|
||||||
{
|
|
||||||
message = "twilight_sensor_nok";
|
message = "twilight_sensor_nok";
|
||||||
}
|
}
|
||||||
else if(obj.device == "em340")
|
else if (obj.device == "em340") {
|
||||||
{
|
|
||||||
message = "electrometer_nok";
|
message = "electrometer_nok";
|
||||||
}
|
}
|
||||||
message && sendNotification("modbus_reader: readingTimeouted", tbName, message, {}, "", SEND_TO.tb, instance);
|
message && sendNotification("modbus_reader: readingTimeouted", tbName, message, {}, "", SEND_TO.tb, instance);
|
||||||
|
|
@ -170,17 +178,16 @@ exports.install = function(instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.errors = 0;
|
obj.errors = 0;
|
||||||
numberOfNotResponding[obj.device] += 1;
|
numberOfNotResponding[obj.device] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(require('util').inspect(arguments, {
|
// console.error(require('util').inspect(arguments, {
|
||||||
depth: null
|
// depth: null
|
||||||
}))
|
// }))
|
||||||
|
|
||||||
// if reading out of device's last register returns error, we send accumulated allValues to dido_controller (if allValues are not an empty object)
|
// if reading out of device's last register returns error, we send accumulated allValues to dido_controller (if allValues are not an empty object)
|
||||||
if(obj.index + 1 >= obj.lengthOfActualDeviceStream)
|
if (obj.index + 1 >= obj.lengthOfActualDeviceStream) {
|
||||||
{
|
if (!isObjectEmpty(obj.allValues)) instance.send(SEND_TO.dido_controller, { values: obj.allValues });
|
||||||
if(!isObjectEmpty(obj.allValues)) instance.send(SEND_TO.dido_controller, {values: obj.allValues});
|
|
||||||
obj.allValues = {};
|
obj.allValues = {};
|
||||||
}
|
}
|
||||||
obj.index++;
|
obj.index++;
|
||||||
|
|
@ -190,7 +197,7 @@ exports.install = function(instance) {
|
||||||
};
|
};
|
||||||
|
|
||||||
readAnotherRegister = () => {
|
readAnotherRegister = () => {
|
||||||
if(this.index < this.lengthOfActualDeviceStream) setTimeout(this.readRegisters, 0);
|
if (this.index < this.lengthOfActualDeviceStream) setTimeout(this.readRegisters, 0);
|
||||||
else this.setNewStream();
|
else this.setNewStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,18 +206,16 @@ exports.install = function(instance) {
|
||||||
for (let i = 0; i < this.lengthOfActualDeviceStream; i++) {
|
for (let i = 0; i < this.lengthOfActualDeviceStream; i++) {
|
||||||
|
|
||||||
let a = this.stream[i];
|
let a = this.stream[i];
|
||||||
if (a.register === register)
|
if (a.register === register) {
|
||||||
{
|
|
||||||
let tbAttribute = a.tbAttribute;
|
let tbAttribute = a.tbAttribute;
|
||||||
let multiplier = a.multiplier;
|
let multiplier = a.multiplier;
|
||||||
|
|
||||||
let value = this.calculateValue(response, multiplier);
|
let value = this.calculateValue(response, multiplier);
|
||||||
// console.log(register, tbName, tbAttribute, response, a.multiplier, value);
|
// console.log(register, tbName, tbAttribute, response, a.multiplier, value);
|
||||||
|
|
||||||
// if(tbName == undefined) return;
|
// if(tbName == undefined) return;
|
||||||
|
|
||||||
if(this.index + 1 < this.lengthOfActualDeviceStream)
|
if (this.index + 1 < this.lengthOfActualDeviceStream) {
|
||||||
{
|
|
||||||
this.allValues[tbAttribute] = value;
|
this.allValues[tbAttribute] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -223,54 +228,45 @@ exports.install = function(instance) {
|
||||||
this.checkNullVoltage(values);
|
this.checkNullVoltage(values);
|
||||||
this.lampSwitchNotification(values);
|
this.lampSwitchNotification(values);
|
||||||
|
|
||||||
instance.send(SEND_TO.dido_controller, {values: values});
|
instance.send(SEND_TO.dido_controller, { values: values });
|
||||||
|
|
||||||
this.allValues = {};
|
this.allValues = {};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setNewStream = () =>
|
setNewStream = () => {
|
||||||
{
|
if (this.lengthOfActualDeviceStream == this.index) {
|
||||||
if(this.lengthOfActualDeviceStream == this.index)
|
if (this.indexInDeviceConfig + 1 == deviceConfig.length) {
|
||||||
{
|
|
||||||
if(this.indexInDeviceConfig + 1 == deviceConfig.length)
|
|
||||||
{
|
|
||||||
this.indexInDeviceConfig = 0;
|
this.indexInDeviceConfig = 0;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
this.indexInDeviceConfig += 1;
|
this.indexInDeviceConfig += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getActualStreamAndDevice();
|
this.getActualStreamAndDevice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateValue = (response, multiplier) =>
|
calculateValue = (response, multiplier) => {
|
||||||
{
|
|
||||||
let value = 0;
|
let value = 0;
|
||||||
|
|
||||||
let l = response.length;
|
let l = response.length;
|
||||||
if (l === 2)
|
if (l === 2) {
|
||||||
{
|
value = (response[1] * (2 ** 16) + response[0]);
|
||||||
value = (response[1]*(2**16) + response[0]);
|
|
||||||
|
|
||||||
if(value >= (2**31)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x80000000), ak vieš robiť logický súčin
|
if (value >= (2 ** 31)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x80000000), ak vieš robiť logický súčin
|
||||||
{
|
{
|
||||||
value = value - "0xFFFFFFFF" + 1;
|
value = value - "0xFFFFFFFF" + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (l === 1)
|
else if (l === 1) {
|
||||||
{
|
|
||||||
value = response[0];
|
value = response[0];
|
||||||
|
|
||||||
if(value >= (2**15)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x8000), ak vieš robiť logický súčin
|
if (value >= (2 ** 15)) // ak je MSB bit nastavený, eventuálne sa dá použiť aj (value & 0x8000), ak vieš robiť logický súčin
|
||||||
{
|
{
|
||||||
value = value - "0xFFFF" + 1;
|
value = value - "0xFFFF" + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,70 +275,72 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
checkNullVoltage = (values) => {
|
checkNullVoltage = (values) => {
|
||||||
|
|
||||||
if(!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return;
|
if (!(values.hasOwnProperty("Phase_1_voltage") || values.hasOwnProperty("Phase_2_voltage") || values.hasOwnProperty("Phase_3_voltage"))) return;
|
||||||
|
|
||||||
Object.keys(values).map(singleValue => {
|
Object.keys(values).map(singleValue => {
|
||||||
if (["Phase_1_voltage", "Phase_2_voltage", "Phase_3_voltage"].includes(singleValue))
|
if (this.phases.includes(singleValue)) {
|
||||||
{
|
|
||||||
let l = singleValue.split("_");
|
let l = singleValue.split("_");
|
||||||
let phase = parseInt(l[1]);
|
let phase = parseInt(l[1]);
|
||||||
|
|
||||||
if(FLOW.OMS_no_voltage == undefined) FLOW.OMS_no_voltage = new Set();
|
|
||||||
// console.log(values[singleValue], tbName);
|
// console.log(values[singleValue], tbName);
|
||||||
|
|
||||||
if(values[singleValue] == 0)
|
if (values[singleValue] == 0) {
|
||||||
{
|
noVoltage.add(phase);
|
||||||
FLOW.OMS_no_voltage.add(phase);
|
sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", { phase: phase }, "", SEND_TO.tb, instance, "voltage" + phase);
|
||||||
sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase );
|
|
||||||
// console.log('no voltage')
|
// console.log('no voltage')
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
noVoltage.delete(phase);
|
||||||
FLOW.OMS_no_voltage.delete(phase);
|
|
||||||
// console.log('voltage detected')
|
// console.log('voltage detected')
|
||||||
sendNotification("modbus_reader: checkNullVoltage", tbName, "voltage_on_phase_restored", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase);
|
sendNotification("modbus_reader: checkNullVoltage", tbName, "voltage_on_phase_restored", { phase: phase }, "", SEND_TO.tb, instance, "voltage" + phase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* function sends notification to slack and to tb, if EM total_power value changes more than 500. This should show, that RVO lamps has been switched on or off
|
* function sends notification to slack and to tb, if EM total_power value changes more than numberOfNodes*15. This should show, that RVO lamps has been switched on or off
|
||||||
*/
|
*/
|
||||||
lampSwitchNotification = (values) => {
|
lampSwitchNotification = (values) => {
|
||||||
|
|
||||||
if(!values.hasOwnProperty("total_power")) return;
|
if (!values.hasOwnProperty("total_power")) return;
|
||||||
|
|
||||||
const actualTotalPower = values.total_power;
|
const actualTotalPower = values.total_power;
|
||||||
if(actualTotalPower > 600 && this.onNotificationSent == false)
|
|
||||||
{
|
if (actualTotalPower > energyToSwitchLamps && this.onNotificationSent == false) {
|
||||||
sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance);
|
sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_on", {}, "", SEND_TO.tb, instance);
|
||||||
this.onNotificationSent = true;
|
this.onNotificationSent = true;
|
||||||
this.offNotificationSent = false;
|
this.offNotificationSent = false;
|
||||||
}
|
}
|
||||||
else if(actualTotalPower <= 600 && this.offNotificationSent == false)
|
else if (actualTotalPower <= energyToSwitchLamps && this.offNotificationSent == false) {
|
||||||
{
|
|
||||||
sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance);
|
sendNotification("modbus_reader: lampSwitchNotification", tbName, "lamps_have_turned_off", {}, "", SEND_TO.tb, instance);
|
||||||
this.onNotificationSent = false;
|
this.onNotificationSent = false;
|
||||||
this.offNotificationSent = true;
|
this.offNotificationSent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const isObjectEmpty = (objectName) => {
|
|
||||||
return Object.keys(objectName).length === 0 && objectName.constructor === Object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
const isObjectEmpty = (objectName) => {
|
||||||
|
return Object.keys(objectName).length === 0 && objectName.constructor === Object;
|
||||||
mainSocket = new SocketWithClients();
|
}
|
||||||
tbName = FLOW.OMS_rvo_tbname;
|
|
||||||
|
function main() {
|
||||||
|
|
||||||
|
phases = FLOW.GLOBALS.settings.phases;
|
||||||
|
tbName = FLOW.GLOBALS.settings.rvoTbName;
|
||||||
|
noVoltage = FLOW.GLOBALS.settings.no_voltage;
|
||||||
|
energyToSwitchLamps = FLOW.GLOBALS.settings.energy_to_switch_lamps / 2.5; //half value is enought to show if lamps are turned on or off
|
||||||
|
if (deviceConfig.length) mainSocket = new SocketWithClients();
|
||||||
|
else console.log("Modbus_reader: no modbus device in configuration");
|
||||||
|
|
||||||
// this notification is to show, that flow (unipi) has been restarted
|
// this notification is to show, that flow (unipi) has been restarted
|
||||||
sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance);
|
sendNotification("modbus_reader", tbName, "flow_restart", {}, "", SEND_TO.slack, instance);
|
||||||
|
}
|
||||||
|
|
||||||
}, 25000);
|
instance.on("0", function(_) {
|
||||||
|
main();
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
70
flow/nodesdb_changecheck.js
Normal file
70
flow/nodesdb_changecheck.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
exports.id = 'nodesdb_change_check';
|
||||||
|
exports.title = 'Nodes DB change check';
|
||||||
|
exports.group = 'Worksys';
|
||||||
|
exports.color = '#888600';
|
||||||
|
exports.version = '1.0.2';
|
||||||
|
exports.icon = 'sign-out';
|
||||||
|
exports.input = 1;
|
||||||
|
exports.output = 1;
|
||||||
|
exports.readme = `Check, if nodes.table db changed compared to original database`;
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { sendNotification } = require('./helper/notification_reporter');
|
||||||
|
const nodesOriginalFile = path.join(__dirname, '../databases/nodes_original/', 'nodes_original.table');
|
||||||
|
|
||||||
|
|
||||||
|
exports.install = function(instance) {
|
||||||
|
|
||||||
|
function compareArrays(array1, array2) {
|
||||||
|
let message = "";
|
||||||
|
let areEqual = true;
|
||||||
|
let zmenene = []
|
||||||
|
|
||||||
|
if (array1.length !== array2.length) {
|
||||||
|
message += "Nezhoda v pocte nodov. "
|
||||||
|
}
|
||||||
|
|
||||||
|
const set1 = new Set(array1.map(obj => JSON.stringify(obj)));
|
||||||
|
const set2 = new Set(array2.map(obj => JSON.stringify(obj)));
|
||||||
|
|
||||||
|
for (const objStr of set1) {
|
||||||
|
|
||||||
|
if (!set2.has(objStr)) {
|
||||||
|
zmenene.push(objStr)
|
||||||
|
areEqual = false;
|
||||||
|
} else {
|
||||||
|
set2.delete(objStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!areEqual) {
|
||||||
|
message += `Aktualne nody: ${zmenene.toString()}. Zmenene proti originalu: ${Array.from(set2).join(' ')}`;
|
||||||
|
sendNotification("Nodesdb_changecheck", FLOW.GLOBALS.settings.rvoTbName, "nodes_db_changed", "", message, 0, instance);
|
||||||
|
}
|
||||||
|
else console.log("Arrays are equal.");
|
||||||
|
|
||||||
|
console.log(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
instance.on("data", _ => {
|
||||||
|
|
||||||
|
let nodesData = FLOW.GLOBALS.nodesData;
|
||||||
|
|
||||||
|
// we check if nodes.table has changed compared to nodes_original.table (we have array of nodes e.g. [{node:255, tbname: "agruhuwhgursuhgo34hgsdiguhrr"}]
|
||||||
|
const nodes_actual = Object.keys(nodesData).map(node => ({ [node]: nodesData[node].tbname }))
|
||||||
|
let nodes_original = fs.readFileSync(nodesOriginalFile, { encoding: 'utf8', flag: 'r' });
|
||||||
|
|
||||||
|
try {
|
||||||
|
nodes_original = JSON.parse(nodes_original);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => compareArrays(nodes_actual, nodes_original), 10000);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
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é
|
|
||||||
|
243
flow/show_dbdata.js
Normal file
243
flow/show_dbdata.js
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
exports.id = 'showdb';
|
||||||
|
exports.title = 'Show db data';
|
||||||
|
exports.group = 'Worksys';
|
||||||
|
exports.color = '#888600';
|
||||||
|
exports.version = '1.0.2';
|
||||||
|
exports.icon = 'sign-out';
|
||||||
|
exports.input = 8;
|
||||||
|
exports.output = 1;
|
||||||
|
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
exports.install = function(instance) {
|
||||||
|
|
||||||
|
instance.on("0", _ => {
|
||||||
|
instance.send(0, FLOW.GLOBALS.settings);
|
||||||
|
})
|
||||||
|
instance.on("1", _ => {
|
||||||
|
instance.send(0, FLOW.GLOBALS.relaysData);
|
||||||
|
})
|
||||||
|
instance.on("2", _ => {
|
||||||
|
instance.send(0, FLOW.GLOBALS.nodesData);
|
||||||
|
})
|
||||||
|
instance.on("3", _ => {
|
||||||
|
instance.send(0, FLOW.GLOBALS.pinsData);
|
||||||
|
})
|
||||||
|
instance.on("4", _ => {
|
||||||
|
instance.send(0, { rpcSwitchOffLine, rpcSetNodeDimming, rpcLineProfile, rpcNodeProfile, sunCalcExample, dataFromTerminalBroadcast })
|
||||||
|
})
|
||||||
|
instance.on("5", _ => {
|
||||||
|
exec("sudo tail -n 25 monitor.txt", (err, stdout, stderr) => {
|
||||||
|
if (err || stderr) instance.send(0, { err, stderr });
|
||||||
|
else instance.send(0, stdout);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
instance.on("6", _ => {
|
||||||
|
exec("sudo tail -n 25 err.txt", (err, stdout, stderr) => {
|
||||||
|
if (err || stderr) instance.send(0, { err, stderr });
|
||||||
|
else instance.send(0, stdout);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
instance.on("7", _ => {
|
||||||
|
instance.send(0, FLOW.deviceStatus);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const rpcSwitchOffLine =
|
||||||
|
{
|
||||||
|
"topic": "v1/gateway/rpc",
|
||||||
|
"content": {
|
||||||
|
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
|
||||||
|
"data": {
|
||||||
|
"id": 8,
|
||||||
|
"method": "set_command",
|
||||||
|
"params": {
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"entity_type": "edb_line",
|
||||||
|
"tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"command": "switch",
|
||||||
|
"payload": {
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpcSetNodeDimming =
|
||||||
|
{
|
||||||
|
"topic": "v1/gateway/rpc",
|
||||||
|
"content": {
|
||||||
|
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
|
||||||
|
"data": {
|
||||||
|
"id": 10,
|
||||||
|
"method": "set_command",
|
||||||
|
"params": {
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"entity_type": "street_luminaire",
|
||||||
|
"tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"command": "dimming",
|
||||||
|
"payload": {
|
||||||
|
"value": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpcLineProfile =
|
||||||
|
{
|
||||||
|
"topic": "v1/gateway/rpc",
|
||||||
|
"content": {
|
||||||
|
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
|
||||||
|
"data": {
|
||||||
|
"id": 9,
|
||||||
|
"method": "set_profile",
|
||||||
|
"params": {
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"entity_type": "edb_line",
|
||||||
|
"tb_name": "MgnK93rkoAazbqdQ4yB2Q0yZ1YXGx6pmwBeVEP2O"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payload": {
|
||||||
|
"intervals": [
|
||||||
|
{
|
||||||
|
"value": 0,
|
||||||
|
"end_time": "20:00",
|
||||||
|
"start_time": "13:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 1,
|
||||||
|
"end_time": "05:30",
|
||||||
|
"start_time": "20:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const rpcNodeProfile =
|
||||||
|
{
|
||||||
|
"topic": "v1/gateway/rpc",
|
||||||
|
"content": {
|
||||||
|
"device": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV",
|
||||||
|
"data": {
|
||||||
|
"id": 11,
|
||||||
|
"method": "set_profile",
|
||||||
|
"params": {
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"entity_type": "street_luminaire",
|
||||||
|
"tb_name": "jbN4q7JPZmexgdnz2yKbWdDYAWwO0Q3BMX6ERLoV"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payload": {
|
||||||
|
"intervals": [
|
||||||
|
{
|
||||||
|
"cct": 3000,
|
||||||
|
"value": 0,
|
||||||
|
"end_time": "17:50",
|
||||||
|
"start_time": "13:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cct": 3000,
|
||||||
|
"value": 100,
|
||||||
|
"end_time": "21:30",
|
||||||
|
"start_time": "17:50"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cct": 3000,
|
||||||
|
"value": 0,
|
||||||
|
"end_time": "13:00",
|
||||||
|
"start_time": "07:10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cct": 3000,
|
||||||
|
"value": 50,
|
||||||
|
"end_time": "00:00",
|
||||||
|
"start_time": "21:30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cct": 3000,
|
||||||
|
"value": 10,
|
||||||
|
"end_time": "04:30",
|
||||||
|
"start_time": "00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cct": 3000,
|
||||||
|
"value": 100,
|
||||||
|
"end_time": "07:10",
|
||||||
|
"start_time": "04: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": 30,
|
||||||
|
"dusk_astro_clock_offset": 20,
|
||||||
|
"dawn_lux_sensor_time_window": 30,
|
||||||
|
"dusk_lux_sensor_time_window": 30,
|
||||||
|
"dawn_astro_clock_time_window": 60,
|
||||||
|
"dusk_astro_clock_time_window": 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sunCalcExample = {
|
||||||
|
dusk_no_offset: '20:18',
|
||||||
|
dawn_no_offset: '05:19',
|
||||||
|
dusk: '20:18',
|
||||||
|
dusk_hours: 20,
|
||||||
|
dusk_minutes: 18,
|
||||||
|
dawn: '05:19',
|
||||||
|
dawn_hours: 5,
|
||||||
|
dawn_minutes: 19,
|
||||||
|
dusk_time: 1715278688962,
|
||||||
|
dawn_time: 1715224744357,
|
||||||
|
dusk_astro_clock_offset: 0,
|
||||||
|
dawn_astro_clock_offset: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const dataFromTerminalBroadcast = {
|
||||||
|
address: 4294967295,
|
||||||
|
byte1: 0,
|
||||||
|
byte2: 0,
|
||||||
|
byte3: 0,
|
||||||
|
byte4: 96,
|
||||||
|
name: "Time Schedule settings",
|
||||||
|
recipient: 2,
|
||||||
|
register: 8,
|
||||||
|
rw: 1
|
||||||
|
}
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
exports.id = 'slack_connector';
|
|
||||||
exports.title = 'Slack_Connector';
|
|
||||||
exports.version = '1.0.0';
|
|
||||||
exports.group = 'Worksys';
|
|
||||||
exports.color = '#888600';
|
|
||||||
exports.input = 1;
|
|
||||||
exports.output = 1;
|
|
||||||
exports.click = false;
|
|
||||||
exports.author = 'Jakub Klena';
|
|
||||||
exports.icon = 'sign-out';
|
|
||||||
exports.options = { slack_channel: "C071KN2Q8SK", api_key: "", bot_name: "Flow DEMO", bot_icon: ":totaljs:" };
|
|
||||||
// Slack channel - where to post the messages, can be name like "backend-alerts"
|
|
||||||
// Bot Name - Name of the "user" that will post these messages, it should be based on which server it is running on.
|
|
||||||
// Bot Icon - We can use any slack icon (even custom ones uploaded by us) as the "user" profile picture
|
|
||||||
|
|
||||||
exports.html = `<div class="padding">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="slack_channel" data-jc-config="placeholder:name or id;required:true" class="m">Slack Channel</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="api_key" data-jc-config="placeholder:api key;required:true" class="m">API Key:</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="bot_name" data-jc-config="placeholder:Flow DEMO;required:false" class="m">Bot Name</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="bot_icon" data-jc-config="placeholder:\:totaljs\:;required:true" class="m">Bot Icon</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
exports.readme = `Sends any string received on input to Slack Channel.`;
|
|
||||||
|
|
||||||
var log4js = require("log4js");
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
log4js.configure({
|
|
||||||
appenders: {
|
|
||||||
errLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, 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");
|
|
||||||
|
|
||||||
exports.install = function(instance) {
|
|
||||||
var can = false;
|
|
||||||
|
|
||||||
process.on('uncaughtException', function (err) {
|
|
||||||
errLogger.error('uncaughtException:', err.message);
|
|
||||||
errLogger.error(err.stack);
|
|
||||||
instance.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
instance.on('data', function(data) {
|
|
||||||
if (!can) return;
|
|
||||||
|
|
||||||
let str = String(data.data); // Ensuring data get converted to string
|
|
||||||
let message = {
|
|
||||||
'channel': instance.options.slack_channel,
|
|
||||||
'username': instance.options.bot_name,
|
|
||||||
'icon_emoji': instance.options.bot_icon,
|
|
||||||
'text': str
|
|
||||||
};
|
|
||||||
let headers = {
|
|
||||||
'Content-type': `application/json`,
|
|
||||||
'Authorization': `Bearer ${instance.options.api_key}`
|
|
||||||
};
|
|
||||||
|
|
||||||
if (F.is4) {
|
|
||||||
let opt = {
|
|
||||||
'method': 'post',
|
|
||||||
'url': 'https://slack.com/api/chat.postMessage',
|
|
||||||
'headers': headers,
|
|
||||||
'body': JSON.stringify(message),
|
|
||||||
'type': 'json',
|
|
||||||
'callback': function(err, response) {
|
|
||||||
if (response && !err) {
|
|
||||||
var msg = { data: response.body, status: response.status, headers: response.headers, host: response.host, cookies: response.cookies };
|
|
||||||
instance.send2(msg);
|
|
||||||
} else if (err) {
|
|
||||||
errLogger.error('Slack post failed - err:', err, '\n - response was:', response);
|
|
||||||
instance.error(err, response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
REQUEST(opt);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
U.request('https://slack.com/api/chat.postMessage', ['json', 'post'], JSON.stringify(message), 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) {
|
|
||||||
errLogger.error('Slack post failed - err:', err, '\n - response was:', response);
|
|
||||||
instance.error(err, response);
|
|
||||||
}
|
|
||||||
}, null, headers);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
instance.reconfigure = function() {
|
|
||||||
var options = instance.options;
|
|
||||||
can = options.slack_channel && options.bot_name && options.bot_icon && options.api_key ? true : false;
|
|
||||||
instance.status(can ? '' : 'Not configured', can ? undefined : 'red');
|
|
||||||
};
|
|
||||||
|
|
||||||
instance.on('options', instance.reconfigure);
|
|
||||||
instance.reconfigure();
|
|
||||||
}
|
|
||||||
|
|
@ -2,18 +2,15 @@ exports.id = 'slack_filter';
|
||||||
exports.title = 'Slack Filter';
|
exports.title = 'Slack Filter';
|
||||||
exports.group = 'Citysys';
|
exports.group = 'Citysys';
|
||||||
exports.color = '#30E193';
|
exports.color = '#30E193';
|
||||||
exports.input = 1;
|
exports.input = 2;
|
||||||
exports.output = 1;
|
exports.output = 1;
|
||||||
exports.author = 'Jakub Klena';
|
exports.author = 'Jakub Klena';
|
||||||
exports.icon = 'plug';
|
exports.icon = 'plug';
|
||||||
exports.version = '1.0.8';
|
exports.version = '1.0.8';
|
||||||
exports.options = { 'name':'', 'types': '["emergency", "critical", "error", "alert"]', 'message_includes':'["is responding again"]', 'tag_on_include':'[{"user_id":"U072JE5JUQG", "includes":["Electrometer", "Twilight sensor"]}]', 'slack_channel':'' };
|
exports.options = { 'name': '', 'types': '["emergency", "critical", "error", "alert"]', 'message_includes': '["is responding again"]', 'tag_on_include': '[{"user_id":"U072JE5JUQG", "includes":["Electrometer", "Twilight sensor"]}]', 'slack_channel': '' };
|
||||||
|
|
||||||
exports.html = `<div class="padding">
|
exports.html = `<div class="padding">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
|
||||||
<div data-jc="textbox" data-jc-path="name" data-jc-config="required:true">@(Name of this server)</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div data-jc="textbox" data-jc-path="slack_channel" data-jc-config="required:false">@(Slack channel to receive the alerts)</div>
|
<div data-jc="textbox" data-jc-path="slack_channel" data-jc-config="required:false">@(Slack channel to receive the alerts)</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -36,11 +33,11 @@ exports.install = function(instance) {
|
||||||
instance["savedSlackMessages"] = [];
|
instance["savedSlackMessages"] = [];
|
||||||
var timer = null;
|
var timer = null;
|
||||||
|
|
||||||
instance.on('data', function(response) {
|
instance.on('0', function(response) {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
let value = response.data;
|
let value = response.data;
|
||||||
if (typeof value !== 'object') return;
|
if (typeof value !== 'object') return;
|
||||||
|
|
||||||
let can = false
|
let can = false
|
||||||
var k = Object.keys(value);
|
var k = Object.keys(value);
|
||||||
var interested = JSON.parse(instance.options.types);
|
var interested = JSON.parse(instance.options.types);
|
||||||
|
|
@ -60,11 +57,11 @@ exports.install = function(instance) {
|
||||||
let icon = ':totaljs:';
|
let icon = ':totaljs:';
|
||||||
let type = value[k[0]][0]['values']['_event']['type'];
|
let type = value[k[0]][0]['values']['_event']['type'];
|
||||||
let source = value[k[0]][0]['values']['_event']['source']['func'];
|
let source = value[k[0]][0]['values']['_event']['source']['func'];
|
||||||
let message = value[k[0]][0]['values']['_event']['message'];
|
let message = value[k[0]][0]['values']['_event']['message']['en'];
|
||||||
let message_data = value[k[0]][0]['values']['_event']['message_data'];
|
let message_data = value[k[0]][0]['values']['_event']['message_data'];
|
||||||
let tag = '';
|
let tag = '';
|
||||||
|
|
||||||
switch(type){
|
switch (type) {
|
||||||
case 'debug':
|
case 'debug':
|
||||||
icon = ':beetle:';
|
icon = ':beetle:';
|
||||||
break;
|
break;
|
||||||
|
|
@ -92,15 +89,15 @@ exports.install = function(instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this message includes one of the strings we are watching for
|
// Check if this message includes one of the strings we are watching for
|
||||||
for (const msg of msg_incl){
|
for (const msg of msg_incl) {
|
||||||
if (message.includes(msg)){
|
if (message.includes(msg)) {
|
||||||
if (msg == 'is responding again') icon = ':large_green_circle:';
|
if (msg == 'is responding again') icon = ':large_green_circle:';
|
||||||
can = true;
|
can = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if message is one of the types we are watching for
|
// Check if message is one of the types we are watching for
|
||||||
if (interested.includes(type)){
|
if (interested.includes(type)) {
|
||||||
can = true;
|
can = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,10 +105,10 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
|
|
||||||
// Check for each person tags based on what the message includes
|
// Check for each person tags based on what the message includes
|
||||||
for (const person of tags){
|
for (const person of tags) {
|
||||||
for (const msg of person.includes){
|
for (const msg of person.includes) {
|
||||||
if (message.includes(msg)){
|
if (message.includes(msg)) {
|
||||||
tag += '<@'+person.user_id+'> ';
|
tag += '<@' + person.user_id + '> ';
|
||||||
break; // Break out from this person checks as they are already tagged now
|
break; // Break out from this person checks as they are already tagged now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,46 +116,46 @@ exports.install = function(instance) {
|
||||||
// Now that all people are tagged add new line symbol
|
// Now that all people are tagged add new line symbol
|
||||||
if (tag != '') tag += '\n';
|
if (tag != '') tag += '\n';
|
||||||
|
|
||||||
let send_data = tag+instance.options.name+' '+type.toUpperCase()+'\n*Source*: '+source+'\n*Message*: '+message;
|
let send_data = tag + instance.options.name + ' ' + type.toUpperCase() + '\n*Source*: ' + source + '\n*Message*: ' + message;
|
||||||
if (message_data) {
|
if (message_data) {
|
||||||
send_data += '\nData: '+message_data;
|
send_data += '\nData: ' + message_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ignore_msg = false
|
let ignore_msg = false
|
||||||
if (message.includes('Configuration of dimming profile to node no')){
|
if (message.includes('Configuration of dimming profile to node no')) {
|
||||||
for (let i = 0; i < FLOW["savedSlackMessages"].length; i++){
|
for (let i = 0; i < FLOW["savedSlackMessages"].length; i++) {
|
||||||
if (FLOW["savedSlackMessages"][i].message == message){
|
if (FLOW["savedSlackMessages"][i].message == message) {
|
||||||
ignore_msg = true;
|
ignore_msg = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ignore_msg){
|
if (!ignore_msg) {
|
||||||
FLOW["savedSlackMessages"].push({message, 'dateandtime': Date.now()});
|
FLOW["savedSlackMessages"].push({ message, 'dateandtime': Date.now() });
|
||||||
if (timer === null){
|
if (timer === null) {
|
||||||
timer = setTimeout(checkSavedMessages, 60*60000);
|
timer = setTimeout(checkSavedMessages, 60 * 60000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignore_msg){
|
if (!ignore_msg) {
|
||||||
instance.send2({'msg':send_data,'bot_name':instance.options.name+' '+type.toUpperCase(),'bot_icon':icon,'channel':instance.options.slack_channel});
|
instance.send2({ 'msg': send_data, 'bot_name': instance.options.name + ' ' + type.toUpperCase(), 'bot_icon': icon, 'channel': instance.options.slack_channel });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkSavedMessages(){
|
function checkSavedMessages() {
|
||||||
var d = Date.now();
|
var d = Date.now();
|
||||||
d = d - 86400000; // older then 24hr
|
d = d - 86400000; // older then 24hr
|
||||||
var a = [];
|
var a = [];
|
||||||
//Remove msgs older then 24hr
|
//Remove msgs older then 24hr
|
||||||
for (let i = 0; i < FLOW["savedSlackMessages"].length; i++){
|
for (let i = 0; i < FLOW["savedSlackMessages"].length; i++) {
|
||||||
if (FLOW["savedSlackMessages"][i].dateandtime > d){
|
if (FLOW["savedSlackMessages"][i].dateandtime > d) {
|
||||||
a.push(FLOW["savedSlackMessages"][i]);
|
a.push(FLOW["savedSlackMessages"][i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FLOW["savedSlackMessages"] = a;
|
FLOW["savedSlackMessages"] = a;
|
||||||
|
|
||||||
if (FLOW["savedSlackMessages"].length > 0) {
|
if (FLOW["savedSlackMessages"].length > 0) {
|
||||||
timer = setTimeout(checkSavedMessages, 60*60000);
|
timer = setTimeout(checkSavedMessages, 60 * 60000);
|
||||||
} else {
|
} else {
|
||||||
timer = null;
|
timer = null;
|
||||||
}
|
}
|
||||||
|
|
@ -166,15 +163,16 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
instance.reconfigure = function() {
|
instance.reconfigure = function() {
|
||||||
try {
|
try {
|
||||||
if (!FLOW["savedSlackMessages"]){
|
if (!FLOW["savedSlackMessages"]) {
|
||||||
FLOW["savedSlackMessages"] = [];
|
FLOW["savedSlackMessages"] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance.options.name = FLOW.GLOBALS.settings.rvo_name;
|
||||||
if (instance.options.name) {
|
if (instance.options.name) {
|
||||||
instance.status('Running');
|
instance.status('Running');
|
||||||
running = true;
|
running = true;
|
||||||
} else {
|
} else {
|
||||||
instance.status('Please enter name', 'red');
|
instance.status('Please run options again', 'red');
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -183,5 +181,8 @@ exports.install = function(instance) {
|
||||||
};
|
};
|
||||||
|
|
||||||
instance.on('options', instance.reconfigure);
|
instance.on('options', instance.reconfigure);
|
||||||
instance.reconfigure();
|
|
||||||
};
|
instance.on("1", _ => {
|
||||||
|
instance.reconfigure();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,220 +1,99 @@
|
||||||
exports.id = 'thermometer';
|
exports.id = 'thermometer';
|
||||||
exports.title = 'Thermometer';
|
exports.title = 'Thermometer';
|
||||||
exports.group = 'Worksys';
|
exports.group = 'Worksys';
|
||||||
exports.color = '#5CB36D';
|
exports.color = '#5CB36D';
|
||||||
exports.version = '1.0.3';
|
exports.input = 1;
|
||||||
exports.output = ["red", "white", "blue"];
|
exports.version = '1.0.3';
|
||||||
exports.author = 'Rastislav Kovac';
|
exports.output = ["red", "white", "blue"];
|
||||||
exports.icon = 'thermometer-three-quarters';
|
exports.icon = 'thermometer-three-quarters';
|
||||||
|
|
||||||
exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`;
|
exports.readme = `# Getting temperature values for RVO. In case of LM, you need device address. In case of unipi, evok sends values, in case thermometer is installed`;
|
||||||
|
|
||||||
const instanceSendTo = {
|
const { errLogger, logger, monitor } = require('./helper/logger');
|
||||||
debug: 0,
|
|
||||||
tb: 1,
|
const SEND_TO = {
|
||||||
dido_controller: 2
|
debug: 0,
|
||||||
}
|
tb: 1,
|
||||||
|
dido_controller: 2
|
||||||
//read temperature - frequency
|
}
|
||||||
let timeoutMin = 5;//minutes
|
|
||||||
|
//read temperature - frequency
|
||||||
var path = require('path');
|
let timeoutMin = 5;//minutes
|
||||||
var log4js = require("log4js");
|
let NUMBER_OF_FAILURES_TO_SEND_ERROR = 13;
|
||||||
|
|
||||||
log4js.configure({
|
exports.install = function(instance) {
|
||||||
appenders: {
|
|
||||||
errLogs: { type: 'file', filename: path.join(__dirname + "/../", 'err.txt') },
|
const { exec } = require('child_process');
|
||||||
monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') },
|
const { sendNotification } = require('./helper/notification_reporter');
|
||||||
console: { type: 'console' }
|
|
||||||
},
|
let startRead;
|
||||||
categories: {
|
let counter = 0;
|
||||||
errLogs: { appenders: ['console', 'errLogs'], level: 'error' },
|
let rvoTbName = "";
|
||||||
monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' },
|
let temperatureAddress = "";
|
||||||
//another: { appenders: ['console'], level: 'trace' },
|
|
||||||
default: { appenders: ['console'], level: 'trace' }
|
logger.debug(exports.title, "installed");
|
||||||
}
|
|
||||||
});
|
instance.on("close", function() {
|
||||||
|
clearInterval(startRead);
|
||||||
const errLogger = log4js.getLogger("errLogs");
|
})
|
||||||
const logger = log4js.getLogger();
|
|
||||||
const monitor = log4js.getLogger("monitorLogs");
|
|
||||||
|
const main = function() {
|
||||||
//logger.debug("text")
|
|
||||||
//monitor.info('info');
|
try {
|
||||||
//errLogger.error("some error");
|
|
||||||
|
if (temperatureAddress === "") throw "Thermometer: temperatureAddress is not defined";
|
||||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper');
|
|
||||||
const dbSettings = TABLE("settings");
|
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
|
||||||
let temperatureAddress = "";
|
|
||||||
|
if (!error) {
|
||||||
async function loadSettings()
|
parseData(stdout)
|
||||||
{
|
return;
|
||||||
//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"];
|
counter++;
|
||||||
}
|
if (counter == NUMBER_OF_FAILURES_TO_SEND_ERROR) {
|
||||||
|
sendNotification("Thermometer_main", rvoTbName, "thermometer_is_not_responding", {}, { "Error": error }, SEND_TO.tb, instance, "thermometer");
|
||||||
loadSettings();
|
monitor.info("Thermometer is not responding", error);
|
||||||
|
}
|
||||||
|
instance.send(SEND_TO.dido_controller, { status: "NOK-thermometer" });
|
||||||
exports.install = function(instance) {
|
});
|
||||||
|
|
||||||
const { exec } = require('child_process');
|
}
|
||||||
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
|
catch (err) {
|
||||||
|
errLogger.error(exports.title, err);
|
||||||
let startRead;
|
clearInterval(startRead);
|
||||||
let dataToTb;
|
}
|
||||||
let counter = 0;
|
}
|
||||||
|
|
||||||
let edgeName = "";
|
const parseData = function(data) {
|
||||||
|
|
||||||
|
data = parseFloat(data);
|
||||||
logger.debug(exports.title, "installed");
|
//logger.debug("Thermometer", data);
|
||||||
|
|
||||||
instance.on("close", function(){
|
if (isNaN(data)) {
|
||||||
clearInterval(startRead);
|
errLogger.error("Thermometer sends invalid data");
|
||||||
})
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const start = function() {
|
if (counter > NUMBER_OF_FAILURES_TO_SEND_ERROR) //1 hour
|
||||||
|
{
|
||||||
try {
|
instance.send(SEND_TO.debug, "Thermometer - temperature data are comming again");
|
||||||
|
sendNotification("Thermometer_parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer");
|
||||||
if(FLOW.OMS_controller_type === "unipi")
|
}
|
||||||
{
|
|
||||||
clearInterval(startRead);
|
const values = {
|
||||||
return;
|
"temperature": Number(data.toFixed(2)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined";
|
instance.send(SEND_TO.dido_controller, { values: values });
|
||||||
|
counter = 0;
|
||||||
logger.debug("FLOW.OMS_temperature_adress", FLOW.OMS_temperature_adress);
|
}
|
||||||
|
|
||||||
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
|
instance.on("data", _ => {
|
||||||
|
temperatureAddress = FLOW.GLOBALS.settings.temperature_address;
|
||||||
edgeName = FLOW.OMS_edgeName;
|
rvoTbName = FLOW.GLOBALS.settings.rvoTbName;
|
||||||
|
startRead = setInterval(main, timeoutMin * 1000 * 60);
|
||||||
if(edgeName !== "")
|
setTimeout(main, 20000);
|
||||||
{
|
})
|
||||||
if(error)
|
};
|
||||||
{
|
|
||||||
|
|
||||||
if(FLOW.OMS_brokerready == undefined)
|
|
||||||
{
|
|
||||||
logger.debug("gettemparature - FLOW.OMS_brokerready is undefined");
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
start();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(FLOW.OMS_brokerready)
|
|
||||||
{
|
|
||||||
//sendNotification("start", edgeName, ERRWEIGHT.WARNING, "Thermometer is not responding", {"Error": error}, instanceSendTo.tb, instance, "thermometer");
|
|
||||||
sendNotification("start", edgeName, "thermometer_is_not_responding", {}, {"Error": error}, instanceSendTo.tb, instance, "thermometer");
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = "NOK";
|
|
||||||
dataToTb = {
|
|
||||||
[edgeName]: [
|
|
||||||
{
|
|
||||||
"ts": Date.now(),
|
|
||||||
"values": {
|
|
||||||
"status": status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
|
|
||||||
|
|
||||||
// instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
|
|
||||||
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
|
|
||||||
}
|
|
||||||
else parseData(stdout);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
monitor.info("gettemperature: edgeName is not defined", FLOW.OMS_edgeName);
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
start();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//instance.send({"Temp":stdout,"stderr":stderr,"err":error});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
errLogger.error(exports.title, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseData = function(data) {
|
|
||||||
|
|
||||||
data = parseFloat(data);
|
|
||||||
|
|
||||||
logger.debug("gettemperature", data);
|
|
||||||
|
|
||||||
if(!isNaN(data)) {
|
|
||||||
|
|
||||||
if(counter > 290)
|
|
||||||
{
|
|
||||||
instance.send(instanceSendTo.debug, "[Get temperature component] - temperature data are comming again from RVO after more than 1 day break");
|
|
||||||
|
|
||||||
//sendNotification("parseData", edgeName, ERRWEIGHT.NOTICE, "Thermometer is working again", "", instanceSendTo.tb, instance, "thermometer");
|
|
||||||
if(FLOW.OMS_brokerready) sendNotification("parseData", edgeName, "thermometer_is_responding_again", {}, "", instanceSendTo.tb, instance, "thermometer");
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("gettemperature", data);
|
|
||||||
const values = {
|
|
||||||
"temperature": Number(data.toFixed(2)),
|
|
||||||
"status": "OK"
|
|
||||||
}
|
|
||||||
|
|
||||||
dataToTb = {
|
|
||||||
[edgeName]: [
|
|
||||||
{
|
|
||||||
"ts": Date.now(),
|
|
||||||
"values":values
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.send(instanceSendTo.tb, dataToTb);
|
|
||||||
instance.send(instanceSendTo.dido_controller, values);
|
|
||||||
|
|
||||||
counter = 0;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
counter++;
|
|
||||||
monitor.info("gettemperature err", counter, data);
|
|
||||||
|
|
||||||
//ked je problem 1 den
|
|
||||||
let day = 24 * 60 / timeoutMin;
|
|
||||||
if ( counter > day && counter < day + 2 ) {
|
|
||||||
//sendNotification("parseData", edgeName, ERRWEIGHT.WARNING, "Thermometer receives invalid data", "", instanceSendTo.tb, instance, "thermometer");
|
|
||||||
sendNotification("parseData", edgeName, "thermometer_sends_invalid_data", {}, "", instanceSendTo.tb, instance, "thermometer");
|
|
||||||
|
|
||||||
instance.send(instanceSendTo.debug, "[Get temperature component] - no temperature data from RVO for more than 1 day");
|
|
||||||
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
start();
|
|
||||||
}, 15000);
|
|
||||||
|
|
||||||
startRead = setInterval(start, timeoutMin * 1000 * 60);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -4,30 +4,27 @@ exports.group = 'MQTT';
|
||||||
exports.color = '#888600';
|
exports.color = '#888600';
|
||||||
exports.version = '1.0.2';
|
exports.version = '1.0.2';
|
||||||
exports.icon = 'sign-out';
|
exports.icon = 'sign-out';
|
||||||
exports.input = 1;
|
exports.input = 2;
|
||||||
exports.output = ["red", "white", "blue"];
|
exports.output = 3;
|
||||||
exports.author = 'Daniel Segeš';
|
|
||||||
exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" };
|
exports.options = { host: 'tb-stage.worksys.io', port: 1883, clientid: "", username: "" };
|
||||||
exports.npm = ['mqtt'];
|
|
||||||
|
|
||||||
|
|
||||||
exports.html = `<div class="padding">
|
exports.html = `<div class="padding">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<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 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>
|
||||||
<div class="col-md-6">
|
<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 data-jc="textbox" data-jc-path="port" data-jc-config="placeholder:1883;required:true" class="m">Port</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div data-jc="textbox" data-jc-path="clientid">@(Client id)</div>
|
<div data-jc="textbox" data-jc-path="clientid">@(Client id)</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div data-jc="textbox" data-jc-path="username" class="m">@(Username)</div>
|
<div data-jc="textbox" data-jc-path="username" class="m">@(Username)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -41,78 +38,46 @@ Added:
|
||||||
- rpc response
|
- rpc response
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const instanceSendTo = {
|
const { promisifyBuilder } = require('./helper/db_helper');
|
||||||
|
const { errLogger, monitor } = require('./helper/logger');
|
||||||
|
const fs = require('fs');
|
||||||
|
const mqtt = require('mqtt');
|
||||||
|
|
||||||
|
const SEND_TO = {
|
||||||
debug: 0,
|
debug: 0,
|
||||||
rpcCall: 1,
|
rpcCall: 1,
|
||||||
services: 2
|
services: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
|
||||||
|
|
||||||
//CONFIG
|
//CONFIG
|
||||||
let useLog4js = true;
|
|
||||||
let createTelemetryBackup = true;
|
let createTelemetryBackup = true;
|
||||||
let saveTelemetryOnError = true;//backup_on_failure overrides this value
|
let saveTelemetryOnError = true;//backup_on_failure overrides this value
|
||||||
//------------------------
|
//------------------------
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
let rollers;
|
let rollers;
|
||||||
if(createTelemetryBackup) rollers = require('streamroller');
|
if (createTelemetryBackup) rollers = require('streamroller');
|
||||||
|
|
||||||
const noSqlFileSizeLimit = 4194304;//use 5MB - 4194304
|
const noSqlFileSizeLimit = 4194304;//use 5MB - 4194304
|
||||||
let insertNoSqlCounter = 0;
|
let insertNoSqlCounter = 0;
|
||||||
let insertBackupNoSqlCounter = 0;
|
let insertBackupNoSqlCounter = 0;
|
||||||
let processingData = false;
|
let processingData = false;
|
||||||
|
|
||||||
let backup_on_failure = false;//== saveTelemetryOnError - create backup broker send failure
|
let backup_on_failure = false;//== saveTelemetryOnError - create backup client send failure
|
||||||
let restore_from_backup = 0; //how many rows process at once?
|
let restore_from_backup = 0; //how many rows process at once?
|
||||||
let restore_backup_wait = 0;//wait seconds
|
let restore_backup_wait = 0;//wait seconds
|
||||||
let lastRestoreTime = 0;
|
let lastRestoreTime = 0;
|
||||||
|
|
||||||
let errLogger;
|
// if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable
|
||||||
let logger;
|
let sendClientError = true;
|
||||||
let monitor;
|
|
||||||
|
|
||||||
if(useLog4js)
|
process.on('uncaughtException', function(err) {
|
||||||
{
|
|
||||||
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
|
errLogger.error('uncaughtException:', err.message)
|
||||||
//logger.debug("text");
|
errLogger.error(err.stack);
|
||||||
//monitor.info('info');
|
|
||||||
//errLogger.error("some error");
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on('uncaughtException', function (err) {
|
|
||||||
|
|
||||||
if(errLogger)
|
|
||||||
{
|
|
||||||
errLogger.error('uncaughtException:', err.message)
|
|
||||||
errLogger.error(err.stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
//send to service
|
//send to service
|
||||||
|
|
||||||
//process.exit(1);
|
//process.exit(1);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -122,40 +87,38 @@ const nosqlBackup = NOSQL('/backup/tbdata');
|
||||||
|
|
||||||
exports.install = function(instance) {
|
exports.install = function(instance) {
|
||||||
|
|
||||||
var broker;
|
var client;
|
||||||
var opts;
|
var opts;
|
||||||
var brokerready = false;
|
var clientReady = false;
|
||||||
|
|
||||||
instance.on('options', loadSettings);
|
|
||||||
|
|
||||||
mqtt = require('mqtt');
|
|
||||||
|
|
||||||
// wsmqtt status for notification purposes on projects.worksys.io database
|
// wsmqtt status for notification purposes on projects.worksys.io database
|
||||||
let wsmqttName = null;
|
let wsmqttName = null;
|
||||||
let sendWsStatusVar = null;
|
let sendWsStatusVar = null;
|
||||||
let wsmqtt_status = 'disconnected';
|
let wsmqtt_status = 'disconnected';
|
||||||
|
|
||||||
function getWsmqttName(host)
|
function getWsmqttName(host) {
|
||||||
{
|
if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo';
|
||||||
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-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';
|
||||||
else if(host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWsStatus()
|
function sendWsStatus() {
|
||||||
{
|
instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status });
|
||||||
instance.send(instanceSendTo.services, {[wsmqttName]: wsmqtt_status});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sendWsStatusVar = setInterval(sendWsStatus, 180000);
|
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
if (!FLOW.dbLoaded) return;
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
clearInterval(sendWsStatus);
|
||||||
|
sendWsStatusVar = setInterval(sendWsStatus, 180000);
|
||||||
|
}
|
||||||
|
|
||||||
//set opts according to db settings
|
//set opts according to db settings
|
||||||
async function loadSettings()
|
function loadSettings() {
|
||||||
{
|
|
||||||
|
|
||||||
if(instance.options.host !== "")
|
if (instance.options.host !== "") {
|
||||||
{
|
|
||||||
//override settings from database
|
//override settings from database
|
||||||
var o = instance.options;
|
var o = instance.options;
|
||||||
opts = {
|
opts = {
|
||||||
|
|
@ -171,24 +134,19 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
console.log("wsmqttpublich -> loadSettings from instance.options", instance.options);
|
console.log("wsmqttpublich -> loadSettings from instance.options", instance.options);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
|
const SETTINGS = FLOW.GLOBALS.settings;
|
||||||
const dbSettings = TABLE("settings");
|
backup_on_failure = SETTINGS.backup_on_failure;
|
||||||
let responseSettings = await promisifyBuilder(dbSettings.find());
|
|
||||||
|
|
||||||
backup_on_failure = responseSettings[0]["backup_on_failure"];
|
|
||||||
saveTelemetryOnError = 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"];
|
restore_from_backup = SETTINGS.restore_from_backup;
|
||||||
let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
|
restore_backup_wait = SETTINGS.restore_backup_wait;
|
||||||
let mqtt_username = responseSettings[0]["mqtt_username"];
|
|
||||||
let mqtt_port = responseSettings[0]["mqtt_port"];
|
|
||||||
|
|
||||||
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
|
let mqtt_host = SETTINGS.mqtt_host;
|
||||||
|
let mqtt_clientid = SETTINGS.mqtt_clientid;
|
||||||
|
let mqtt_username = SETTINGS.mqtt_username;
|
||||||
|
let mqtt_port = SETTINGS.mqtt_port;
|
||||||
|
|
||||||
opts = {
|
opts = {
|
||||||
host: mqtt_host,
|
host: mqtt_host,
|
||||||
|
|
@ -206,123 +164,106 @@ exports.install = function(instance) {
|
||||||
connectToTbServer();
|
connectToTbServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectToTbServer()
|
function connectToTbServer() {
|
||||||
{
|
|
||||||
var url = "mqtt://" + opts.host + ":" + opts.port;
|
var url = "mqtt://" + opts.host + ":" + opts.port;
|
||||||
console.log("MQTT URL: ", url);
|
console.log("MQTT URL: ", url);
|
||||||
|
|
||||||
broker = mqtt.connect(url, opts);
|
client = mqtt.connect(url, opts);
|
||||||
|
|
||||||
broker.on('connect', function() {
|
client.on('connect', function() {
|
||||||
instance.status("Connected", "green");
|
instance.status("Connected", "green");
|
||||||
monitor.info("MQTT broker connected");
|
//monitor.info("MQTT client connected");
|
||||||
|
|
||||||
brokerready = true;
|
sendClientError = true;
|
||||||
FLOW.OMS_brokerready = brokerready;
|
clientReady = true;
|
||||||
wsmqtt_status = 'connected';
|
wsmqtt_status = 'connected';
|
||||||
});
|
});
|
||||||
|
|
||||||
broker.on('reconnect', function() {
|
client.on('reconnect', function() {
|
||||||
instance.status("Reconnecting", "yellow");
|
instance.status("Reconnecting", "yellow");
|
||||||
brokerready = false;
|
clientReady = false;
|
||||||
|
|
||||||
FLOW.OMS_brokerready = brokerready;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
broker.on('message', function(topic, message) {
|
client.on('message', function(topic, message) {
|
||||||
// message is type of buffer
|
// message is type of buffer
|
||||||
message = message.toString();
|
message = message.toString();
|
||||||
if (message[0] === '{') {
|
if (message[0] === '{') {
|
||||||
TRY(function() {
|
TRY(function() {
|
||||||
|
|
||||||
message = JSON.parse(message);
|
message = JSON.parse(message);
|
||||||
if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) {
|
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});
|
client.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.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
}, () => instance.debug('MQTT: Error parsing data', message));
|
}, () => instance.debug('MQTT: Error parsing data', message));
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message });
|
instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message });
|
||||||
});
|
});
|
||||||
|
|
||||||
broker.on('close', function(err) {
|
client.on('close', function() {
|
||||||
brokerready = false;
|
clientReady = false;
|
||||||
FLOW.OMS_brokerready = brokerready;
|
|
||||||
wsmqtt_status = 'disconnected';
|
wsmqtt_status = 'disconnected';
|
||||||
|
|
||||||
if (err && err.toString().indexOf('Error')) {
|
instance.status("Disconnected", "red");
|
||||||
instance.status("Err: "+err.code, "red");
|
instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" });
|
||||||
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
});
|
||||||
} else {
|
|
||||||
instance.status("Disconnected", "red");
|
client.on('error', function(err) {
|
||||||
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
instance.status("Err: " + err.code, "red");
|
||||||
|
instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts });
|
||||||
|
if (sendClientError) {
|
||||||
|
monitor.info('MQTT client error', err);
|
||||||
|
sendClientError = false;
|
||||||
}
|
}
|
||||||
|
clientReady = false;
|
||||||
broker.reconnect();
|
|
||||||
});
|
|
||||||
|
|
||||||
broker.on('error', function(err) {
|
|
||||||
instance.status("Err: "+ err.code, "red");
|
|
||||||
instance.send(instanceSendTo.debug, {"message":"Broker ERROR signal received !", "error":err, "opt":opts });
|
|
||||||
monitor.info('MQTT broker error', err);
|
|
||||||
|
|
||||||
brokerready = false;
|
|
||||||
FLOW.OMS_brokerready = brokerready;
|
|
||||||
wsmqtt_status = 'disconnected';
|
wsmqtt_status = 'disconnected';
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.on('data', function(data) {
|
|
||||||
|
|
||||||
if (brokerready)
|
instance.on("0", _ => {
|
||||||
{
|
main();
|
||||||
//do we have some data in backup file?
|
})
|
||||||
//if any, process data from database
|
|
||||||
if(saveTelemetryOnError)
|
|
||||||
{
|
instance.on('1', function(data) {
|
||||||
|
|
||||||
|
if (clientReady) {
|
||||||
|
//do we have some data in backup file? if any, process data from database
|
||||||
|
if (saveTelemetryOnError) {
|
||||||
//read telemetry data and send back to server
|
//read telemetry data and send back to server
|
||||||
if(!processingData) processDataFromDatabase();
|
if (!processingData) processDataFromDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (brokerready)
|
|
||||||
{
|
|
||||||
let stringifiedJson = JSON.stringify(data.data);
|
let stringifiedJson = JSON.stringify(data.data);
|
||||||
broker.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1});
|
client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 });
|
||||||
|
|
||||||
//backup telemetry
|
//backup telemetry
|
||||||
if(createTelemetryBackup)
|
if (createTelemetryBackup) {
|
||||||
{
|
|
||||||
data.data.id = UID();
|
data.data.id = UID();
|
||||||
nosqlBackup.insert(data.data);
|
nosqlBackup.insert(data.data);
|
||||||
|
|
||||||
insertBackupNoSqlCounter++;
|
insertBackupNoSqlCounter++;
|
||||||
if(insertBackupNoSqlCounter > 150)
|
if (insertBackupNoSqlCounter > 150) {
|
||||||
{
|
let options = { compress: true };
|
||||||
let options = {compress: true};
|
|
||||||
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
||||||
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
||||||
stream.write("");
|
stream.write("");
|
||||||
stream.end();
|
stream.end();
|
||||||
|
|
||||||
insertBackupNoSqlCounter = 0;
|
insertBackupNoSqlCounter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
//logger.debug("Client unavailable. Data not sent !", JSON.stringify(data.data));
|
||||||
|
instance.send(SEND_TO.debug, { "message": "Client unavailable. Data not sent !", "data": data.data });
|
||||||
|
|
||||||
if(logger) logger.debug("Broker unavailable. Data not sent !", data.data);
|
if (saveTelemetryOnError) {
|
||||||
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
|
//create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql
|
||||||
makeBackupFromDbFile();
|
makeBackupFromDbFile();
|
||||||
|
|
||||||
|
|
@ -330,78 +271,68 @@ exports.install = function(instance) {
|
||||||
data.data.id = UID();
|
data.data.id = UID();
|
||||||
nosql.insert(data.data);
|
nosql.insert(data.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
instance.close = function(done) {
|
instance.close = function(done) {
|
||||||
if (brokerready){
|
if (clientReady) {
|
||||||
broker.end();
|
client.end();
|
||||||
clearInterval(sendWsStatusVar);
|
clearInterval(sendWsStatusVar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function getDbBackupFileCounter(type)
|
|
||||||
{
|
function getDbBackupFileCounter(type) {
|
||||||
var files = fs.readdirSync(__dirname + "/../databases");
|
var files = fs.readdirSync(__dirname + "/../databases");
|
||||||
|
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
for(var i = 0; i < files.length; i++)
|
for (var i = 0; i < files.length; i++) {
|
||||||
{
|
|
||||||
|
|
||||||
if(files[i] == "tbdata.nosql") continue;
|
if (files[i] == "tbdata.nosql") continue;
|
||||||
|
|
||||||
if(files[i].endsWith(".nosql"))
|
if (files[i].endsWith(".nosql")) {
|
||||||
{
|
|
||||||
|
|
||||||
let pos = files[i].indexOf(".");
|
let pos = files[i].indexOf(".");
|
||||||
if(pos > -1)
|
if (pos > -1) {
|
||||||
{
|
|
||||||
|
|
||||||
let fileCounter = counter;
|
let fileCounter = counter;
|
||||||
let firstDigit = files[i].slice(0, pos);
|
let firstDigit = files[i].slice(0, pos);
|
||||||
|
|
||||||
fileCounter = parseInt(firstDigit);
|
fileCounter = parseInt(firstDigit);
|
||||||
if (isNaN(fileCounter)) fileCounter = 0;
|
if (isNaN(fileCounter)) fileCounter = 0;
|
||||||
//console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type);
|
//console.log("getDbBackupFileCounter digit:", files[i], firstDigit, fileCounter, isNaN(fileCounter), type);
|
||||||
|
|
||||||
if(type == "max")
|
if (type == "max") {
|
||||||
{
|
if (fileCounter > counter) {
|
||||||
if(fileCounter > counter)
|
|
||||||
{
|
|
||||||
counter = fileCounter;
|
counter = fileCounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(type == "min")
|
else if (type == "min") {
|
||||||
{
|
if (counter == 0) counter = fileCounter;
|
||||||
if(counter == 0) counter = fileCounter;
|
|
||||||
|
|
||||||
if(fileCounter < counter)
|
if (fileCounter < counter) {
|
||||||
{
|
|
||||||
counter = fileCounter;
|
counter = fileCounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(type == "max") counter++;
|
if (type == "max") counter++;
|
||||||
|
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeBackupFromDbFile = async () => {
|
const makeBackupFromDbFile = async () => {
|
||||||
|
|
||||||
if(!saveTelemetryOnError) return;
|
if (!saveTelemetryOnError) return;
|
||||||
|
|
||||||
//to avoid large file: tbdata.nosql
|
//to avoid large file: tbdata.nosql
|
||||||
|
|
||||||
//init value is 0!
|
//init value is 0!
|
||||||
if(insertNoSqlCounter > 0)
|
if (insertNoSqlCounter > 0) {
|
||||||
{
|
|
||||||
--insertNoSqlCounter;
|
--insertNoSqlCounter;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -413,8 +344,7 @@ exports.install = function(instance) {
|
||||||
var stats = fs.statSync(source);
|
var stats = fs.statSync(source);
|
||||||
var fileSizeInBytes = stats.size;
|
var fileSizeInBytes = stats.size;
|
||||||
|
|
||||||
if(fileSizeInBytes > noSqlFileSizeLimit)
|
if (fileSizeInBytes > noSqlFileSizeLimit) {
|
||||||
{
|
|
||||||
|
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
counter = getDbBackupFileCounter("max");
|
counter = getDbBackupFileCounter("max");
|
||||||
|
|
@ -428,24 +358,20 @@ exports.install = function(instance) {
|
||||||
//clear tbdata.nosql
|
//clear tbdata.nosql
|
||||||
fs.writeFileSync(source, "");
|
fs.writeFileSync(source, "");
|
||||||
fs.truncateSync(source, 0);
|
fs.truncateSync(source, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const processDataFromDatabase = async () => {
|
const processDataFromDatabase = async () => {
|
||||||
|
|
||||||
if(restore_from_backup <= 0)
|
if (restore_from_backup <= 0) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//calculate diff
|
//calculate diff
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let currentTime = now.getTime();
|
let currentTime = now.getTime();
|
||||||
let diff = currentTime - lastRestoreTime;
|
let diff = currentTime - lastRestoreTime;
|
||||||
|
|
||||||
if( (diff / 1000) < restore_backup_wait)
|
if ((diff / 1000) < restore_backup_wait) {
|
||||||
{
|
|
||||||
//console.log("*********restore_backup_wait", diff, restore_backup_wait);
|
//console.log("*********restore_backup_wait", diff, restore_backup_wait);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -459,33 +385,30 @@ exports.install = function(instance) {
|
||||||
let dataBase = 'tbdata';
|
let dataBase = 'tbdata';
|
||||||
|
|
||||||
var nosql;
|
var nosql;
|
||||||
if(counter == 0) dataBase = 'tbdata';
|
if (counter == 0) dataBase = 'tbdata';
|
||||||
else dataBase = counter + "." + 'tbdata';
|
else dataBase = counter + "." + 'tbdata';
|
||||||
|
|
||||||
nosql = NOSQL(dataBase);
|
nosql = NOSQL(dataBase);
|
||||||
|
|
||||||
//select all data - use limit restore_from_backup
|
//select all data - use limit restore_from_backup
|
||||||
let records = await promisifyBuilder(nosql.find().take(restore_from_backup));
|
let records = await promisifyBuilder(nosql.find().take(restore_from_backup));
|
||||||
|
|
||||||
for(let i = 0; i < records.length; i++)
|
for (let i = 0; i < records.length; i++) {
|
||||||
{
|
if (clientReady) {
|
||||||
if (brokerready) {
|
|
||||||
|
|
||||||
let item = records[i];
|
let item = records[i];
|
||||||
let id = item.id;
|
let id = item.id;
|
||||||
|
|
||||||
if(id !== undefined)
|
if (id !== undefined) {
|
||||||
{
|
|
||||||
//console.log("------------processDataFromDatabase - remove", id, dataBase, i);
|
//console.log("------------processDataFromDatabase - remove", id, dataBase, i);
|
||||||
|
|
||||||
try{
|
try {
|
||||||
|
|
||||||
|
let message = JSON.parse(JSON.stringify(item));
|
||||||
|
delete message.id;
|
||||||
|
|
||||||
|
client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 });
|
||||||
|
|
||||||
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
|
//remove from database
|
||||||
await promisifyBuilder(nosql.remove().where("id", id));
|
await promisifyBuilder(nosql.remove().where("id", id));
|
||||||
|
|
||||||
|
|
@ -494,27 +417,23 @@ exports.install = function(instance) {
|
||||||
console.log("processDataFromDatabase", error);
|
console.log("processDataFromDatabase", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
processingData = false;
|
processingData = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(records.length > 0)
|
if (records.length > 0) {
|
||||||
{
|
|
||||||
//clean backup file
|
//clean backup file
|
||||||
if(counter > 0) nosql.clean();
|
if (counter > 0) nosql.clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
//no data in db, remove
|
//no data in db, remove
|
||||||
if(records.length == 0)
|
if (records.length == 0) {
|
||||||
{
|
if (counter > 0) nosql.drop();
|
||||||
if(counter > 0) nosql.drop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
|
|
@ -524,8 +443,6 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSettings();
|
instance.on('options', main);
|
||||||
|
//instance.reconfigure();
|
||||||
//instance.on('options', instance.reconfigure);
|
|
||||||
//instance.reconfigure();
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"easy-crc": "0.0.2",
|
"easy-crc": "0.0.2",
|
||||||
"jsmodbus": "^4.0.6",
|
"jsmodbus": "^4.0.6",
|
||||||
"log4js": "^6.3.0",
|
"log4js": "^6.3.0",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"mqtt": "^4.2.6",
|
"mqtt": "^4.2.6",
|
||||||
"nodemailer": "^6.9.7",
|
"nodemailer": "^6.9.7",
|
||||||
"serialport": "^9.2.8",
|
"serialport": "^9.2.8",
|
||||||
|
|
|
||||||
27
readme.md
27
readme.md
|
|
@ -1,15 +1,18 @@
|
||||||
[](https://www.totaljs.com/support/)
|
# Total.js version 3 - Flow
|
||||||
|
|
||||||
- [__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 terminal / command-line
|
||||||
- open app directory
|
- open app directory
|
||||||
- install latest version of Total.js from NPM `$ npm install total.js`
|
- run `$ npm install`
|
||||||
- run `$ node debug.js`
|
- run `$ node index.js`
|
||||||
- open browser `http://127.0.0.1:8000`
|
- open browser `http://0.0.0.0:12345`
|
||||||
|
|
||||||
|
# Settings configuration:
|
||||||
|
- set in databases directory
|
||||||
|
- chosen options:
|
||||||
|
- restore_from_backup => number of records, that are restored from db after thingsboard reconnected
|
||||||
|
- restore_backup_wait => number of seconds between bulk of records are sent
|
||||||
|
- node_status_nok_time => number of hours after which the node sends a message to thingsboard that its status is NOK
|
||||||
|
- phases => number of phases in RVO
|
||||||
|
- cloud_topic => we need to set cloud_topic, if we want to send telemetry to RADO's thingsboard
|
||||||
|
- daily_report => if we want to create and send daily reports by email, set this to 1
|
||||||
|
- send_changed_node_numbers => if we are sending daily reports, we need to keep a track of changed node numbers. They are processed on the cloud. Set this to 1 if yes.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue