Compare commits
No commits in common. "total4-citysys-flowserver-with-debian12" and "master" have entirely different histories.
total4-cit
...
master
39 changed files with 7188 additions and 12568 deletions
36
addSwitch.py
36
addSwitch.py
|
|
@ -1,36 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
# 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|time_of_last_communication:number
|
table.nodes : node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
||||||
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
|
table.settings : rvo_name:string|lang:string|temperature_adress:string|latitude:number|longitude:number|mqtt_host:string|mqtt_clientid:string|mqtt_username:string|mqtt_port:number|maintanace_mode:boolean|projects_id:number|controller_type:string|serial_port:string|backup_on_failure:boolean|restore_from_backup:number|restore_backup_wait:number
|
||||||
table.pins : pin: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
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
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[1] + 1 : i[2] ]
|
|
||||||
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()
|
|
||||||
#
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,2 +1,31 @@
|
||||||
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean|time_of_last_communication:number
|
node:number|tbname:string|line:number|profile:string|processed:boolean|status:boolean
|
||||||
+|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|............................................................................................................................................................................................................................................................
|
+|3522|RO8rjaBDy21qPQJzW7oD96ApK3xmNleVZg9Ed4Gw|1||1|1|...........
|
||||||
|
+|4018|3JjOWdylwgNLzxVab7NEznkZ2vG64rq8PEB5QmDo|1||1|1|...........
|
||||||
|
+|4019|Z5KyJe9nEg1QNbWlX0wWRB0oDjBLdqzR83VGv624|1||1|1|...........
|
||||||
|
+|4154|1JMYvnx2RzKEo4aWQ7DmN5AL8yZV3m9NBePXbrdj|1||1|0|...........
|
||||||
|
+|3907|PjLblDgRBO6WQqnxmkJ59r0Jv3ewZN4p5a89yKdY|1||1|1|...........
|
||||||
|
+|4148|dz4ojlpP85JMgDLZWkQOoGAaKYqQexEr62GXRV1y|1||1|1|...........
|
||||||
|
+|4153|d5xjWYMwEJon6rLlK7yBYmAqgV4DaOeNB9ZX3Gzb|1||1|1|...........
|
||||||
|
+|3938|gRoJEyXVx4qD9er287LP1v7wBzGldaPjLWQKm3Mv|1||1|1|...........
|
||||||
|
+|3802|K94XLav1glVRnyQ6r01BNzkme3YJwBxM5oOzdP2j|1||1|1|...........
|
||||||
|
+|4015|d9x2V5LGYBzXp4mMRAOBDj7PloaqJwnQj6DgrNe3|1||1|0|...........
|
||||||
|
+|3929|B5EoxeMVp4zwr8nqW0GjDpARjvD1PNamOGbLg63Z|1||1|1|...........
|
||||||
|
+|3946|aw4eELG2DlPMdn1JW0B1DnAqQXOZRN3xB5yp8VKr|1||1|1|...........
|
||||||
|
+|4014|ZmRwd93QL4gaezxEbAxW5971prn2XjlPvGyqJ6BO|1||1|1|...........
|
||||||
|
+|4155|eod9aRWLVl34Gx1Dn7VoaaA2rz6qjgmpEXwQJN5Z|1||1|1|...........
|
||||||
|
+|4149|3a5oqJN1bgnx4Ol9dk86NBAByE6jQ8mKDWMpGrLV|1||1|1|...........
|
||||||
|
+|3642|EjgWGnXaLy9opPOz20n694086BlYM3w1deVQvbKr|1||1|1|...........
|
||||||
|
+|3636|wvKJdZML6mXP4DzWBAXWNW7jxNloa5g23Ve9Y1ry|1||1|1|...........
|
||||||
|
+|3991|Nzp2OoJlqn6r1ZgvdA3GWdAabBwP5G4eE3RQmyxD|1||1|1|...........
|
||||||
|
+|3994|PLBJzmK1r3Gynd6OW0gGdM0e5wV4vx9bDEqNgYR8|1||1|1|...........
|
||||||
|
+|3990|52dD6ZlV1QaOpRBmbAqKZgkKnGzWMLj4eJq38Pgo|1||1|1|...........
|
||||||
|
+|3967|rDbQ84xzwgdqEoPm3kbJw3k9anOZY1RXyBv2LVM6|1||1|1|...........
|
||||||
|
+|3977|E6Kg9oDnLWyzPRMva7vrwa7Jxp4VG58qO2w1lZYe|1||1|1|...........
|
||||||
|
+|3757|roKgWqY95V3mXMRzyAjm8D7bLjexpJPvaGDBw826|1||1|1|...........
|
||||||
|
+|3633|nJL5lPMwBx23YpqRe0rlKV7damXvWVbOrD4gNzy8|1||1|1|...........
|
||||||
|
+|3744|ZmRwd93QL4gaezxEbAxW5O71prn2XjlPvGyqJ6BO|1||1|1|...........
|
||||||
|
+|4023|eod9aRWLVl34Gx1Dn7VoaMA2rz6qjgmpEXwQJN5Z|1||1|1|...........
|
||||||
|
+|3720|3a5oqJN1bgnx4Ol9dk86NZAByE6jQ8mKDWMpGrLV|1||1|1|...........
|
||||||
|
+|3734|EjgWGnXaLy9opPOz20n69V086BlYM3w1deVQvbKr|1||1|1|...........
|
||||||
|
+|3741|wvKJdZML6mXP4DzWBAXWN17jxNloa5g23Ve9Y1ry|1||1|1|...........
|
||||||
|
+|3721|Nzp2OoJlqn6r1ZgvdA3GWKAabBwP5G4eE3RQmyxD|1||0|0|...........
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
[{"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,12 +20,9 @@ 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_main_open|NOTICE|Hlavné dvere boli otvorené|Main door has been opened|...............
|
+|door_has_been_open|NOTICE|Dvere boli otvorené|Door has been open|...............
|
||||||
+|door_em_open|NOTICE|Dvere silovej časti boli otvorené|Power door has been opened|...............
|
+|door_has_been_closed|NOTICE|Dvere boli zatvorené|Door has been closed|...............
|
||||||
+|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_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_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|...............
|
||||||
|
|
@ -37,5 +34,4 @@ 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|FLOW bol reštartovaný|FLOW has been restarted|...............
|
+|flow_restart|NOTICE|Restart flowu|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|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|JzwxZXOvDj1bVrN4nkWw9Qk8qdyBl3MRKLpGPgaQ|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||||
+|2|g9OxBZ5KRwNznlY6pAp6mxkWXvjdEL4eGQobMDy2|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}|...........
|
||||||
+|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}|...........
|
+|3|OzNMgZ9n43qPbjXmy7zWMJA2DKdYvW5e6pxGRrVa|1|{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"08:00","start_time":"20:00"},{"value":0,"end_time":"13:00","start_time":"08:00"}],"astro_clock":true,"dawn_lux_sensor":true,"dusk_lux_sensor":true,"dawn_lux_sensor_value":15,"dusk_lux_sensor_value":15,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}|...........
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
rvo_name:string|lang:string|temperature_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
|
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_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|...........................................
|
+|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|...........................................
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
//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,
|
|
||||||
55: 1000,
|
|
||||||
56: 5500
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = total_energy;
|
|
||||||
48
flow/audit_test_panel.csv
Normal file
48
flow/audit_test_panel.csv
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
LumDimm;NODE (HEX);NODE (DEC);Line;TB name
|
||||||
|
1;299;665;3;gbv4nzqxW0XGAPKVNk8kr25ZQ2l3O6LRBprM97ew
|
||||||
|
2;28A;650;3;0XYElWeKBNJn1gdoMG8lYdDALkPvj4V3xra2q6mO
|
||||||
|
3;296;662;3;gbv4nzqxW0XGAPKVNk8kW48ZQ2l3O6LRBprM97ew
|
||||||
|
4;297;663;1;LpkVlmq4b3jMwJQxBZ8akayrXAP6o97Ke0aOYEg2
|
||||||
|
5;29C;668;3;lekrmdvO0BQG1ZW4AV8jeZ5M39xnN2wEbRgPjXLp
|
||||||
|
6;2B1;689;3;q0rElBPdL6kxMAjnzVDRl95emNZY7oOv2wK9gb31
|
||||||
|
7;2AB;683;3;XKQbz3WAwY21dGa0R453rWyJm9PZOjqlvpr6Nkeo
|
||||||
|
8;2B0;688;3;PaGbQ3wBAZWOmRvK9VDpvz5endLJYopEqlkzNMxX
|
||||||
|
9;2B9;697;3;joqRYBVL30k9eQWOlZ5qwpD2KJpNEmA6gPxXzwaM
|
||||||
|
10;293;659;3;Ymn9oleRxJ0vw17WzAyGwdyEBk4ObdMXj2VgpNLG
|
||||||
|
11;294;660;3;gj7zbKV46oQ1p2e0AJ8XqZDG3YNWaRrlOEXvBxmM
|
||||||
|
12;295;661;3;laYK7Pomn2bNZXEpedDxAqyOJkQ3WwV49gqxLrAR
|
||||||
|
13;2A0;672;2;0XYElWeKBNJn1gdoMG8lON5ALkPvj4V3xra2q6mO
|
||||||
|
14;2B4;692;2;l9YkRpoB2vVa0mKqEO8ZGGDjW43eXnJML6GxzbwQ
|
||||||
|
15;2B2;690;2;wGjQobgOK0n2YqBZmVDVR3DR9ep6EXA1ka3vzlP7
|
||||||
|
16;27C;636;2;M6ogKQW09bOXewAYvZyvJqyJrV1aRnPGE37p42Nx
|
||||||
|
17;27B;635;2;Vq2JaWpw1OdBKmNeoj8w605XE40l3kgL76Azb9QP
|
||||||
|
18;2B6;694;2;Jm32GR1qpwQxlZza0N5mE15AP96YbOKLogrXVW4e
|
||||||
|
19;2B5;693;2;KjbN4q7JPZmexgdnz2yKdn5YAWwO0Q3BMX6ERLoV
|
||||||
|
20;2B3;691;1;lekrmdvO0BQG1ZW4AV8jzq8M39xnN2wEbRgPjXLp
|
||||||
|
21;27F;639;3;BOjEzGRZ46bnp9wa2A8z76D0JkmW1QPNdrqevXVL
|
||||||
|
22;27E;638;3;9xgzG4Op1BrKZPmoQkDrmj8E73ndJNMjavAwX2Re
|
||||||
|
23;27D;637;3;koW06PeGrLlBp2YJQE5Ogw5RmMaXKzj3wOAZg9n7
|
||||||
|
24;28F;655;2;RMgnK93rkoAazbqdQ4yBYpDZ1YXGx6pmwBeVEP2O
|
||||||
|
25;288;648;2;gaMGN4x1e9JlZz0QPRDd9Rym6dVr3OpvqKnoWBbk
|
||||||
|
26;298;664;1;oGVzxNWP9lrjaQ7vKODQ7g51gqp62YZREmdw3XBM
|
||||||
|
27;29F;671;3;AvVdgzYJZaPx3oMqeED4Oj8NnmKkw716bRO90jLB
|
||||||
|
28;280;640;2;WjBL12pg63eX4N9P7zy0XYyEJKmlbkGwZMx0avQV
|
||||||
|
29;28B;651;2;qaAOzENGrvpbe0VoK7D6Ld519PZmdg3nl24JLQMk
|
||||||
|
30;27A;634;2;NGWamnYqlP1wbgrZQxDAWm5e2X7OVAK69koR04vL
|
||||||
|
31;29E;670;2;dlE1VQjYrNx9gZRmb38g1YyoLBO4qaAk2M6JPnG7
|
||||||
|
32;281;641;2;vnmG4kJxaXWNBgMQq0D7Mz5e9oZzOAlr6LdR3w2V
|
||||||
|
33;278;632;2;LpkVlmq4b3jMwJQxBZ8aM78rXAP6o97Ke0aOYEg2
|
||||||
|
34;29D;669;3;Y9aLW03wOZkABvKXbMyL0lyV1xdNj72r4egqGRzJ
|
||||||
|
35;2A8;680;1;KL2jNOVpdARa9XvoeJDPga8bkmPBxqn7Ww3gzGQ1
|
||||||
|
36;2BA;698;1;mYnBzbeGaAL62jowRv59M35Xq9QpZ0K7O1dg4xVl
|
||||||
|
37;29B;667;1;MzXBoWbEZjO0lrpqnRyoJ4DkmVeaNAGdL9g4QKxP
|
||||||
|
38;289;649;1;0p2rwdP7aGoOQLJNgAynJNy6xWXbmMe3nvZqlzkV
|
||||||
|
39;290;656;1;BrQx3NGKgVMRaXYAo9y1GE8ZzkWnj1le6bdOLE20
|
||||||
|
40;2AA;682;1;vnreBJ6PMqgz20pYEL82XQyG1jkWwdQxZVNAOlmK
|
||||||
|
41;285;645;1;jklN4JpQAx362o9XYZDN6wDgrWw1P7GEbdBM0vRV
|
||||||
|
42;283;643;1;oZmYXEbw9lVWRv1jLxDe9bDdgAMz4PKQnNJ6eB23
|
||||||
|
43;282;642;1;pEonaKBOGbj9034MgJ8W3G8qXvxNWVkAPQz21R6L
|
||||||
|
44;287;647;1;BLQal6Pn9oz1KmNgek5Yqd50vd2MAbqG3OV7Rp4j
|
||||||
|
45;286;646;1;4agVJ9dPQkmp1R2X3EDJKxyrK6ZlNoM0n7qxBOev
|
||||||
|
46;29A;666;1;9PpgLEnvk4WMV6RmOJybMGDaeAXzo2BQNG3K17Zw
|
||||||
|
47;28E;654;1;Mmp93b2nvd7OoqgBeEyEZq5kjlAV1Y4ZNXwW0zLG
|
||||||
|
|
|
@ -1,348 +0,0 @@
|
||||||
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 = null; //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);
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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 } });
|
|
||||||
}
|
|
||||||
} catch (e) { 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) {
|
|
||||||
//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);
|
|
||||||
|
|
||||||
};
|
|
||||||
6372
flow/cmd_manager.js
6372
flow/cmd_manager.js
File diff suppressed because it is too large
Load diff
|
|
@ -1,60 +0,0 @@
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
175
flow/csv_import.js
Normal file
175
flow/csv_import.js
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
exports.id = 'csv_import';
|
||||||
|
exports.title = 'CsvImport';
|
||||||
|
exports.version = '1.0.0';
|
||||||
|
exports.group = 'Worksys';
|
||||||
|
exports.color = '#2134B0';
|
||||||
|
exports.input = 1;
|
||||||
|
exports.output = ["red", "white"];
|
||||||
|
exports.click = false;
|
||||||
|
exports.author = 'Daniel Segeš';
|
||||||
|
exports.icon = 'file-import';
|
||||||
|
exports.options = { edge: "undefined" };
|
||||||
|
|
||||||
|
exports.html = `<div class="padding">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div data-jc="textbox" data-jc-path="edge" data-jc-config="placeholder:undefined;required:true" class="m">CSV Import</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
exports.readme = `# load csv to table db`;
|
||||||
|
|
||||||
|
//config
|
||||||
|
let delimiter = ";";
|
||||||
|
let uniqueColumn = "node";
|
||||||
|
let path = "flow/audit_test_panel.csv";
|
||||||
|
let startFrom = 1;
|
||||||
|
let table = "nodes";
|
||||||
|
let mapImport = {
|
||||||
|
2: "node",
|
||||||
|
4: "tbname",
|
||||||
|
3: "line"
|
||||||
|
};
|
||||||
|
|
||||||
|
//10.0.0.62
|
||||||
|
delimiter = ";";
|
||||||
|
uniqueColumn = "node";
|
||||||
|
path = "flow/audit_rvo14_lampy.csv";
|
||||||
|
startFrom = 1;
|
||||||
|
table = "nodes";
|
||||||
|
mapImport = {
|
||||||
|
1: "node",
|
||||||
|
3: "tbname",
|
||||||
|
2: "line"
|
||||||
|
};
|
||||||
|
|
||||||
|
//notification
|
||||||
|
delimiter = ";";
|
||||||
|
uniqueColumn = undefined;
|
||||||
|
path = "flow/notifikacie.csv";
|
||||||
|
startFrom = 1;
|
||||||
|
table = "notifications";
|
||||||
|
mapImport = {
|
||||||
|
0: "key",
|
||||||
|
1: "weight",
|
||||||
|
2: "en",
|
||||||
|
3: "sk"
|
||||||
|
};
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
exports.install = function(instance) {
|
||||||
|
|
||||||
|
//console.log("csv import installed");
|
||||||
|
|
||||||
|
instance.on("close", () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
instance.on("data", (flowdata) => {
|
||||||
|
|
||||||
|
instance.send(0, "start import");
|
||||||
|
console.log("csv import", flowdata.data);
|
||||||
|
|
||||||
|
//{table: "nodes", startFrom: 1, delimiter: ";", uniqueColumn: "node", path: "flow/audit_rvo14_lampy.csv", mapImport: {1: "node", 3: "tbname", 2: "line"}}
|
||||||
|
|
||||||
|
|
||||||
|
if(typeof flowdata.data === 'object')
|
||||||
|
{
|
||||||
|
console.log("*******************", flowdata.data);
|
||||||
|
|
||||||
|
if(!flowdata.data.hasOwnProperty("table"))
|
||||||
|
{
|
||||||
|
instance.send(0, "!!!!csv import - nedefinovana tabulka");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flowdata.data.hasOwnProperty("uniqueColumn"))
|
||||||
|
{
|
||||||
|
//instance.send(0, "!!!!csv import - nedefinovane uniqueColumn");
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flowdata.data.hasOwnProperty("path"))
|
||||||
|
{
|
||||||
|
instance.send(0, "!!!!csv import - nedefinovana cesta k suboru");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!flowdata.data.hasOwnProperty("mapImport"))
|
||||||
|
{
|
||||||
|
instance.send(0, "!!!!csv import - nedefinovany mapImport");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
table = flowdata.data.table;
|
||||||
|
uniqueColumn = flowdata.data.uniqueColumn;
|
||||||
|
if(uniqueColumn === "") uniqueColumn = undefined;
|
||||||
|
|
||||||
|
path = flowdata.data.path;
|
||||||
|
mapImport = flowdata.data.mapImport;
|
||||||
|
|
||||||
|
if(flowdata.data.hasOwnProperty("delimiter")) delimiter = flowdata.data.delimiter;
|
||||||
|
if(flowdata.data.hasOwnProperty("startFrom")) startFrom = flowdata.data.startFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var db = TABLE(table);
|
||||||
|
db.clear();
|
||||||
|
|
||||||
|
let keys = Object.keys(mapImport);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = fs.readFileSync(path, 'utf8')
|
||||||
|
|
||||||
|
let lines = data.split("\n");
|
||||||
|
|
||||||
|
for(let i = startFrom; i < lines.length; i++)
|
||||||
|
{
|
||||||
|
let line = lines[i];
|
||||||
|
if(line === "") continue;
|
||||||
|
|
||||||
|
let data = line.split(delimiter);
|
||||||
|
if(data.length == 0) continue;
|
||||||
|
|
||||||
|
let insertData = {};
|
||||||
|
|
||||||
|
keys.map(function(key){
|
||||||
|
let k = mapImport[key];
|
||||||
|
|
||||||
|
//console.log("importineg", i, key, k);
|
||||||
|
|
||||||
|
if(data[key] != undefined) insertData[k] = data[key].trim();
|
||||||
|
else{
|
||||||
|
console.log("undefined", key, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("insertData", insertData);
|
||||||
|
|
||||||
|
if(uniqueColumn != undefined)
|
||||||
|
{
|
||||||
|
db.insert(insertData, true).where(uniqueColumn, insertData[uniqueColumn]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db.insert(insertData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("csv import finished");
|
||||||
|
instance.send(0, "csv import finished");
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
instance.send(0, err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
124
flow/db_init.js
124
flow/db_init.js
|
|
@ -1,124 +0,0 @@
|
||||||
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.html = `<div class="padding">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="host" data-jc-config="placeholder:test.mosquitto.org;required:false" class="m">Hostname or IP address (if not empty - setting will override db setting)</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="port" data-jc-config="placeholder:1883;required:true" class="m">Port</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="clientid">@(Client id)</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div data-jc="textbox" data-jc-path="username" class="m">@(Username)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
|
|
||||||
exports.readme = `
|
|
||||||
# DB initialization
|
|
||||||
`;
|
|
||||||
|
|
||||||
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper.js');
|
|
||||||
const { initNotification } = require('./helper/notification_reporter');
|
|
||||||
const errorHandler = require('./helper/ErrorToServiceHandler');
|
|
||||||
|
|
||||||
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 = {})
|
|
||||||
|
|
||||||
dbs.settings = {
|
|
||||||
edge_fw_version: "2025-04-24", //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"],
|
|
||||||
|
|
||||||
//dynamic values
|
|
||||||
masterNodeIsResponding: true, //cmd_manager
|
|
||||||
maintenance_mode: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
1861
flow/designer.json
1861
flow/designer.json
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,187 +1,163 @@
|
||||||
class DataToTbHandler {
|
class DataToTbHandler
|
||||||
|
{
|
||||||
|
constructor(index) {
|
||||||
|
this.index = index;
|
||||||
|
|
||||||
constructor(index) {
|
this.previousValues = {};
|
||||||
this.index = index;
|
this.debug = false;
|
||||||
|
this.messageCounter = 0;
|
||||||
|
|
||||||
// time, after new value for the given key will be resend to tb (e.g. {status: "OK"})
|
this.sender = "";
|
||||||
this.timeToHoldTbValue = 30 * 60; //30 minutes
|
}
|
||||||
this.previousValues = {};
|
|
||||||
this.debug = false;
|
|
||||||
this.messageCounter = 0;
|
|
||||||
this.itIsNodeReadout = false;
|
|
||||||
this.sender = "";
|
|
||||||
|
|
||||||
// if attribute change difference is less than limit value, we do not send to tb.
|
dump()
|
||||||
this.attributeChangeLimit = {
|
{
|
||||||
temperature: 0.5,
|
console.log("----------------------------");
|
||||||
Phase_1_voltage: 2,
|
console.log("previousValues", this.previousValues);
|
||||||
Phase_2_voltage: 2,
|
console.log("----------------------------");
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
setSender(sender)
|
||||||
|
{
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
dump() {
|
isEmptyObject( obj ) {
|
||||||
console.log("----------------------------");
|
for ( var name in obj ) {
|
||||||
console.log("previousValues", this.previousValues);
|
return false;
|
||||||
console.log("----------------------------");
|
}
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
setSender(sender) {
|
sendToTb(dataToTb, instance)
|
||||||
this.sender = sender;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
isEmptyObject(obj) {
|
if(!FLOW.OMS_brokerready)
|
||||||
for (var _ in obj) {
|
{
|
||||||
return false;
|
return dataToTb;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
let keys = Object.keys(dataToTb);
|
||||||
|
|
||||||
|
if(keys.length == 0)
|
||||||
|
{
|
||||||
|
if(this.debug) console.log("sendToTb received epty object", dataToTb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sendToTb(data, instance) {
|
let tbname = keys[0];
|
||||||
|
let ts;
|
||||||
|
|
||||||
//not to modify data object, we do deep copy:
|
let arrayOfValues = dataToTb[tbname];
|
||||||
let dataCopy = JSON.parse(JSON.stringify(data));
|
let arrayOfValuesToSend = [];
|
||||||
|
|
||||||
let keys = Object.keys(dataCopy);
|
for(let i = 0; i < arrayOfValues.length; i++)
|
||||||
|
{
|
||||||
|
ts = arrayOfValues[i].ts;
|
||||||
|
|
||||||
if (keys.length == 0) {
|
//console.log("sendToTb------------>before", arrayOfValues[i].values, tbname);
|
||||||
if (this.debug) console.log("sendToTb received empty object", dataCopy);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tbname = keys[0];
|
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
||||||
let ts;
|
|
||||||
|
|
||||||
let arrayOfValues = dataCopy[tbname];
|
//console.log("sendToTb------------>after", values);
|
||||||
let arrayOfValuesToSend = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < arrayOfValues.length; i++) {
|
if(!this.isEmptyObject(values))
|
||||||
|
{
|
||||||
|
arrayOfValuesToSend.push({ts: ts, values: values});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ts = arrayOfValues[i].ts;
|
if(arrayOfValuesToSend.length == 0)
|
||||||
let values = this.prepareValuesForTb(tbname, ts, arrayOfValues[i].values);
|
{
|
||||||
|
//if(this.debug) console.log("data not sent - empty array");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isEmptyObject(values)) {
|
/*
|
||||||
arrayOfValuesToSend.push({ ts: ts, values: values });
|
let dataToTb = {
|
||||||
}
|
[tbname]: [
|
||||||
|
{
|
||||||
|
"ts": Date.now(),
|
||||||
|
"values": values
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
this.messageCounter++;
|
||||||
|
|
||||||
if (arrayOfValuesToSend.length == 0) {
|
let dataToTbModified = {
|
||||||
//if(this.debug) console.log("data not sent - empty array");
|
[tbname]: arrayOfValuesToSend
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.messageCounter++;
|
//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);
|
||||||
|
|
||||||
let dataToTbModified = {
|
instance.send(this.index, dataToTbModified);
|
||||||
[tbname]: arrayOfValuesToSend
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//console.log(this.sender + " DATA SEND TO TB ", tbname, this.messageCounter, new Date(ts), dataToTbModified[tbname][0].values, this.instance);
|
getDiffTimestamp(key)
|
||||||
//if(this.debug) console.log(this.sender + " DATA SEND TO TB ", this.index, tbname, arrayOfValuesToSend);
|
{
|
||||||
instance.send(this.index, dataToTbModified);
|
let seconds = 60*60;//1h
|
||||||
}
|
//seconds = 1;//for testing
|
||||||
|
|
||||||
|
//TODO set different value for given key!!!
|
||||||
|
//if(key == "status") seconds = 2*60*60;//2h
|
||||||
|
|
||||||
getDiffTimestamp(key) {
|
let timestampDiffToRemoveKey = seconds*1000;
|
||||||
//TODO set different value for given key!!!
|
|
||||||
//if(key == "status") this.timeToHoldTbValue = 2*60*60;//2h
|
|
||||||
return this.timeToHoldTbValue * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return timestampDiffToRemoveKey;
|
||||||
|
}
|
||||||
|
|
||||||
prepareValuesForTb(tbname, timestamp, values) {
|
prepareValuesForTb(tbname, timestamp, values)
|
||||||
|
{
|
||||||
|
let keys = Object.keys(values);
|
||||||
|
if(!this.previousValues.hasOwnProperty(tbname))
|
||||||
|
{
|
||||||
|
this.previousValues[tbname] = {};
|
||||||
|
}
|
||||||
|
|
||||||
let keys = Object.keys(values);
|
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
||||||
|
|
||||||
if (keys.includes("lifetime")) this.itIsNodeReadout = true;
|
for(let i = 0; i < keys.length; i++)
|
||||||
|
{
|
||||||
|
let key = keys[i];
|
||||||
|
let value = values[key];
|
||||||
|
|
||||||
if (!this.previousValues.hasOwnProperty(tbname)) {
|
if(!this.previousValues[tbname].hasOwnProperty(key))
|
||||||
this.previousValues[tbname] = {};
|
{
|
||||||
}
|
this.previousValues[tbname][key] = {ts: timestamp, value: value};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
//if(this.debug) console.log("prepareValuesForTb", tbname, timestamp, values);
|
if(this.previousValues[tbname][key].value === value)
|
||||||
|
{
|
||||||
|
let diff = timestamp - this.previousValues[tbname][key].ts;
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
let timestampDiffToRemoveKey = this.getDiffTimestamp(key);
|
||||||
|
if(diff > timestampDiffToRemoveKey)
|
||||||
|
{
|
||||||
|
this.previousValues[tbname][key].ts = Date.now();
|
||||||
|
//if(this.debug) console.log(this.sender + ": update ts for key", key, "diff is", diff, "messageCounter", this.messageCounter);
|
||||||
|
|
||||||
let key = keys[i];
|
}
|
||||||
let value = values[key];
|
else
|
||||||
|
{
|
||||||
|
delete values[key];
|
||||||
|
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.previousValues[tbname][key].value = value;
|
||||||
|
this.previousValues[tbname][key].ts = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.previousValues[tbname].hasOwnProperty(key)) {
|
}
|
||||||
this.previousValues[tbname][key] = { ts: timestamp, value: value };
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// attributeData ==> {voltage: {ts:333333, value:5}}
|
return values;
|
||||||
let attributeData = this.previousValues[tbname][key];
|
}
|
||||||
let attributeToChange = false;
|
|
||||||
if (key in this.attributeChangeLimit) attributeToChange = true;
|
|
||||||
let limit = this.attributeChangeLimit[key];
|
|
||||||
let timestampDiffToRemoveKey;
|
|
||||||
|
|
||||||
//this will ensure "node statecode" will be sent just once an hour
|
|
||||||
if (this.itIsNodeReadout && key === "statecode") {
|
|
||||||
attributeData.value = value;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
delete values[key];
|
|
||||||
//if(this.debug) console.log(this.sender + ": delete key", key, "diff is", diff, "messageCounter", this.messageCounter, timestampDiffToRemoveKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
attributeData.value = value;
|
|
||||||
attributeData.ts = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = DataToTbHandler;
|
module.exports = DataToTbHandler;
|
||||||
|
|
||||||
|
|
@ -1,91 +1,124 @@
|
||||||
const { MD5 } = require('./md5.js');
|
const { MD5 } = require('./md5.js');
|
||||||
const { networkInterfaces } = require('os');
|
const { networkInterfaces } = require('os');
|
||||||
|
|
||||||
class ErrorToServiceHandler {
|
class ErrorToServiceHandler
|
||||||
|
{
|
||||||
constructor() {
|
constructor() {
|
||||||
this.previousValues = {};
|
this.previousValues = {};
|
||||||
|
|
||||||
this.project_id = undefined;
|
this.projects_id = undefined;
|
||||||
|
|
||||||
const nets = networkInterfaces();
|
const nets = networkInterfaces();
|
||||||
this.ipAddresses = {};
|
this.ipAddresses = Object.create(null); // Or just '{}', an empty object
|
||||||
|
|
||||||
for (const name of Object.keys(nets)) {
|
for (const name of Object.keys(nets)) {
|
||||||
for (const net of nets[name]) {
|
for (const net of nets[name]) {
|
||||||
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
|
||||||
if (net.family === 'IPv4' && !net.internal) {
|
if (net.family === 'IPv4' && !net.internal) {
|
||||||
if (!this.ipAddresses[name]) {
|
if (!this.ipAddresses[name]) {
|
||||||
this.ipAddresses[name] = [];
|
this.ipAddresses[name] = [];
|
||||||
}
|
}
|
||||||
this.ipAddresses[name].push(net.address);
|
this.ipAddresses[name].push(net.address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//console.log(this.ipAddresses);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setProjectId(project_id) {
|
setProjectsId(projects_id)
|
||||||
this.project_id = project_id;
|
{
|
||||||
|
this.projects_id = projects_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
processMessage(message, seconds) {
|
processMessage(message, seconds, message_type)
|
||||||
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 ts = Date.now();
|
let timestamp = new Date().getTime();
|
||||||
|
|
||||||
//keep in memory - default value is 1h
|
//keep in memory
|
||||||
if (seconds === undefined) seconds = 60 * 60;
|
if (seconds === undefined) seconds = 60*60;
|
||||||
|
|
||||||
if (!this.previousValues.hasOwnProperty(key)) {
|
if(!this.previousValues.hasOwnProperty(key))
|
||||||
this.previousValues[key] = { ts: ts, duration: seconds };
|
{
|
||||||
|
this.previousValues[key] = {ts: timestamp, duration: seconds};
|
||||||
}
|
}
|
||||||
|
|
||||||
let diff = (ts - this.previousValues[key].ts);
|
let diff = (timestamp - this.previousValues[key].ts);
|
||||||
if (diff < this.previousValues[key].duration * 1000) return false;
|
if(diff < this.previousValues[key].duration*1000) return;
|
||||||
|
|
||||||
this.previousValues[key].ts = ts;
|
this.previousValues[key].ts = timestamp;
|
||||||
|
*/
|
||||||
|
|
||||||
return message;
|
//-------------------------
|
||||||
}
|
|
||||||
|
|
||||||
sendMessageToService(message, seconds, message_type) {
|
//send to service
|
||||||
|
|
||||||
// if error occures too early FLOW.GLOBALS.settings.project_id is still undefined
|
let dataToInfoSender = {id: this.projects_id};
|
||||||
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
|
||||||
toService[message_type] = message;
|
dataToInfoSender[message_type] = message;
|
||||||
|
dataToInfoSender.ipAddresses = this.ipAddresses;
|
||||||
|
|
||||||
console.log("ErrorToServiceHandler------------------------>send to service", toService);
|
console.log("ErrorToServiceHandler------------------------>send to service", dataToInfoSender);
|
||||||
|
|
||||||
|
//TODO UGLY!!!
|
||||||
|
if(this.projects_id === undefined) this.projects_id = FLOW.OMS_projects_id;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(this.projects_id === undefined)
|
||||||
|
{
|
||||||
|
console.log("this.projects_id is undefined");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
RESTBuilder.make(function(builder) {
|
RESTBuilder.make(function(builder) {
|
||||||
builder.method('POST');
|
builder.method('POST');
|
||||||
builder.post(toService);
|
builder.post(dataToInfoSender);
|
||||||
builder.url('http://192.168.252.2:8004/sentmessage');
|
builder.url('http://192.168.252.2:8004/sentmessage');
|
||||||
|
|
||||||
builder.callback(function(err, response, output) {
|
builder.callback(function(err, response, output) {
|
||||||
console.log("process.on error send", err, response, output, toService);
|
console.log("process.on error send", err, response, output, dataToInfoSender);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorHandler = new ErrorToServiceHandler();
|
module.exports = ErrorToServiceHandler;
|
||||||
|
|
||||||
|
|
||||||
module.exports = errorHandler;
|
|
||||||
//module.exports = ErrorToServiceHandler;
|
|
||||||
72
flow/helper/error_reporter.js
Normal file
72
flow/helper/error_reporter.js
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
//key is device, value = str
|
||||||
|
let sentValues= {};
|
||||||
|
|
||||||
|
function sendError(func, device, weight, str, extra, tb_output, instance, type) {
|
||||||
|
// if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){
|
||||||
|
// return; // Allow debug messages only if CONFIG.debug is active
|
||||||
|
// }
|
||||||
|
|
||||||
|
let key = device;
|
||||||
|
if(type != undefined) key = type;
|
||||||
|
|
||||||
|
if(sentValues.hasOwnProperty(key))
|
||||||
|
{
|
||||||
|
if(sentValues[key] == str)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sentValues[key] = str;
|
||||||
|
|
||||||
|
let content = {
|
||||||
|
"type": weight,
|
||||||
|
"status": "new",
|
||||||
|
"source": {
|
||||||
|
"func":func,
|
||||||
|
"component":instance.id,
|
||||||
|
"component_name":instance.name,
|
||||||
|
"edge":device
|
||||||
|
},
|
||||||
|
"message":str,
|
||||||
|
"message_data": extra
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg = {};
|
||||||
|
msg[device] = [
|
||||||
|
{
|
||||||
|
"ts": Date.now(),
|
||||||
|
"values": {
|
||||||
|
"_event":content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Msg can be outputted from components only after configuration
|
||||||
|
/*if (canSendErrData()){
|
||||||
|
sendBufferedErrors();
|
||||||
|
} else {
|
||||||
|
bufferError(msg);
|
||||||
|
}*/
|
||||||
|
instance.send(tb_output, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let ERRWEIGHT = {
|
||||||
|
EMERGENCY: "emergency", // System unusable
|
||||||
|
ALERT: "alert", // Action must be taken immidiately
|
||||||
|
CRITICAL: "critical", // Component unable to function
|
||||||
|
ERROR: "error", // Error, but component able to recover from it
|
||||||
|
WARNING: "warning", // Possibility of error, system running futher
|
||||||
|
NOTICE: "notice", // Significant message but not an error, things user might want to know about
|
||||||
|
INFO: "informational", // Info
|
||||||
|
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sendError,
|
||||||
|
ERRWEIGHT
|
||||||
|
}
|
||||||
56
flow/helper/error_reporting.js
Normal file
56
flow/helper/error_reporting.js
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
const ERRWEIGHT = {
|
||||||
|
EMERGENCY: "emergency", // System unusable
|
||||||
|
ALERT: "alert", // Action must be taken immidiately
|
||||||
|
CRITICAL: "critical", // Component unable to function
|
||||||
|
ERROR: "error", // Error, but component able to recover from it
|
||||||
|
WARNING: "warning", // Possibility of error, system running futher
|
||||||
|
NOTICE: "notice", // Significant message but not an error, things user might want to know about
|
||||||
|
INFO: "informational", // Info
|
||||||
|
DEBUG: "debug" // Debug - only if CONFIG.debug is enabled
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function sendError(func, device, weight, str, extra){
|
||||||
|
if ((weight === ERRWEIGHT.DEBUG) && (instance.CONFIG.debug === false)){
|
||||||
|
return; // Allow debug messages only if CONFIG.debug is active
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = {
|
||||||
|
"type": weight,
|
||||||
|
"status": "new",
|
||||||
|
"source": {
|
||||||
|
"func":func,
|
||||||
|
"component":instance.id,
|
||||||
|
"component_name":instance.name,
|
||||||
|
"edge":myEdge
|
||||||
|
},
|
||||||
|
"message":str,
|
||||||
|
"message_data": extra
|
||||||
|
};
|
||||||
|
let msg = {};
|
||||||
|
msg[device] = [
|
||||||
|
{
|
||||||
|
"ts": Date.now(),
|
||||||
|
"values": {
|
||||||
|
"_event":content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Msg can be outputted from components only after configuration
|
||||||
|
/*if (canSendErrData()){
|
||||||
|
sendBufferedErrors();
|
||||||
|
} else {
|
||||||
|
bufferError(msg);
|
||||||
|
}*/
|
||||||
|
instance.send(0, msg); // Even if error server is unavailable, send this message to output, for other possible component connections
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sendError,
|
||||||
|
ERRWEIGHT,
|
||||||
|
}
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
//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,9 +1,10 @@
|
||||||
//key is device, value = message {}
|
|
||||||
let sentValues = {};
|
|
||||||
let notificationsData = null;
|
|
||||||
let rvoName;
|
|
||||||
|
|
||||||
//sendNotification("CMD Manager: process cmd", SETTINGS.rvoTbName, "dimming_profile_was_successfully_received_by_node", { node: node }, "", SEND_TO.tb, instance);
|
const { promisifyBuilder, makeMapFromDbResult } = require('./db_helper.js');
|
||||||
|
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
|
||||||
|
|
@ -23,70 +24,87 @@ 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");
|
||||||
|
|
||||||
function initNotification() {
|
console.log("initNotifications done" );
|
||||||
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 = {};
|
|
||||||
|
|
||||||
let notification = notificationsData[key];
|
if(notificationsData[key])
|
||||||
|
{
|
||||||
|
weight = notificationsData[key].weight;
|
||||||
|
weight = weight.toLowerCase();
|
||||||
|
|
||||||
if (notification) {
|
tpl = notificationsData[key][lang];
|
||||||
weight = notification.weight.toLowerCase();
|
tpl = template(tpl, params);
|
||||||
|
|
||||||
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);
|
console.error("sendNotification: Notifications: undefined key", key, func, notificationsData);
|
||||||
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] = message;
|
sentValues[saveKey] = tpl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storeToSendValues) sentValues[saveKey] = message;
|
if(saveKey == "rvo_door")
|
||||||
|
{
|
||||||
|
//console.log("******", saveKey, sentValues[saveKey], tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(storeToSendValues) sentValues[saveKey] = tpl;
|
||||||
|
|
||||||
|
let str = FLOW.OMS_rvo_name;
|
||||||
|
if(str != "") str = str + ": ";
|
||||||
|
str = str + tpl;
|
||||||
|
|
||||||
let content = {
|
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": message,
|
"message":str,
|
||||||
"message_data": extra
|
"message_data": extra
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -95,7 +113,7 @@ function sendNotification(func, device, key, params, extra, tb_output, instance,
|
||||||
{
|
{
|
||||||
"ts": Date.now(),
|
"ts": Date.now(),
|
||||||
"values": {
|
"values": {
|
||||||
"_event": content
|
"_event":content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -106,7 +124,6 @@ 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;
|
||||||
|
|
@ -115,7 +132,6 @@ function sendNotification(func, device, key, params, extra, tb_output, instance,
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sendNotification,
|
sendNotification,
|
||||||
ERRWEIGHT,
|
initNotifications,
|
||||||
initNotification
|
ERRWEIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,90 @@
|
||||||
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) => {
|
exec(command, (error, stdout, stderr) => {
|
||||||
if (error == null) resolve(stdout);
|
if(error == null) resolve(stdout);
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function writeData(port, data, readbytes, timeout) {
|
async function writeData(port, data, readbytes, timeout){
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
// If first item in data array is 255, we just write broadcast command to rsPort
|
//readbytes = 0 = broadcast
|
||||||
// We wait 3 seconds and resolve(["broadcast"])
|
if(readbytes == undefined) readbytes = 0;
|
||||||
// It is important to resolve with array
|
if(timeout == undefined) timeout = 10000;//10s, default timeout MASTERA je 3s
|
||||||
if (data[0] == 255) {
|
|
||||||
|
//cmd-manager mame http route POST / terminal a tomu sa tiez nastavuje timeout!!!
|
||||||
|
|
||||||
|
var callback = function(data) {
|
||||||
|
rsPortReceivedData.push(...data);
|
||||||
|
let l = rsPortReceivedData.length;
|
||||||
|
|
||||||
|
if(l >= readbytes)
|
||||||
|
{
|
||||||
|
port.removeListener('data', callback);
|
||||||
|
|
||||||
|
clearTimeout(t);
|
||||||
|
resolve(rsPortReceivedData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
port.removeListener('data', callback);
|
||||||
|
|
||||||
|
let t = setTimeout(() => {
|
||||||
|
port.removeListener('data', callback);
|
||||||
|
|
||||||
|
console.log("serialport helper: writeData TIMEOUT READING", rsPortReceivedData);
|
||||||
|
|
||||||
|
reject("TIMEOUT READING");
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
let rsPortReceivedData = [];
|
||||||
|
|
||||||
|
if(readbytes > 0) port.on('data', callback);
|
||||||
|
|
||||||
port.write(Buffer.from(data), function(err) {
|
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 = {
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,56 @@
|
||||||
function bytesToInt_orig(bytes, numberOfBytes) {
|
function bytesToInt(bytes, numberOfBytes)
|
||||||
|
{
|
||||||
|
let buffer = [];
|
||||||
|
if(Array.isArray(bytes))
|
||||||
|
{
|
||||||
|
buffer = bytes.slice(0);
|
||||||
|
if(numberOfBytes != undefined)
|
||||||
|
{
|
||||||
|
buffer = bytes.slice(bytes.length - numberOfBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else buffer.push(bytes);
|
||||||
|
|
||||||
let buffer = [];
|
//var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
|
||||||
if (Array.isArray(bytes)) {
|
let l = (buffer.length - 1) * 8;
|
||||||
buffer = bytes.slice(0);
|
|
||||||
if (numberOfBytes != undefined) {
|
|
||||||
buffer = bytes.slice(bytes.length - numberOfBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else buffer.push(bytes);
|
|
||||||
//var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
|
|
||||||
|
|
||||||
let l = (buffer.length - 1) * 8;
|
let decimal = 0;
|
||||||
let decimal = 0;
|
for(let i = 0; i < buffer.length; i++)
|
||||||
for (let i = 0; i < buffer.length; i++) {
|
{
|
||||||
var s = buffer[i] << l;
|
var s = buffer[i] << l;
|
||||||
if (l < 8) s = buffer[i]
|
if(l < 8) s = buffer[i]
|
||||||
decimal = decimal + s;
|
decimal = decimal + s;
|
||||||
l = l - 8;
|
|
||||||
}
|
|
||||||
// console.log("decimal utils.js: ", decimal);
|
|
||||||
|
|
||||||
let decimal1 = 0n;
|
l = l - 8;
|
||||||
for (let i = 0; i < buffer.length; i++) {
|
|
||||||
decimal1 += BigInt(buffer[i]) * (2n ** BigInt((buffer.length - 1 - i) * 8));
|
}
|
||||||
}
|
|
||||||
// console.log("decimal biging utils.js: ", decimal1);
|
return decimal;
|
||||||
return decimal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//bytestouintBE
|
|
||||||
function bytesToInt(bytes, numberOfBytes) {
|
|
||||||
|
|
||||||
let buffer = [];
|
|
||||||
if (Array.isArray(bytes)) {
|
|
||||||
buffer = bytes.slice(0);
|
|
||||||
if (numberOfBytes != undefined) {
|
|
||||||
buffer = bytes.slice(bytes.length - numberOfBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else buffer.push(bytes);
|
|
||||||
|
|
||||||
console.log(bytes, buffer);
|
|
||||||
|
|
||||||
let result = 0;
|
|
||||||
for (let i = 0; i < buffer.length; i++) {
|
|
||||||
result = (result << 8) | bytes[i];
|
|
||||||
}
|
|
||||||
// console.log("decimal biging utils.js: ", decimal1);
|
|
||||||
|
|
||||||
console.log("originall: ", bytesToInt_orig(buffer));
|
|
||||||
console.log("uint little endian: ", bytesToUintLE(buffer));
|
|
||||||
console.log('neww: ', result >>> 0);
|
|
||||||
return result >>> 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function bytesToUintLE(bytes, numberOfBytes) {
|
|
||||||
|
|
||||||
let buffer = [];
|
|
||||||
if (Array.isArray(bytes)) {
|
|
||||||
buffer = bytes.slice(0);
|
|
||||||
if (numberOfBytes != undefined) {
|
|
||||||
buffer = bytes.slice(bytes.length - numberOfBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else buffer.push(bytes);
|
|
||||||
//var decimal = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
|
|
||||||
|
|
||||||
let result = 0;
|
|
||||||
for (let i = buffer.length - 1; i <= 0; i--) {
|
|
||||||
result = (result << 8) | bytes[i];
|
|
||||||
}
|
|
||||||
return result >>> 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function resizeArray(arr, newSize, defaultValue) {
|
function resizeArray(arr, newSize, defaultValue) {
|
||||||
while (newSize > arr.length)
|
while(newSize > arr.length)
|
||||||
arr.push(defaultValue);
|
arr.push(defaultValue);
|
||||||
arr.length = newSize;
|
arr.length = newSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
longToByteArray = function(/*long*/long) {
|
longToByteArray = function(/*long*/long) {
|
||||||
// we want to represent the input as a 8-bytes array
|
// we want to represent the input as a 8-bytes array
|
||||||
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
for (var index = 0; index < byteArray.length; index++) {
|
for ( var index = 0; index < byteArray.length; index ++ ) {
|
||||||
var byte = long & 0xff;
|
var byte = long & 0xff;
|
||||||
byteArray[index] = byte;
|
byteArray [ index ] = byte;
|
||||||
long = (long - byte) / 256;
|
long = (long - byte) / 256 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return byteArray;
|
return byteArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
function addDays(date, days) {
|
function addDays(date, days) {
|
||||||
var result = new Date(date);
|
var result = new Date(date);
|
||||||
result.setDate(result.getDate() + days);
|
result.setDate(result.getDate() + days);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -107,64 +61,64 @@ sleep(2000).then(() => {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function sleep(time) {
|
function sleep (time) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, time));
|
return new Promise((resolve) => setTimeout(resolve, time));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmptyObject(obj) {
|
function isEmptyObject( obj ) {
|
||||||
for (var name in obj) {
|
for ( var name in obj ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertUTCDateToLocalDate(date) {
|
function convertUTCDateToLocalDate(date) {
|
||||||
var newDate = new Date(date);
|
var newDate = new Date(date);
|
||||||
newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());
|
newDate.setMinutes(date.getMinutes() + date.getTimezoneOffset());
|
||||||
return newDate;
|
return newDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addZeroBefore(n) {
|
function addZeroBefore(n) {
|
||||||
return (n < 10 ? '0' : '') + n;
|
return (n < 10 ? '0' : '') + n;
|
||||||
}
|
}
|
||||||
|
|
||||||
var convertBase = function() {
|
var convertBase = function () {
|
||||||
|
|
||||||
function convertBase(baseFrom, baseTo) {
|
function convertBase(baseFrom, baseTo) {
|
||||||
return function(num) {
|
return function (num) {
|
||||||
return parseInt(num, baseFrom).toString(baseTo);
|
return parseInt(num, baseFrom).toString(baseTo);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// binary to decimal
|
// binary to decimal
|
||||||
convertBase.bin2dec = convertBase(2, 10);
|
convertBase.bin2dec = convertBase(2, 10);
|
||||||
|
|
||||||
// binary to hexadecimal
|
// binary to hexadecimal
|
||||||
convertBase.bin2hex = convertBase(2, 16);
|
convertBase.bin2hex = convertBase(2, 16);
|
||||||
|
|
||||||
// decimal to binary
|
// decimal to binary
|
||||||
convertBase.dec2bin = convertBase(10, 2);
|
convertBase.dec2bin = convertBase(10, 2);
|
||||||
|
|
||||||
// decimal to hexadecimal
|
// decimal to hexadecimal
|
||||||
convertBase.dec2hex = convertBase(10, 16);
|
convertBase.dec2hex = convertBase(10, 16);
|
||||||
|
|
||||||
// hexadecimal to binary
|
// hexadecimal to binary
|
||||||
convertBase.hex2bin = convertBase(16, 2);
|
convertBase.hex2bin = convertBase(16, 2);
|
||||||
|
|
||||||
// hexadecimal to decimal
|
// hexadecimal to decimal
|
||||||
convertBase.hex2dec = convertBase(16, 10);
|
convertBase.hex2dec = convertBase(16, 10);
|
||||||
|
|
||||||
return convertBase;
|
return convertBase;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
bytesToInt,
|
bytesToInt,
|
||||||
longToByteArray,
|
longToByteArray,
|
||||||
addDays,
|
addDays,
|
||||||
addZeroBefore,
|
addZeroBefore,
|
||||||
resizeArray,
|
resizeArray,
|
||||||
isEmptyObject,
|
isEmptyObject,
|
||||||
sleep,
|
sleep,
|
||||||
convertUTCDateToLocalDate
|
convertUTCDateToLocalDate
|
||||||
}
|
}
|
||||||
|
|
@ -3,9 +3,12 @@ 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 = 2;
|
exports.input = 1;
|
||||||
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');
|
||||||
|
|
||||||
|
|
@ -19,12 +22,13 @@ 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)`;
|
||||||
|
|
||||||
exports.install = function(instance) {
|
const fs = require('fs');
|
||||||
|
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"));
|
||||||
|
|
@ -33,28 +37,43 @@ exports.install = 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() {
|
|
||||||
if (!configured) return;
|
|
||||||
|
|
||||||
if (Object.keys(allValues).length > 0) {
|
function sendValues()
|
||||||
let dataToSend = { ...allValues };
|
{
|
||||||
dataToSend.id = id;
|
const id = FLOW.OMS_projects_id;
|
||||||
dataToSend.ipAddresses = ipAddresses;
|
|
||||||
|
|
||||||
instance.send(0, dataToSend);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
allValues = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,20 +81,25 @@ exports.install = function(instance) {
|
||||||
clearInterval(sendAllValuesInterval);
|
clearInterval(sendAllValuesInterval);
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.on("0", _ => {
|
instance.on("data", (flowdata) => {
|
||||||
id = FLOW.GLOBALS.settings.project_id;
|
|
||||||
if (id) configured = true;
|
allValues = { ...allValues, ...flowdata.data};
|
||||||
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,7 +3,6 @@ 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';
|
||||||
|
|
@ -16,7 +15,7 @@ exports.readme = `
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const modbus = require('jsmodbus');
|
const modbus = require('jsmodbus');
|
||||||
const {SerialPort} = require('serialport');
|
const SerialPort = require('serialport');
|
||||||
const { timeoutInterval, deviceConfig } = require("../databases/modbus_config");
|
const { timeoutInterval, deviceConfig } = require("../databases/modbus_config");
|
||||||
const { sendNotification } = require('./helper/notification_reporter');
|
const { sendNotification } = require('./helper/notification_reporter');
|
||||||
|
|
||||||
|
|
@ -32,16 +31,13 @@ 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;
|
|
||||||
|
|
||||||
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 = {};
|
||||||
|
|
@ -59,35 +55,27 @@ exports.install = function(instance) {
|
||||||
// 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;
|
||||||
|
|
||||||
this.socket = new SerialPort({path: "/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('socket connection error', e);
|
||||||
if (e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') {
|
if(e.code == 'ECONNREFUSED' || e.code == 'ECONNRESET') {
|
||||||
console.log(exports.title + ' Waiting 10 seconds before trying to connect again');
|
console.log(exports.title + ' Waiting 10 seconds before trying to connect again');
|
||||||
setTimeout(obj.startSocket, 10000);
|
setTimeout(obj.startSocket, 10000);
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +86,7 @@ exports.install = function(instance) {
|
||||||
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
|
||||||
|
|
@ -115,7 +103,7 @@ exports.install = function(instance) {
|
||||||
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(this.indexInDeviceConfig == 0) setTimeout(this.readRegisters, this.timeoutInterval);
|
||||||
else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES);
|
else setTimeout(this.readRegisters, DELAY_BETWEEN_DEVICES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,18 +117,21 @@ 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);
|
||||||
|
|
@ -153,21 +144,25 @@ 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);
|
||||||
|
|
@ -178,13 +173,14 @@ exports.install = function(instance) {
|
||||||
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++;
|
||||||
|
|
@ -194,7 +190,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,7 +199,8 @@ 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;
|
||||||
|
|
||||||
|
|
@ -212,7 +209,8 @@ exports.install = function(instance) {
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|
@ -225,20 +223,26 @@ 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.indexInDeviceConfig + 1 == deviceConfig.length) {
|
if(this.lengthOfActualDeviceStream == this.index)
|
||||||
|
{
|
||||||
|
if(this.indexInDeviceConfig + 1 == deviceConfig.length)
|
||||||
|
{
|
||||||
this.indexInDeviceConfig = 0;
|
this.indexInDeviceConfig = 0;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
this.indexInDeviceConfig += 1;
|
this.indexInDeviceConfig += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,22 +250,25 @@ exports.install = function(instance) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -272,47 +279,49 @@ 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 (this.phases.includes(singleValue)) {
|
if (["Phase_1_voltage", "Phase_2_voltage", "Phase_3_voltage"].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);
|
{
|
||||||
sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", { phase: phase }, "", SEND_TO.tb, instance, "voltage" + phase);
|
FLOW.OMS_no_voltage.add(phase);
|
||||||
|
sendNotification("modbus_reader: checkNullVoltage", tbName, "no_voltage_on_phase", {phase: phase}, "", SEND_TO.tb, instance, "voltage" + phase );
|
||||||
// console.log('no voltage')
|
// 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 numberOfNodes*15. 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 500. 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)
|
||||||
const numberOfNodes = Object.keys(FLOW.GLOBALS.nodesData).length;
|
{
|
||||||
if (numberOfNodes == 0) numberOfNodes = 20; // to make sure, we send notification if totalPower is more than 300
|
|
||||||
|
|
||||||
if (actualTotalPower > numberOfNodes * 15 && 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 <= numberOfNodes * 15 && this.offNotificationSent == false) {
|
else if(actualTotalPower <= 600 && 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;
|
||||||
|
|
@ -322,25 +331,18 @@ exports.install = function(instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isObjectEmpty = (objectName) => {
|
const isObjectEmpty = (objectName) => {
|
||||||
return Object.keys(objectName).length === 0 && objectName.constructor === Object;
|
return Object.keys(objectName).length === 0 && objectName.constructor === Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
setTimeout(() => {
|
||||||
|
|
||||||
phases = FLOW.GLOBALS.settings.phases;
|
|
||||||
tbName = FLOW.GLOBALS.settings.rvoTbName;
|
|
||||||
noVoltage = FLOW.GLOBALS.settings.no_voltage;
|
|
||||||
mainSocket = new SocketWithClients();
|
mainSocket = new SocketWithClients();
|
||||||
|
tbName = FLOW.OMS_rvo_tbname;
|
||||||
console.log("novoltage: ", noVoltage, typeof noVoltage);
|
|
||||||
|
|
||||||
// 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);
|
||||||
}
|
|
||||||
|
|
||||||
instance.on("0", function(_) {
|
}, 25000);
|
||||||
main();
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
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);
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
31
flow/notifikacie.csv
Normal file
31
flow/notifikacie.csv
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
key;weight;en;sk
|
||||||
|
switching_profile_point_applied_to_line;INFO;Switching profile point applied to line no. ${line} : ${value};Aplikovaný bod spínacieho profilu na línií č. ${line} : ${value}
|
||||||
|
dusk_has_occured;INFO;Dusk has occured;Nastal súmrak
|
||||||
|
dawn_has_occured;INFO;Dawn has occured;Nastal úsvit
|
||||||
|
dimming_profile_was_successfully_received_by_node;NOTICE;Dimming profile was successfully received by node no. ${node};Stmievací profil bol úspešne prijatý nodom č. ${node}
|
||||||
|
master_node_is_responding_again;NOTICE;Master node is responding again;Master node začal znovu odpovedať
|
||||||
|
command_was_sent_from_terminal_interface;DEBUG;A command was sent from terminal interface;Z terminálu bol odoslaný príkaz
|
||||||
|
master_node_is_not_responding;ALERT;Master node is not responding;Master node neodpovedá
|
||||||
|
configuration_of_dimming_profile_to_node_failed;ALERT;Configuration of dimming profile to node no. ${node} has failed;Konfigurácia stmievacieho profilu pre node č. ${node} zlyhala
|
||||||
|
circuit_breaker_was_turned_on_line;NOTICE;Circuit breaker was turned on - line no. ${line};Zapnutie ističa na línii č. ${line}
|
||||||
|
circuit_breaker_was_turned_off_line;ERROR;Circuit breaker was turned off - line no. ${line};Vypnutie ističa na línií č. ${line}
|
||||||
|
dimming_profile_was_processed_for_node;INFO;Dimming profile was processed for node no. ${node};Stmievací profil bol spracovaný pre node č. ${node}
|
||||||
|
switching_profile_was_processed_for_line;INFO;Switching profile was processed for line no. ${line};Spínací profil bol spracovaný pre líniu č. ${line}
|
||||||
|
thermometer_is_not_responding;WARNING;Thermometer is not responding;Teplomer neodpovedá
|
||||||
|
thermometer_is_responding_again;NOTICE;Thermometer is responding again;Teplomer znovu odpovedá
|
||||||
|
thermometer_sends_invalid_data;WARNING;Thermometer sends invalid data;Teplomer posiela neplatné hodnoty
|
||||||
|
main_switch_has_been_turned_off;CRITICAL;Main switch has been turned off;Hlavný vypínač bol vypnutý
|
||||||
|
main_switch_has_been_turned_on;NOTICE;Main switch has been turned on;Hlavný vypínač bol zapnutý
|
||||||
|
power_supply_has_disconnected_input;ALERT;Power supply has disconnected input;Napájací zdroj nemá napätie na vstupe
|
||||||
|
power_supply_works_correctly;NOTICE;Power supply works correctly ;Napájací zdroj pracuje správne
|
||||||
|
battery_level_is_low;ERROR;Battery level is low;Batéria má nízku úroveň napätia
|
||||||
|
battery_level_is_ok;NOTICE;Battery level is OK;Batéria má správnu úroveň napätia
|
||||||
|
door_has_been_open;NOTICE;Door has been open;Dvere boli otvorené
|
||||||
|
door_has_been_closed;NOTICE;Door has been closed;Dvere boli zatvorené
|
||||||
|
door_has_been_open_without_permision_alarm_is_on;WARNING;Door has been open without permision - alarm is on;Dvere boli otvorené bez povolania - zapnutá siréna
|
||||||
|
state_of_contactor_for_line;INFO;State of contactor for line no. ${line} is ${value};Stav stýkača pre líniu č. ${line} je ${value}
|
||||||
|
local_database_is_corrupted;CRITICAL;;
|
||||||
|
electrometer_is_not_responding;ERROR;Electrometer is not responding;Elektromer neodpovedá
|
||||||
|
no_voltage_detected_on_phase;CRITICAL;No voltage detected on phase no. ${phase};Na fáze č. ${phase} nie je napätie
|
||||||
|
electrometer_is_responding_again;NOTICE;Electrometer is responding again;Elektromer znovu odpovedá
|
||||||
|
voltaga_on_phase_has_been_restored;NOTICE;Voltaga on phase no. ${phase} has been restored;Napätie na fáze č. ${phase} bolo obnovené
|
||||||
|
|
|
@ -1,243 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
124
flow/slack_connector.js
Normal file
124
flow/slack_connector.js
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
@ -7,10 +7,13 @@ 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>
|
||||||
|
|
@ -57,11 +60,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']['en'];
|
let message = value[k[0]][0]['values']['_event']['message'];
|
||||||
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;
|
||||||
|
|
@ -89,15 +92,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,10 +108,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,46 +119,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;
|
||||||
}
|
}
|
||||||
|
|
@ -163,16 +166,15 @@ 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 run options again', 'red');
|
instance.status('Please enter name', 'red');
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -181,6 +183,5 @@ exports.install = function(instance) {
|
||||||
};
|
};
|
||||||
|
|
||||||
instance.on('options', instance.reconfigure);
|
instance.on('options', instance.reconfigure);
|
||||||
setTimeout(instance.reconfigure, 10000);
|
instance.reconfigure();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
@ -2,16 +2,14 @@ exports.id = 'thermometer';
|
||||||
exports.title = 'Thermometer';
|
exports.title = 'Thermometer';
|
||||||
exports.group = 'Worksys';
|
exports.group = 'Worksys';
|
||||||
exports.color = '#5CB36D';
|
exports.color = '#5CB36D';
|
||||||
exports.input = 1;
|
|
||||||
exports.version = '1.0.3';
|
exports.version = '1.0.3';
|
||||||
exports.output = ["red", "white", "blue"];
|
exports.output = ["red", "white", "blue"];
|
||||||
|
exports.author = 'Rastislav Kovac';
|
||||||
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 { errLogger, logger, monitor } = require('./helper/logger');
|
const instanceSendTo = {
|
||||||
|
|
||||||
const SEND_TO = {
|
|
||||||
debug: 0,
|
debug: 0,
|
||||||
tb: 1,
|
tb: 1,
|
||||||
dido_controller: 2
|
dido_controller: 2
|
||||||
|
|
@ -19,81 +17,204 @@ const SEND_TO = {
|
||||||
|
|
||||||
//read temperature - frequency
|
//read temperature - frequency
|
||||||
let timeoutMin = 5;//minutes
|
let timeoutMin = 5;//minutes
|
||||||
let NUMBER_OF_FAILURES_TO_SEND_ERROR = 13;
|
|
||||||
|
var path = require('path');
|
||||||
|
var log4js = require("log4js");
|
||||||
|
|
||||||
|
log4js.configure({
|
||||||
|
appenders: {
|
||||||
|
errLogs: { type: 'file', filename: path.join(__dirname + "/../", 'err.txt') },
|
||||||
|
monitorLogs: { type: 'file', compress:true, daysToKeep: 2, maxLogSize: 1048576, backups: 1, keepFileExt: true, filename: path.join(__dirname + "/../", 'monitor.txt') },
|
||||||
|
console: { type: 'console' }
|
||||||
|
},
|
||||||
|
categories: {
|
||||||
|
errLogs: { appenders: ['console', 'errLogs'], level: 'error' },
|
||||||
|
monitorLogs: { appenders: ['console', 'monitorLogs'], level: 'trace' },
|
||||||
|
//another: { appenders: ['console'], level: 'trace' },
|
||||||
|
default: { appenders: ['console'], level: 'trace' }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const errLogger = log4js.getLogger("errLogs");
|
||||||
|
const logger = log4js.getLogger();
|
||||||
|
const monitor = log4js.getLogger("monitorLogs");
|
||||||
|
|
||||||
|
//logger.debug("text")
|
||||||
|
//monitor.info('info');
|
||||||
|
//errLogger.error("some error");
|
||||||
|
|
||||||
|
const { promisifyBuilder, makeMapFromDbResult } = require('./helper/db_helper');
|
||||||
|
const dbSettings = TABLE("settings");
|
||||||
|
let temperatureAddress = "";
|
||||||
|
|
||||||
|
async function loadSettings()
|
||||||
|
{
|
||||||
|
//todo global FLOW.OMS_edgeName is making problem, so we load it here as well, it should not be
|
||||||
|
let responseSettings = await promisifyBuilder(dbSettings.find());
|
||||||
|
temperatureAddress = responseSettings[0]["temperature_adress"];
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
|
||||||
exports.install = function(instance) {
|
exports.install = function(instance) {
|
||||||
|
|
||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
const { sendNotification } = require('./helper/notification_reporter');
|
const { sendNotification, ERRWEIGHT } = require('./helper/notification_reporter');
|
||||||
|
|
||||||
let startRead;
|
let startRead;
|
||||||
|
let dataToTb;
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
let rvoTbName = "";
|
|
||||||
let temperatureAddress = "";
|
let edgeName = "";
|
||||||
|
|
||||||
|
|
||||||
logger.debug(exports.title, "installed");
|
logger.debug(exports.title, "installed");
|
||||||
|
|
||||||
instance.on("close", function() {
|
instance.on("close", function(){
|
||||||
clearInterval(startRead);
|
clearInterval(startRead);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const main = function() {
|
const start = function() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (temperatureAddress === "") throw "Thermometer: temperatureAddress is not defined";
|
if(FLOW.OMS_controller_type === "unipi")
|
||||||
|
{
|
||||||
|
clearInterval(startRead);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(temperatureAddress === "") throw "gettemperature: temperatureAddress is not defined";
|
||||||
|
|
||||||
|
logger.debug("FLOW.OMS_temperature_adress", FLOW.OMS_temperature_adress);
|
||||||
|
|
||||||
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
|
exec(`owread -C ${temperatureAddress}/temperature`, (error, stdout, stderr) => {
|
||||||
|
|
||||||
if (!error) {
|
edgeName = FLOW.OMS_edgeName;
|
||||||
parseData(stdout)
|
|
||||||
|
if(edgeName !== "")
|
||||||
|
{
|
||||||
|
if(error)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(FLOW.OMS_brokerready == undefined)
|
||||||
|
{
|
||||||
|
logger.debug("gettemparature - FLOW.OMS_brokerready is undefined");
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
start();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(FLOW.OMS_brokerready)
|
||||||
|
{
|
||||||
|
//sendNotification("start", edgeName, ERRWEIGHT.WARNING, "Thermometer is not responding", {"Error": error}, instanceSendTo.tb, instance, "thermometer");
|
||||||
|
sendNotification("start", edgeName, "thermometer_is_not_responding", {}, {"Error": error}, instanceSendTo.tb, instance, "thermometer");
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = "NOK";
|
||||||
|
dataToTb = {
|
||||||
|
[edgeName]: [
|
||||||
|
{
|
||||||
|
"ts": Date.now(),
|
||||||
|
"values": {
|
||||||
|
"status": status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.info("Thermometer is not responding", error, FLOW.OMS_brokerready);
|
||||||
|
|
||||||
|
// instance.send(instanceSendTo.tb, dataToTb); // poslat stav nok do tb, ak to handluje dido_controller ??
|
||||||
|
instance.send(instanceSendTo.dido_controller, {status: "NOK-thermometer"});
|
||||||
|
}
|
||||||
|
else parseData(stdout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
monitor.info("gettemperature: edgeName is not defined", FLOW.OMS_edgeName);
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
start();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
counter++;
|
|
||||||
if (counter == NUMBER_OF_FAILURES_TO_SEND_ERROR) {
|
//instance.send({"Temp":stdout,"stderr":stderr,"err":error});
|
||||||
sendNotification("Thermometer_main", rvoTbName, "thermometer_is_not_responding", {}, { "Error": error }, SEND_TO.tb, instance, "thermometer");
|
|
||||||
monitor.info("Thermometer is not responding", error);
|
|
||||||
}
|
|
||||||
instance.send(SEND_TO.dido_controller, { status: "NOK-thermometer" });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch(err) {
|
||||||
errLogger.error(exports.title, err);
|
errLogger.error(exports.title, err);
|
||||||
clearInterval(startRead);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseData = function(data) {
|
const parseData = function(data) {
|
||||||
|
|
||||||
data = parseFloat(data);
|
data = parseFloat(data);
|
||||||
//logger.debug("Thermometer", data);
|
|
||||||
|
|
||||||
if (isNaN(data)) {
|
logger.debug("gettemperature", data);
|
||||||
errLogger.error("Thermometer sends invalid data");
|
|
||||||
return;
|
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"});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (counter > NUMBER_OF_FAILURES_TO_SEND_ERROR) //1 hour
|
|
||||||
{
|
|
||||||
instance.send(SEND_TO.debug, "Thermometer - temperature data are comming again");
|
|
||||||
sendNotification("Thermometer_parseData", rvoTbName, "thermometer_is_responding_again", {}, "", SEND_TO.tb, instance, "thermometer");
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = {
|
|
||||||
"temperature": Number(data.toFixed(2)),
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.send(SEND_TO.dido_controller, { values: values });
|
|
||||||
counter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.on("data", _ => {
|
setTimeout(function(){
|
||||||
temperatureAddress = FLOW.GLOBALS.settings.temperature_address;
|
start();
|
||||||
rvoTbName = FLOW.GLOBALS.settings.rvoTbName;
|
}, 15000);
|
||||||
startRead = setInterval(main, timeoutMin * 1000 * 60);
|
|
||||||
setTimeout(main, 20000);
|
startRead = setInterval(start, timeoutMin * 1000 * 60);
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
@ -4,27 +4,30 @@ 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 = 2;
|
exports.input = 1;
|
||||||
exports.output = 3;
|
exports.output = ["red", "white", "blue"];
|
||||||
|
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>`;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,47 +41,79 @@ Added:
|
||||||
- rpc response
|
- rpc response
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const { promisifyBuilder } = require('./helper/db_helper');
|
const instanceSendTo = {
|
||||||
const { errLogger, monitor } = require('./helper/logger');
|
debug: 0,
|
||||||
const fs = require('fs');
|
rpcCall: 1,
|
||||||
const mqtt = require('mqtt');
|
services: 2
|
||||||
|
|
||||||
const SEND_TO = {
|
|
||||||
debug: 0,
|
|
||||||
rpcCall: 1,
|
|
||||||
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 client send failure
|
let backup_on_failure = false;//== saveTelemetryOnError - create backup broker 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;
|
||||||
|
|
||||||
// if there is an error in client connection, flow logs to monitor.txt. Not to log messages every second, we use sendClientError variable
|
let errLogger;
|
||||||
let sendClientError = true;
|
let logger;
|
||||||
|
let monitor;
|
||||||
|
|
||||||
process.on('uncaughtException', function(err) {
|
if(useLog4js)
|
||||||
|
{
|
||||||
|
var path = require('path');
|
||||||
|
var log4js = require("log4js");
|
||||||
|
|
||||||
errLogger.error('uncaughtException:', err.message)
|
log4js.configure({
|
||||||
errLogger.error(err.stack);
|
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' }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//TODO
|
errLogger = log4js.getLogger("errLogs");
|
||||||
//send to service
|
logger = log4js.getLogger();
|
||||||
|
monitor = log4js.getLogger("monitorLogs");
|
||||||
|
|
||||||
//process.exit(1);
|
//USAGE
|
||||||
|
//logger.debug("text");
|
||||||
|
//monitor.info('info');
|
||||||
|
//errLogger.error("some error");
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('uncaughtException', function (err) {
|
||||||
|
|
||||||
|
if(errLogger)
|
||||||
|
{
|
||||||
|
errLogger.error('uncaughtException:', err.message)
|
||||||
|
errLogger.error(err.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
//send to service
|
||||||
|
|
||||||
|
//process.exit(1);
|
||||||
})
|
})
|
||||||
|
|
||||||
const nosql = NOSQL('tbdata');
|
const nosql = NOSQL('tbdata');
|
||||||
|
|
@ -87,364 +122,410 @@ const nosqlBackup = NOSQL('/backup/tbdata');
|
||||||
|
|
||||||
exports.install = function(instance) {
|
exports.install = function(instance) {
|
||||||
|
|
||||||
var client;
|
var broker;
|
||||||
var opts;
|
var opts;
|
||||||
var clientReady = false;
|
var brokerready = false;
|
||||||
|
|
||||||
// wsmqtt status for notification purposes on projects.worksys.io database
|
instance.on('options', loadSettings);
|
||||||
let wsmqttName = null;
|
|
||||||
let sendWsStatusVar = null;
|
|
||||||
let wsmqtt_status = 'disconnected';
|
|
||||||
|
|
||||||
function getWsmqttName(host) {
|
mqtt = require('mqtt');
|
||||||
if (host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo';
|
|
||||||
else if (host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01';
|
|
||||||
else if (host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01';
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendWsStatus() {
|
// wsmqtt status for notification purposes on projects.worksys.io database
|
||||||
instance.send(SEND_TO.services, { [wsmqttName]: wsmqtt_status });
|
let wsmqttName = null;
|
||||||
}
|
let sendWsStatusVar = null;
|
||||||
|
let wsmqtt_status = 'disconnected';
|
||||||
|
|
||||||
|
function getWsmqttName(host)
|
||||||
|
{
|
||||||
|
if(host == "tb-demo.worksys.io" || host == '192.168.252.4') return 'wsmqtt_demo';
|
||||||
|
else if(host == "tb-qas01.worksys.io" || host == '192.168.252.5') return 'wsmqtt_qas01';
|
||||||
|
else if(host == "tb-prod01.worksys.io" || host == '192.168.252.1') return 'wsmqtt_prod01';
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendWsStatus()
|
||||||
|
{
|
||||||
|
instance.send(instanceSendTo.services, {[wsmqttName]: wsmqtt_status});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendWsStatusVar = setInterval(sendWsStatus, 180000);
|
||||||
|
|
||||||
|
|
||||||
function main() {
|
//set opts according to db settings
|
||||||
if (!FLOW.dbLoaded) return;
|
async function loadSettings()
|
||||||
|
{
|
||||||
|
|
||||||
loadSettings();
|
if(instance.options.host !== "")
|
||||||
clearInterval(sendWsStatus);
|
{
|
||||||
sendWsStatusVar = setInterval(sendWsStatus, 180000);
|
//override settings from database
|
||||||
}
|
var o = instance.options;
|
||||||
|
opts = {
|
||||||
|
host: o.host,
|
||||||
|
port: o.port,
|
||||||
|
clientId: o.clientid,
|
||||||
|
username: o.username,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
resubscribe: false
|
||||||
|
};
|
||||||
|
|
||||||
//set opts according to db settings
|
wsmqttName = getWsmqttName(o.host);
|
||||||
function loadSettings() {
|
|
||||||
|
|
||||||
if (instance.options.host !== "") {
|
console.log("wsmqttpublich -> loadSettings from instance.options", instance.options);
|
||||||
//override settings from database
|
}
|
||||||
var o = instance.options;
|
else
|
||||||
opts = {
|
{
|
||||||
host: o.host,
|
|
||||||
port: o.port,
|
|
||||||
clientId: o.clientid,
|
|
||||||
username: o.username,
|
|
||||||
rejectUnauthorized: false,
|
|
||||||
resubscribe: false
|
|
||||||
};
|
|
||||||
|
|
||||||
wsmqttName = getWsmqttName(o.host);
|
const dbSettings = TABLE("settings");
|
||||||
|
let responseSettings = await promisifyBuilder(dbSettings.find());
|
||||||
|
|
||||||
console.log("wsmqttpublich -> loadSettings from instance.options", instance.options);
|
backup_on_failure = responseSettings[0]["backup_on_failure"];
|
||||||
}
|
saveTelemetryOnError = backup_on_failure;
|
||||||
else {
|
|
||||||
|
|
||||||
const SETTINGS = FLOW.GLOBALS.settings;
|
restore_from_backup = responseSettings[0]["restore_from_backup"];
|
||||||
backup_on_failure = SETTINGS.backup_on_failure;
|
restore_backup_wait = responseSettings[0]["restore_backup_wait"];
|
||||||
saveTelemetryOnError = backup_on_failure;
|
|
||||||
|
|
||||||
restore_from_backup = SETTINGS.restore_from_backup;
|
let mqtt_host = responseSettings[0]["mqtt_host"];
|
||||||
restore_backup_wait = SETTINGS.restore_backup_wait;
|
let mqtt_clientid = responseSettings[0]["mqtt_clientid"];
|
||||||
|
let mqtt_username = responseSettings[0]["mqtt_username"];
|
||||||
|
let mqtt_port = responseSettings[0]["mqtt_port"];
|
||||||
|
|
||||||
let mqtt_host = SETTINGS.mqtt_host;
|
console.log("wsmqttpublich -> loadSettings from db", responseSettings[0]);
|
||||||
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,
|
||||||
port: mqtt_port,
|
port: mqtt_port,
|
||||||
keepalive: 10,
|
keepalive: 10,
|
||||||
clientId: mqtt_clientid,
|
clientId: mqtt_clientid,
|
||||||
username: mqtt_username,
|
username: mqtt_username,
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
resubscribe: false,
|
resubscribe: false
|
||||||
};
|
};
|
||||||
|
|
||||||
wsmqttName = getWsmqttName(mqtt_host);
|
wsmqttName = getWsmqttName(mqtt_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectToTbServer();
|
connectToTbServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectToTbServer() {
|
function connectToTbServer()
|
||||||
var url = "mqtt://" + opts.host + ":" + opts.port;
|
{
|
||||||
console.log("MQTT URL: ", url);
|
var url = "mqtt://" + opts.host + ":" + opts.port;
|
||||||
|
console.log("MQTT URL: ", url);
|
||||||
|
|
||||||
client = mqtt.connect(url, opts);
|
broker = mqtt.connect(url, opts);
|
||||||
|
|
||||||
client.on('connect', function() {
|
broker.on('connect', function() {
|
||||||
instance.status("Connected", "green");
|
instance.status("Connected", "green");
|
||||||
//monitor.info("MQTT client connected");
|
monitor.info("MQTT broker connected");
|
||||||
|
|
||||||
sendClientError = true;
|
brokerready = true;
|
||||||
clientReady = true;
|
FLOW.OMS_brokerready = brokerready;
|
||||||
wsmqtt_status = 'connected';
|
wsmqtt_status = 'connected';
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('reconnect', function() {
|
broker.on('reconnect', function() {
|
||||||
instance.status("Reconnecting", "yellow");
|
instance.status("Reconnecting", "yellow");
|
||||||
clientReady = false;
|
brokerready = false;
|
||||||
});
|
|
||||||
|
|
||||||
client.on('message', function(topic, message) {
|
FLOW.OMS_brokerready = brokerready;
|
||||||
// message is type of buffer
|
});
|
||||||
message = message.toString();
|
|
||||||
if (message[0] === '{') {
|
|
||||||
|
|
||||||
try {
|
broker.on('message', function(topic, message) {
|
||||||
message = JSON.parse(message);
|
// message is type of buffer
|
||||||
if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) {
|
message = message.toString();
|
||||||
client.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, { qos: 1 });
|
if (message[0] === '{') {
|
||||||
instance.send(SEND_TO.rpcCall, { "device": message.device, "id": message.data.id, "RPC response": { "success": true } });
|
TRY(function() {
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
message = JSON.parse(message);
|
||||||
console.log('MQTT: Error parsing data', e);
|
if (message.hasOwnProperty("device") && message.hasOwnProperty("data") && message.data.hasOwnProperty("id")) {
|
||||||
}
|
broker.publish(topic, `{"device": ${message.device}, "id": ${message.data.id}, "data": {"success": true}}`, {qos:1});
|
||||||
}
|
instance.send(instanceSendTo.rpcCall, {"device": message.device, "id": message.data.id, "RPC response": {"success": true}});
|
||||||
|
}
|
||||||
|
|
||||||
instance.send(SEND_TO.rpcCall, { "topic": topic, "content": message });
|
}, () => instance.debug('MQTT: Error parsing data', message));
|
||||||
});
|
}
|
||||||
|
|
||||||
client.on('close', function() {
|
instance.send(instanceSendTo.rpcCall, {"topic":topic, "content":message });
|
||||||
clientReady = false;
|
});
|
||||||
wsmqtt_status = 'disconnected';
|
|
||||||
|
|
||||||
instance.status("Disconnected", "red");
|
broker.on('close', function(err) {
|
||||||
instance.send(SEND_TO.debug, { "message": "Client CLOSE signal received !" });
|
brokerready = false;
|
||||||
});
|
FLOW.OMS_brokerready = brokerready;
|
||||||
|
wsmqtt_status = 'disconnected';
|
||||||
|
|
||||||
client.on('error', function(err) {
|
if (err && err.toString().indexOf('Error')) {
|
||||||
instance.status("Err: " + err.code, "red");
|
instance.status("Err: "+err.code, "red");
|
||||||
instance.send(SEND_TO.debug, { "message": "Client ERROR signal received !", "error": err, "opt": opts });
|
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
||||||
if (sendClientError) {
|
} else {
|
||||||
monitor.info('MQTT client error', err);
|
instance.status("Disconnected", "red");
|
||||||
sendClientError = false;
|
instance.send(instanceSendTo.debug, {"message":"Broker CLOSE signal received !", "error":err, "opt":opts });
|
||||||
}
|
}
|
||||||
clientReady = false;
|
|
||||||
wsmqtt_status = 'disconnected';
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
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);
|
||||||
|
|
||||||
instance.on("0", _ => {
|
brokerready = false;
|
||||||
main();
|
FLOW.OMS_brokerready = brokerready;
|
||||||
})
|
wsmqtt_status = 'disconnected';
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
instance.on('1', function(data) {
|
}
|
||||||
|
|
||||||
if (clientReady) {
|
instance.on('data', function(data) {
|
||||||
//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);
|
if (brokerready)
|
||||||
client.publish("v1/gateway/telemetry", stringifiedJson, { qos: 1 });
|
{
|
||||||
|
//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();
|
||||||
|
}
|
||||||
|
|
||||||
//backup telemetry
|
}
|
||||||
if (createTelemetryBackup) {
|
|
||||||
data.data.id = UID();
|
|
||||||
nosqlBackup.insert(data.data);
|
|
||||||
|
|
||||||
insertBackupNoSqlCounter++;
|
if (brokerready)
|
||||||
if (insertBackupNoSqlCounter > 150) {
|
{
|
||||||
let options = { compress: true };
|
let stringifiedJson = JSON.stringify(data.data);
|
||||||
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
broker.publish("v1/gateway/telemetry", stringifiedJson, {qos: 1});
|
||||||
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
|
||||||
stream.write("");
|
|
||||||
stream.end();
|
|
||||||
|
|
||||||
insertBackupNoSqlCounter = 0;
|
//backup telemetry
|
||||||
}
|
if(createTelemetryBackup)
|
||||||
}
|
{
|
||||||
|
data.data.id = UID();
|
||||||
|
nosqlBackup.insert(data.data);
|
||||||
|
|
||||||
}
|
insertBackupNoSqlCounter++;
|
||||||
else {
|
if(insertBackupNoSqlCounter > 150)
|
||||||
//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 });
|
let options = {compress: true};
|
||||||
|
let path = __dirname + "/../databases/backup/tbdata.nosql";
|
||||||
|
var stream = new rollers.RollingFileStream(path, noSqlFileSizeLimit, 150, options);
|
||||||
|
stream.write("");
|
||||||
|
stream.end();
|
||||||
|
|
||||||
if (saveTelemetryOnError) {
|
insertBackupNoSqlCounter = 0;
|
||||||
//create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql
|
}
|
||||||
makeBackupFromDbFile();
|
}
|
||||||
|
|
||||||
//write to tb
|
}
|
||||||
data.data.id = UID();
|
else
|
||||||
nosql.insert(data.data);
|
{
|
||||||
}
|
|
||||||
}
|
if(logger) logger.debug("Broker unavailable. Data not sent !", data.data);
|
||||||
});
|
instance.send(instanceSendTo.debug, {"message":"Broker unavailable. Data not sent !", "data": data.data });
|
||||||
|
|
||||||
|
if(saveTelemetryOnError)
|
||||||
|
{
|
||||||
|
//create new file from tbdata.nosql, if file size exceeds given limit, and clear tbdata.nosql
|
||||||
|
makeBackupFromDbFile();
|
||||||
|
|
||||||
|
//write to tb
|
||||||
|
data.data.id = UID();
|
||||||
|
nosql.insert(data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
instance.close = function(done) {
|
instance.close = function(done) {
|
||||||
if (clientReady) {
|
if (brokerready){
|
||||||
client.end();
|
broker.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) {
|
{
|
||||||
counter = fileCounter;
|
if(fileCounter > counter)
|
||||||
}
|
{
|
||||||
}
|
counter = fileCounter;
|
||||||
else if (type == "min") {
|
}
|
||||||
if (counter == 0) counter = fileCounter;
|
}
|
||||||
|
else if(type == "min")
|
||||||
|
{
|
||||||
|
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;
|
{
|
||||||
return;
|
--insertNoSqlCounter;
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
insertNoSqlCounter = 100;
|
insertNoSqlCounter = 100;
|
||||||
|
|
||||||
let source = __dirname + "/../databases/tbdata.nosql";
|
let source = __dirname + "/../databases/tbdata.nosql";
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql";
|
let destination = __dirname + "/../databases/" + counter + "." + "tbdata.nosql";
|
||||||
|
|
||||||
//make backup file
|
//make backup file
|
||||||
fs.copyFileSync(source, destination);
|
fs.copyFileSync(source, destination);
|
||||||
//fs.renameSync(p, p + "." + counter);
|
//fs.renameSync(p, p + "." + counter);
|
||||||
|
|
||||||
//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) return;
|
if(restore_from_backup <= 0)
|
||||||
|
{
|
||||||
|
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);
|
{
|
||||||
return;
|
//console.log("*********restore_backup_wait", diff, restore_backup_wait);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
processingData = true;
|
processingData = true;
|
||||||
|
|
||||||
//get filename to process
|
//get filename to process
|
||||||
let counter = getDbBackupFileCounter("min");
|
let counter = getDbBackupFileCounter("min");
|
||||||
|
|
||||||
//we have some backup files
|
//we have some backup files
|
||||||
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));
|
let o = JSON.parse(JSON.stringify(item));
|
||||||
delete message.id;
|
delete o.id;
|
||||||
|
let message = JSON.stringify(o);
|
||||||
|
|
||||||
client.publish("v1/gateway/telemetry", JSON.stringify(message), { qos: 1 });
|
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));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//process error
|
//process error
|
||||||
console.log("processDataFromDatabase", error);
|
console.log("processDataFromDatabase", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
processingData = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (records.length > 0) {
|
}
|
||||||
//clean backup file
|
else
|
||||||
if (counter > 0) nosql.clean();
|
{
|
||||||
}
|
processingData = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//no data in db, remove
|
if(records.length > 0)
|
||||||
if (records.length == 0) {
|
{
|
||||||
if (counter > 0) nosql.drop();
|
//clean backup file
|
||||||
}
|
if(counter > 0) nosql.clean();
|
||||||
|
}
|
||||||
|
|
||||||
const d = new Date();
|
//no data in db, remove
|
||||||
lastRestoreTime = d.getTime();
|
if(records.length == 0)
|
||||||
|
{
|
||||||
|
if(counter > 0) nosql.drop();
|
||||||
|
}
|
||||||
|
|
||||||
processingData = false;
|
const d = new Date();
|
||||||
|
lastRestoreTime = d.getTime();
|
||||||
|
|
||||||
}
|
processingData = false;
|
||||||
|
|
||||||
instance.on('options', main);
|
}
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
//instance.on('options', instance.reconfigure);
|
||||||
//instance.reconfigure();
|
//instance.reconfigure();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
"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",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue