From 999979b847d9b1576e43ef509b5a24f6feefca7e Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Mon, 26 Jun 2023 18:24:02 +0200 Subject: [PATCH 01/10] Update pyluos version --- pyluos/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyluos/version.py b/pyluos/version.py index bc1c32b..5918abd 100644 --- a/pyluos/version.py +++ b/pyluos/version.py @@ -1 +1 @@ -version = '2.2.12' +version = '3.0.0' From 9259f6b86d479904e5a5806f5aae9f8e256036fc Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Fri, 23 Jun 2023 20:07:57 +0200 Subject: [PATCH 02/10] Update topology management to new Luos V3 routing_Table format. --- pyluos/device.py | 62 +++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/pyluos/device.py b/pyluos/device.py index a1ec198..197875a 100644 --- a/pyluos/device.py +++ b/pyluos/device.py @@ -38,45 +38,37 @@ def __repr__(self): prefill = '' prechild = False for pre, fill, node in RenderTree(self[0], style=DoubleStyle()): - child = [] + # Draw the input part if (node.parent == None): branch = " ┃ " - for i,x in enumerate(node.port_table): - child.append(i) else: - l_port_id = '?' - for i,x in enumerate(node.parent.port_table): - if (x == node.id): - l_port_id = str(i) - r_port_id = node.port_table.index(min(node.port_table)) - for i,x in enumerate(node.port_table): - if ((i != r_port_id) and (x != 65535)): - child.append(i) - branch = str(l_port_id) + ">┃" + str(r_port_id) + " " + branch = "═■┫ " + + # Draw the node body prefill = (prefill[:len(fill)]) if len(prefill) > len(fill) else prefill s +='{:<{fillsize}s}'.format(prefill, fillsize=len(fill)) if (prechild == True): - position = -4 - s = s[:position] + '║' + s[position+1:] - s += " ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n" - tmpstr = "%s╭node %s" % (branch, node.id) - s += pre + '{:^10s}'.format(tmpstr) - if (node.certified == True): - s += '{:^41s}'.format("Certified") + "┃\n" - else: - s += '{:^41s}'.format("/!\\ Not certified") + "┃\n" - s += fill + " ┃ │ " + '{:<20s}{:<20s}{:<5s}'.format("Type", "Alias", "ID")+ "┃\n" + s = s[:-4] + '║' + s[-4+1:] + s += '{:<54s}'.format(" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n") + tmpstr = '{:<52s}'.format("%s╭────────────────── Node %s ──────────────────" % (branch, node.id)) + + if (len(pre)>0): + pre = pre[:-1] + "═" + s += pre + tmpstr + '{:>3s}'.format("┃\n") + s += fill + " ┃ │ " + '{:<20s}{:<20s}{:<4s}'.format("Type", "Alias", "ID") + '{:>3s}'.format("┃\n") for y,elem in enumerate(node.services): if (y == (len(node.services)-1)): - s += fill + " ┃ ╰> " + '{:<20s}{:<20s}{:<5d}'.format(elem.type, elem.alias, elem.id)+ "┃\n" + s += fill + " ┃ ╰> " + '{:<20s}{:<20s}{:<4d}'.format(elem.type, elem.alias, elem.id) + '{:>3s}'.format("┃\n") else: - s += fill + " ┃ ├> " + '{:<20s}{:<20s}{:<5d}'.format(elem.type, elem.alias, elem.id) + "┃\n" - if (not child): - s += fill + " >┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" - prechild = False - else: - s += fill + "╔>┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" + s += fill + " ┃ ├> " + '{:<20s}{:<20s}{:<4d}'.format(elem.type, elem.alias, elem.id) + '{:>3s}'.format("┃\n") + + # Draw the output part + if (node.children): + s += fill + "╔■┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" prechild = True + else: + s += fill + " ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n" + prechild = False prefill = fill return s @@ -160,7 +152,7 @@ def _setup(self): retry = 0 while ('routing_table' not in state): if ('route_table' in state): - self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision on pyluos.\n Please consider updating Luos on your boards") + self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision of pyluos.\n Please consider updating Luos on your boards") return state = self._poll_once() if (time.time()-startTime > 1): @@ -175,17 +167,17 @@ def _setup(self): self._nodes = [] for i, node in enumerate(state['routing_table']): if ('node_id' not in node): - self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision on pyluos.\n Please consider updating Luos on your boards") + self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision of pyluos.\n Please consider updating Luos on your boards") parent_elem = None - # find a parent and create a link - if (min(node["port_table"]) < node["services"][0]["id"]): - parent_id = min(node["port_table"]) + # find a parent and create the link + if (node["con"]["parent"][0] != 0): + parent_id = node["con"]["parent"][0] for elem in self._nodes: if (elem.id == parent_id): parent_elem = elem break; # create the node - self._nodes.append(AnyNode(id=node["node_id"], certified=node["certified"], parent=parent_elem, port_table=node["port_table"])) + self._nodes.append(AnyNode(id=node["node_id"], parent=parent_elem, connection=node["con"])) filtered_services = contList([mod for mod in node["services"] if 'type' in mod and mod['type'] in name2mod.keys()]) From 25f716900367f4904ce680560472ee52b97fa2cf Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Mon, 26 Jun 2023 18:15:28 +0200 Subject: [PATCH 03/10] Make pyluos-bootloader less dependant of luos_bootloader evolutions. --- pyluos/tools/bootloader.py | 252 ++++++++++++++++++++++--------------- 1 file changed, 151 insertions(+), 101 deletions(-) diff --git a/pyluos/tools/bootloader.py b/pyluos/tools/bootloader.py index bab9ff5..21845bc 100644 --- a/pyluos/tools/bootloader.py +++ b/pyluos/tools/bootloader.py @@ -22,23 +22,21 @@ # ******************************************************************************* # Global Variables # ******************************************************************************* -BOOTLOADER_IDLE = 0 -BOOTLOADER_START = 1 -BOOTLOADER_STOP = 2 -BOOTLOADER_READY = 3 -BOOTLOADER_ERASE = 4 -BOOTLOADER_BIN_CHUNK = 5 -BOOTLOADER_BIN_END = 6 -BOOTLOADER_CRC_TEST = 7 -BOOTLOADER_APP_SAVED = 8 -BOOTLOADER_RESET = 9 -BOOTLOADER_READY_RESP = 16 -BOOTLOADER_BIN_HEADER_RESP = 17 -BOOTLOADER_ERASE_RESP = 18 -BOOTLOADER_BIN_CHUNK_RESP = 19 -BOOTLOADER_BIN_END_RESP = 20 -BOOTLOADER_CRC_RESP = 21 -BOOTLOADER_ERROR_SIZE = 32 +# BOOTLOADER_IDLE = 0 +BOOTLOADER_START = "start" +BOOTLOADER_STOP = "stop" +BOOTLOADER_READY = "ready" +BOOTLOADER_ERASE = "erase" +BOOTLOADER_BIN_CHUNK = "bin_chunk" +BOOTLOADER_BIN_END = "bin_end" +BOOTLOADER_CRC = "crc" +BOOTLOADER_APP_SAVED = "app_saved" +BOOTLOADER_RESET = "reset" +BOOTLOADER_ERROR_SIZE = "error_size" + +OKGREEN = '\r\033[92m' +FAIL = '\r\033[91m' +ENDC = '\033[0m' FILEPATH = None NB_SAMPLE_BY_FRAME_MAX = 127 @@ -61,6 +59,8 @@ # @param port connected to the luos gate # @return an object containing routing table # ******************************************************************************* + + def find_network(device): device._send({'detection': {}}) startTime = time.time() @@ -68,14 +68,14 @@ def find_network(device): retry = 0 while ('routing_table' not in state): if ('route_table' in state): - print('version of luos not supported') + print(FAIL + 'version of luos not supported' + ENDC) return None state = device._poll_once() - if (time.time()-startTime > 1): - retry = retry +1 + if (time.time() - startTime > 1): + retry = retry + 1 if retry > 5: # detection is not working - print('Detection failed.') + print(FAIL + 'Detection failed.' + ENDC) return None device._send({'detection': {}}) startTime = time.time() @@ -87,6 +87,8 @@ def find_network(device): # @param target list, routing table # @return a tuple with 2 lists : nodes to reboot and nodes to program # ******************************************************************************* + + def create_target_list(args, state): bypass_node = False nodes_to_program = [] @@ -94,17 +96,23 @@ def create_target_list(args, state): for node in state['routing_table']: # prevent programmation of node 1 bypass_node = False - if(node['node_id'] == 1): + if (node['node_id'] == 1): bypass_node = True # check if node is in target list if not (bypass_node): nodes_to_reboot.append(node['node_id']) for target in args.target: - if(int(node['node_id']) == int(target)): + if (int(node['node_id']) == int(target)): nodes_to_program.append(node['node_id']) + errorList = [] for target in args.target: - if not(int(target) in nodes_to_program): - print ("**** Node " + target + " is not available and will be ignored. ****") + if not (int(target) in nodes_to_program): + errorList.append(target) + + if (len(errorList) > 1): + print("\n** Nodes " + ' '.join(errorList) + " are not available and will be ignored. **") + elif (len(errorList) == 1): + print("** Node " + errorList[0] + " is not available and will be ignored. **") return (nodes_to_reboot, nodes_to_program) @@ -113,7 +121,9 @@ def create_target_list(args, state): # @param command type # @return None # ******************************************************************************* -def send_topic_command(device, topic, command, size = 0): + + +def send_topic_command(device, topic, command, size=0): # create a json file with the list of the nodes to program bootloader_cmd = { 'bootloader': { @@ -127,7 +137,8 @@ def send_topic_command(device, topic, command, size = 0): # send json command device._send(bootloader_cmd) -def send_node_command(device, node, topic, command, size = 0): + +def send_node_command(device, node, topic, command, size=0): # create a json file with the list of the nodes to program bootloader_cmd = { 'bootloader': { @@ -147,6 +158,8 @@ def send_node_command(device, node, topic, command, size = 0): # @param # @return binary size # ******************************************************************************* + + def get_binary_size(): # get number of bytes in binary file with open(FILEPATH, mode="rb") as f: @@ -159,6 +172,8 @@ def get_binary_size(): # @param command type # @return None # ******************************************************************************* + + def send_ready_cmd(device, node, topic): return_value = True # send ready command to the node @@ -170,11 +185,11 @@ def send_ready_cmd(device, node, topic): if 'bootloader' in state: for response in state['bootloader']: if response['response'] == BOOTLOADER_ERROR_SIZE: - print(u" ╰> Node n°", response['node'], "has not enough space in flash memory.") + print(FAIL + u" ╰> Node n°", response['node'], "has not enough space in flash memory." + ENDC) # don't load binary if there is not enough place in flash memory return_value = False else: - print(u" ╰> Node n°", response['node'], "is ready.") + print(OKGREEN + u" ╰> Node n°", response['node'], "is ready." + ENDC) return_value = True break @@ -182,36 +197,39 @@ def send_ready_cmd(device, node, topic): return_value = False return return_value + # ******************************************************************************* # @brief waiting for erase response # @param # @return binary size # ******************************************************************************* + + def waiting_erase(): count = 0 period = 0.4 print("\r ", end='') - print(u"\r ╰> Erase flash ", end='') - while(1): + print(u"\r ╰> Erase flash ", end='') + while (1): time.sleep(period) - if(count == 0): + if (count == 0): print("\r ", end='') - print(u"\r ╰> Erase flash . ", end='') + print(u"\r ╰> Erase flash . ", end='') count += 1 continue - if(count == 1): + if (count == 1): print("\r ", end='') - print(u"\r ╰> Erase flash .. ", end='') + print(u"\r ╰> Erase flash .. ", end='') count += 1 continue - if(count == 2): + if (count == 2): print("\r ", end='') - print(u"\r ╰> Erase flash ... ", end='') + print(u"\r ╰> Erase flash ... ", end='') count += 1 continue - if(count == 3): + if (count == 3): print("\r ", end='') - print(u"\r ╰> Erase flash ", end='') + print(u"\r ╰> Erase flash ", end='') count = 0 continue @@ -220,6 +238,8 @@ def waiting_erase(): # @param command type # @return None # ******************************************************************************* + + def erase_flash(device, topic, nodes_to_program): return_value = True failed_nodes = [] @@ -240,39 +260,39 @@ def erase_flash(device, topic, nodes_to_program): # check if all messages are received while len(failed_nodes): # timeout for exiting loop in case of fails - if(time.time() - init_time > timeout): + if (time.time() - init_time > timeout): return_value = False break # check if it is a response message if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_ERASE_RESP): + if (response['response'] == BOOTLOADER_ERASE): # this node responded, delete it from the failed nodes list if response['node'] in failed_nodes: timeout -= ERASE_TIMEOUT failed_nodes.remove(response['node']) - print(u"\r\n ╰> Flash memory of node ", response['node'], " erased.") + print(OKGREEN + u"\r ╰> Flash memory of node ", response['node'], " erased." + ENDC) state = device._poll_once() # retry sending failed messages for node in failed_nodes: send_node_command(device, node, topic, BOOTLOADER_ERASE) - print(u"\r\n ╰> Retry erase memory of node ", node) + print(u"\r\n ╰> Retry erase memory of node ", node) init_time = time.time() state = device._poll_once() while len(failed_nodes): - if(time.time() - init_time > ERASE_TIMEOUT): + if (time.time() - init_time > ERASE_TIMEOUT): return_value = False break # check if it is a response message if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_ERASE_RESP): - # this node responded, delete it from the failed nodes list + if (response['response'] == BOOTLOADER_ERASE): + # this node responded, delete it from the failed nodes list if response['node'] in failed_nodes: failed_nodes.remove(response['node']) - print(u"\r\n ╰> Flash memory of node ", response['node'], " erased.") + print(OKGREEN + u"\r ╰> Flash memory of node ", response['node'], " erased." + ENDC) state = device._poll_once() if not len(failed_nodes): @@ -286,17 +306,21 @@ def erase_flash(device, topic, nodes_to_program): # @param # @return binary size # ******************************************************************************* + + def loading_bar(loading_progress): period = 0.2 - while(1): + while (1): time.sleep(period) - print(u"\r ╰> loading : {} %".format(loading_progress.value), end='') + print(u"\r ╰> Loading : {} %".format(loading_progress.value), end='') # ******************************************************************************* # @brief send the binary file to the node # @param command type # @return None # ******************************************************************************* + + def send_binary_data(device, topic, nodes_to_program): loading_state = True failed_nodes = [] @@ -315,16 +339,16 @@ def send_binary_data(device, topic, nodes_to_program): # send each frame to the network file_offset = 0 for frame_index in range(nb_frames): - if (frame_index == (nb_frames-1)): + if (frame_index == (nb_frames - 1)): # last frame, compute size - frame_size = nb_bytes - (nb_frames-1)*NB_SAMPLE_BY_FRAME_MAX + frame_size = nb_bytes - (nb_frames - 1) * NB_SAMPLE_BY_FRAME_MAX else: frame_size = NB_SAMPLE_BY_FRAME_MAX # send the current frame loading_state, failed_nodes = send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_program) if not loading_state: - print(u"\r ╰> loading of node: ", failed_nodes, "FAILED %") + print(FAIL + u"\r ╰> Loading of node: ", failed_nodes, "FAILED %" + ENDC) for fail in failed_nodes: nodes_to_program.remove(fail) prev_fails.extend(failed_nodes) @@ -334,12 +358,12 @@ def send_binary_data(device, topic, nodes_to_program): # update loading progress loading_progress.value = math.trunc(frame_index / nb_frames * 100) if not len(nodes_to_program): - break; + break # kill the progress bar at the end of the loading loading_bar_bg.terminate() if loading_state: - print(u"\r ╰> loading : 100.0 %") + print(OKGREEN + u"\r ╰> Loading : 100.0 %" + ENDC) if len(prev_fails): loading_state = False return loading_state, prev_fails @@ -349,6 +373,8 @@ def send_binary_data(device, topic, nodes_to_program): # @param # @return None # ******************************************************************************* + + def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_program): return_value = True failed_nodes = [] @@ -360,7 +386,7 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog f.seek(file_offset) # read binary data data_bytes = f.read(1) - for sample in range(frame_size-1): + for sample in range(frame_size - 1): data_bytes = data_bytes + f.read(1) send_data(device, topic, BOOTLOADER_BIN_CHUNK, frame_size, data_bytes) @@ -370,13 +396,13 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog init_time = time.time() while len(failed_nodes): # check for timeout of nodes - if(time.time() - init_time > timeout): + if (time.time() - init_time > timeout): return_value = False break # check if it is a response message if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_BIN_CHUNK_RESP): + if (response['response'] == BOOTLOADER_BIN_CHUNK): # the node responsed, remove it for fails list if response['node'] in failed_nodes: timeout -= PROGRAM_TIMEOUT @@ -388,18 +414,18 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog # retry sending failed messages send_data_node(device, node, BOOTLOADER_BIN_CHUNK, frame_size, data_bytes) state = device._poll_once() - print(u"\r\n ╰> Retry sending binary message to node ", node) + print(u"\r\n ╰> Retry sending binary message to node ", node) init_time = time.time() while len(failed_nodes): # check for timeout of nodes - if(time.time() - init_time > PROGRAM_TIMEOUT): + if (time.time() - init_time > PROGRAM_TIMEOUT): return_value = False break # check if it is a response message if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_BIN_CHUNK_RESP): + if (response['response'] == BOOTLOADER_BIN_CHUNK): # the node responsed, remove it for fails list if response['node'] in failed_nodes: failed_nodes.remove(response['node']) @@ -416,6 +442,8 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog # @param # @return None # ******************************************************************************* + + def send_data(device, topic, command, size, data): # create a json file with the list of the nodes to program bootloader_cmd = { @@ -435,6 +463,8 @@ def send_data(device, topic, command, size, data): # @param # @return None # ******************************************************************************* + + def send_data_node(device, node, command, size, data): # create a json file with the list of the nodes to program bootloader_cmd = { @@ -454,6 +484,8 @@ def send_data_node(device, node, command, size, data): # @param # @return # ******************************************************************************* + + def send_binary_end(device, topic, nodes_to_program): return_value = True failed_nodes = [] @@ -467,14 +499,14 @@ def send_binary_end(device, topic, nodes_to_program): init_time = time.time() while len(failed_nodes) > 0: # check if we exit with timeout - if(time.time() - init_time > timeout): + if (time.time() - init_time > timeout): return_value = False break if 'bootloader' in state: for response in state['bootloader']: # check each node response - if (response['response'] == BOOTLOADER_BIN_END_RESP): - print(u" ╰> Node", response['node'], "acknowledge received, loading is complete.") + if (response['response'] == BOOTLOADER_BIN_END): + print(OKGREEN + u" ╰> Node", response['node'], "acknowledge received, loading is complete." + ENDC) # remove node from fails list if response['node'] in failed_nodes: timeout -= RESP_TIMEOUT @@ -484,19 +516,19 @@ def send_binary_end(device, topic, nodes_to_program): for node in failed_nodes: # retry sending failed messages send_node_command(device, node, topic, BOOTLOADER_BIN_END) - print(u"\r\n ╰> Retry sending end message to node ", node) + print(u"\r\n ╰> Retry sending end message to node ", node) state = device._poll_once() init_time = time.time() while len(failed_nodes): # check for timeout of nodes - if(time.time() - init_time > RESP_TIMEOUT): + if (time.time() - init_time > RESP_TIMEOUT): return_value = False break # check if it is a response message if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_BIN_END_RESP): + if (response['response'] == BOOTLOADER_BIN_END): # the node responsed, remove it for fails list if response['node'] in failed_nodes: failed_nodes.remove(response['node']) @@ -513,6 +545,8 @@ def send_binary_end(device, topic, nodes_to_program): # @param # @return None # ******************************************************************************* + + def compute_crc(): # create crc8 function object hash = crc8.crc8() @@ -533,6 +567,8 @@ def compute_crc(): # @param # @return # ******************************************************************************* + + def check_crc(device, topic, nodes_to_program): return_value = True failed_nodes = [] @@ -540,65 +576,65 @@ def check_crc(device, topic, nodes_to_program): timeout = RESP_TIMEOUT * len(nodes_to_program) # send crc command - send_topic_command(device, topic, BOOTLOADER_CRC_TEST) + send_topic_command(device, topic, BOOTLOADER_CRC) state = device._poll_once() # wait bin_end response init_time = time.time() while len(failed_nodes): # check for timeout exit - if(time.time() - init_time > timeout): + if (time.time() - init_time > timeout): return_value = False break # check the response if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_CRC_RESP): + if (response['response'] == BOOTLOADER_CRC): source_crc = int.from_bytes(compute_crc(), byteorder='big') node_crc = response['crc_value'] node_id = response['node'] - # crc properly received + # crc properly received if (source_crc == node_crc): - print(u" ╰> CRC test for node", node_id, " : OK.") + print(OKGREEN + u" ╰> CRC test for node", node_id, " : OK." + ENDC) if node_id in failed_nodes: timeout -= RESP_TIMEOUT failed_nodes.remove(node_id) else: # not a good crc - print(u" ╰> CRC test for node", node_id, ": NOK.") - print(u" ╰> waited :", hex(source_crc), ", received :", hex(node_crc)) + print(FAIL + u" ╰> CRC test for node", node_id, ": NOK." + ENDC) + print(FAIL + u" ╰> waited :", hex(source_crc), ", received :", hex(node_crc) + ENDC) return_value = False state = device._poll_once() for node in failed_nodes: # retry sending failed messages - send_node_command(device, node, topic, BOOTLOADER_CRC_TEST) - print(u"\r\n ╰> Retry sending crc demand to node ", node) + send_node_command(device, node, topic, BOOTLOADER_CRC) + print(u"\r\n ╰> Retry sending crc request to node ", node) state = device._poll_once() init_time = time.time() while len(failed_nodes): # check for timeout of nodes - if(time.time() - init_time > RESP_TIMEOUT): + if (time.time() - init_time > RESP_TIMEOUT): return_value = False break # check if it is a response message if 'bootloader' in state: for response in state['bootloader']: - if (response['response'] == BOOTLOADER_CRC_RESP): + if (response['response'] == BOOTLOADER_CRC): source_crc = int.from_bytes(compute_crc(), byteorder='big') node_crc = response['crc_value'] node_id = response['node'] - # crc properly received + # crc properly received if (source_crc == node_crc): - print(u" ╰> CRC test for node", node_id, " : OK.") + print(OKGREEN + u" ╰> CRC test for node", node_id, " : OK." + ENDC) if node_id in failed_nodes: timeout -= RESP_TIMEOUT failed_nodes.remove(node_id) else: # not a good crc - print(u" ╰> CRC test for node", node_id, ": NOK.") - print(u" ╰> waited :", hex(source_crc), ", received :", hex(node_crc)) + print(FAIL + u" ╰> CRC test for node", node_id, ": NOK." + ENDC) + print(FAIL + u" ╰> waited :", hex(source_crc), ", received :", hex(node_crc) + ENDC) return_value = False state = device._poll_once() @@ -612,22 +648,27 @@ def check_crc(device, topic, nodes_to_program): # @param # @return # ******************************************************************************* + + def reboot_network(device, topic, nodes_to_reboot): for node in nodes_to_reboot: send_node_command(device, node, topic, BOOTLOADER_STOP) # delay to let gate send commands time.sleep(0.01) + # ******************************************************************************* # @brief command used to flash luos nodes # @param flash function arguments : -g, -t, -b # @return None # ******************************************************************************* + + def luos_flash(args): topic = 1 if not (args.port): try: - args.port= serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] + args.port = serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] except: print('Please specify a port to access the network.') return BOOTLOADER_FLASH_PORT_ERROR @@ -651,7 +692,7 @@ def luos_flash(args): try: f = open(FILEPATH, mode="rb") except IOError: - print("Cannot open :", FILEPATH) + print(FAIL + "Cannot open :", FILEPATH + ENDC) return BOOTLOADER_FLASH_BINARY_ERROR else: f.close() @@ -669,11 +710,11 @@ def luos_flash(args): # check if we have available node to program if not nodes_to_program: - print("No target found :\n" + str(device.nodes)) + print(FAIL + "No target found :\n" + str(device.nodes) + ENDC) return BOOTLOADER_DETECT_ERROR # reboot all nodes in bootloader mode - print("** Reboot all nodes in bootloader mode **") + print("\n** Reboot all nodes in bootloader mode **") for node in nodes_to_reboot: send_node_command(device, node, topic, BOOTLOADER_START) # delay to let gate send commands @@ -695,54 +736,54 @@ def luos_flash(args): # go to header state if node is ready for node in nodes_to_program: - print("--> Check if node", node, " is ready.") + print("─> Check if node", node, " is ready.") machine_state = send_ready_cmd(device, node, topic) if not machine_state: total_fails.append(node) machine_state = True - print("Node ", node, "failed to load!") + print(FAIL + "Node ", node, "failed to update!" + ENDC) time.sleep(0.01) for node in total_fails: nodes_to_program.remove(node) # erase node flash memory - print("--> Erase flash memory.") + print("─> Erase flash memory.") machine_state, failed_nodes = erase_flash(device, topic, nodes_to_program) if not machine_state: for fail in failed_nodes: nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print("Erase flash of node: ", failed_nodes, "failed!") + print(FAIL + "Node ", failed_nodes, " flash erasing failed!" + ENDC) # send binary data - print("--> Send binary data.") + print("─> Send binary data.") machine_state, failed_nodes = send_binary_data(device, topic, nodes_to_program) if not machine_state: total_fails.extend(failed_nodes) machine_state = True - print("Flash of node: ", failed_nodes, "failed!") + print(FAIL + "Node ", failed_nodes, "update failed!" + ENDC) # inform the node of the end of the loading - print("--> Programmation finished, waiting for acknowledge.") + print("─> Programmation finished, waiting for acknowledge.") machine_state, failed_nodes = send_binary_end(device, topic, nodes_to_program) if not machine_state: for fail in failed_nodes: nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print("ACK of node: ", failed_nodes, "failed!") + print(FAIL + "Node ", failed_nodes, "ACK failed!" + ENDC) # Ask the node to send binary crc - print("--> Check binary CRC.") + print("─> Check binary CRC.") machine_state, failed_nodes = check_crc(device, topic, nodes_to_program) if not machine_state: for fail in failed_nodes: nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print("ACK of node: ", failed_nodes, "failed!") + print(FAIL + "Node ", failed_nodes, "ACK failed!" + ENDC) # Say to the bootloader that the integrity # of the app saved in flash has been verified @@ -752,13 +793,13 @@ def luos_flash(args): time.sleep(0.1) # reboot all nodes in application mode if len(total_fails) == 0: - print("** Reboot all nodes in application mode **") + print("\n** Reboot all nodes in application mode **") reboot_network(device, topic, nodes_to_reboot) device.close() return BOOTLOADER_SUCCESS else: device.close() - print("Load of nodes: ", total_fails, " failed, please reboot and retry.") + print(FAIL + "Nodes ", total_fails, " update failed, please reboot and retry." + ENDC) return BOOTLOADER_FLASH_ERROR # ******************************************************************************* @@ -766,10 +807,12 @@ def luos_flash(args): # @param detect function arguments : -p # @return None # ******************************************************************************* + + def luos_detect(args): if not (args.port): try: - args.port= serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] + args.port = serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] except: print('Please specify a port to access the network.') return BOOTLOADER_DETECT_ERROR @@ -792,10 +835,12 @@ def luos_detect(args): # @param detect function arguments : -p # @return None # ******************************************************************************* + + def luos_reset(args): if not (args.port): try: - args.port= serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] + args.port = serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] except: return BOOTLOADER_DETECT_ERROR @@ -833,6 +878,8 @@ def luos_reset(args): # @param detect function arguments : -p # @return None # ******************************************************************************* + + def luos_options(): parser = argparse.ArgumentParser( description='Luos command line interface') @@ -863,7 +910,7 @@ def luos_options(): detect_parser = subparsers.add_parser('detect', help='tool to detect luos network') detect_parser.add_argument('port', help='port used to detect network', - nargs='?') + nargs='?') detect_parser.add_argument('--baudrate', help='Choose pyluos serial baudrate default value = 1000000', default=1000000) @@ -873,7 +920,7 @@ def luos_options(): rescue_parser = subparsers.add_parser('reset', help='tool to reset one or multiple blocked nodes in rescue mode') rescue_parser.add_argument('port', help='port used to access to the network', - nargs='?') + nargs='?') rescue_parser.add_argument('--baudrate', help='Choose pyluos serial baudrate default value = 1000000', default=1000000) @@ -886,6 +933,8 @@ def luos_options(): # @param None # @return None # ******************************************************************************* + + def main(): # declare options of the CLI parser = luos_options() @@ -896,5 +945,6 @@ def main(): # execute CLI subcommand return args.func(args) + if __name__ == '__main__': sys.exit(main()) From be521ba61597acd92af94675cdb6bac0a527b8d1 Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Fri, 25 Aug 2023 12:58:41 +0200 Subject: [PATCH 04/10] Remove telemetry --- README.md | 8 ------- pyluos/device.py | 60 ++++++++++++++++-------------------------------- 2 files changed, 20 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 25df758..d832739 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,3 @@ Luos proposes organized and effective development practices, guaranteeing develo * → Try on your own with the [get started](https://www.luos.io/tutorials/get-started) * → Consult the full [documentation](https://www.luos.io/docs) - -## Disclaimer -This library send some anonymous information to Luos allowing to improve Pyluos experience. -To disable the telemetry please add `telemetry=False` parameter at Device creation. -For example: -```python -device = Device('/dev/cu.usbserial-DN2EUDGP', telemetry=False) -``` diff --git a/pyluos/device.py b/pyluos/device.py index 197875a..b641a56 100644 --- a/pyluos/device.py +++ b/pyluos/device.py @@ -22,6 +22,7 @@ def run_from_unittest(): return 'unittest' in sys.services + class contList(list): def __repr__(self): s = '-------------------------------------------------\n' @@ -31,6 +32,7 @@ def __repr__(self): s += '{:<20s}{:<20s}{:<5d}\n'.format(elem.type, elem.alias, elem.id) return s + class nodeList(list): def __repr__(self): # Display the topology @@ -46,18 +48,18 @@ def __repr__(self): # Draw the node body prefill = (prefill[:len(fill)]) if len(prefill) > len(fill) else prefill - s +='{:<{fillsize}s}'.format(prefill, fillsize=len(fill)) + s += '{:<{fillsize}s}'.format(prefill, fillsize=len(fill)) if (prechild == True): - s = s[:-4] + '║' + s[-4+1:] + s = s[:-4] + '║' + s[-4 + 1:] s += '{:<54s}'.format(" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n") tmpstr = '{:<52s}'.format("%s╭────────────────── Node %s ──────────────────" % (branch, node.id)) - if (len(pre)>0): + if (len(pre) > 0): pre = pre[:-1] + "═" s += pre + tmpstr + '{:>3s}'.format("┃\n") s += fill + " ┃ │ " + '{:<20s}{:<20s}{:<4s}'.format("Type", "Alias", "ID") + '{:>3s}'.format("┃\n") - for y,elem in enumerate(node.services): - if (y == (len(node.services)-1)): + for y, elem in enumerate(node.services): + if (y == (len(node.services) - 1)): s += fill + " ┃ ╰> " + '{:<20s}{:<20s}{:<4d}'.format(elem.type, elem.alias, elem.id) + '{:>3s}'.format("┃\n") else: s += fill + " ┃ ├> " + '{:<20s}{:<20s}{:<4d}'.format(elem.type, elem.alias, elem.id) + '{:>3s}'.format("┃\n") @@ -85,7 +87,6 @@ def __init__(self, host, log_conf=_base_log_conf, test_mode=False, background_task=True, - telemetry=True, *args, **kwargs): if IO is not None: self._io = IO(host=host, *args, **kwargs) @@ -98,7 +99,6 @@ def __init__(self, host, config = json.load(f) logging.config.dictConfig(config) - self.telemetry = telemetry self.logger = logging.getLogger(__name__) self.logger.info('Connected to "{}".'.format(host)) @@ -113,7 +113,7 @@ def __init__(self, host, self._running = True self._pause = False - if(background_task == True): + if (background_task == True): # Setup both poll/push synchronization loops. self._poll_bg = threading.Thread(target=self._poll_and_up) self._poll_bg.daemon = True @@ -155,8 +155,8 @@ def _setup(self): self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision of pyluos.\n Please consider updating Luos on your boards") return state = self._poll_once() - if (time.time()-startTime > 1): - retry = retry +1 + if (time.time() - startTime > 1): + retry = retry + 1 if retry > 5: # detection is not working sys.exit("Detection failed.") @@ -175,12 +175,12 @@ def _setup(self): for elem in self._nodes: if (elem.id == parent_id): parent_elem = elem - break; + break # create the node self._nodes.append(AnyNode(id=node["node_id"], parent=parent_elem, connection=node["con"])) filtered_services = contList([mod for mod in node["services"] - if 'type' in mod and mod['type'] in name2mod.keys()]) + if 'type' in mod and mod['type'] in name2mod.keys()]) # Create a list of services in the node self._nodes[i].services = [ name2mod[mod['type']](id=mod['id'], @@ -198,25 +198,6 @@ def _setup(self): self._cmd_data = [] self._binary = [] - if (self.telemetry == True): - from pyluos.version import version - self.logger.info('Sending telemetry...') - luos_telemetry = {"telemetry_type": "pyluos", - "mac": hex(uuid.getnode()), - "system": sys.platform, - "unix_time": int(time.time()), - "pyluos_rev": version, - "routing_table":state['routing_table']} - try: - r = requests.post("https://monorepo-services.vercel.app/api/telemetry", - data=luos_telemetry) - if not r: - print("Telemetry request failed : error " + str(r.status_code)) - except: - print("Telemetry request failed.") - else: - self.logger.info("Telemetry disabled, please consider enabling it by removing the 'telemetry=False' argument of your Device creation.") - # We push our current state to make sure that # both our model and the hardware are synced. self._push_once() @@ -239,30 +220,30 @@ def _poll_once(self): def _poll_and_up(self): while self._running: - if not self._pause : + if not self._pause: state = self._poll_once() if self._state != []: self._update(state) self._push_once() - else : + else: time.sleep(0.1) # Update our model with the new state. def _update(self, new_state): - if 'dead_service' in new_state.keys() : - #we have lost a service put a flag on this service + if 'dead_service' in new_state.keys(): + # we have lost a service put a flag on this service alias = new_state['dead_service'] if hasattr(self, alias): getattr(self, alias)._kill() if (self._freedomLink != None): self._freedomLink._kill(alias) - if 'assert' in new_state.keys() : + if 'assert' in new_state.keys(): # A node assert, print assert informations if (('node_id' in new_state['assert']) and ('file' in new_state['assert']) and ('line' in new_state['assert'])): s = "************************* ASSERT *************************\n" s += "* Node " + str(new_state['assert']['node_id']) + " assert in file " + new_state['assert']['file'] + " line " + str(new_state['assert']['line']) s += "\n**********************************************************" - print (s) + print(s) if (self._freedomLink != None): self._freedomLink._assert(alias) if 'services' not in new_state.keys(): @@ -288,16 +269,15 @@ def update_data(self, alias, key, val, data): def _push_once(self): with self._cmd_lock: if self._cmd: - self._write( json.dumps({'services': self._cmd}).encode()) + self._write(json.dumps({'services': self._cmd}).encode()) self._cmd = defaultdict(lambda: defaultdict(lambda: None)) for cmd, binary in zip(self._cmd_data, self._binary): time.sleep(0.01) - self._write( json.dumps({'services': cmd}).encode() + '\n'.encode() + binary) + self._write(json.dumps({'services': cmd}).encode() + '\n'.encode() + binary) self._cmd_data = [] self._binary = [] - def _send(self, msg): with self._send_lock: self._io.send(msg) From c3a5e3dba4ab0313606ea523a955d245abe10d47 Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Thu, 28 Sep 2023 10:24:47 +0200 Subject: [PATCH 05/10] Improve bootloader display and reduce detection number. --- pyluos/device.py | 2 ++ pyluos/tools/bootloader.py | 62 +++++++++++++++++++++++--------------- pyluos/tools/discover.py | 17 +++++++---- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/pyluos/device.py b/pyluos/device.py index b641a56..f897d11 100644 --- a/pyluos/device.py +++ b/pyluos/device.py @@ -162,6 +162,8 @@ def _setup(self): sys.exit("Detection failed.") self._send({'detection': {}}) startTime = time.time() + # Save routing table data + self._routing_table = state # Create nodes self._services = [] self._nodes = [] diff --git a/pyluos/tools/bootloader.py b/pyluos/tools/bootloader.py index 21845bc..e3d3157 100644 --- a/pyluos/tools/bootloader.py +++ b/pyluos/tools/bootloader.py @@ -37,6 +37,8 @@ OKGREEN = '\r\033[92m' FAIL = '\r\033[91m' ENDC = '\033[0m' +UNDERLINE = '\r\033[4m' +BOLD = '\r\033[1m' FILEPATH = None NB_SAMPLE_BY_FRAME_MAX = 127 @@ -262,6 +264,7 @@ def erase_flash(device, topic, nodes_to_program): # timeout for exiting loop in case of fails if (time.time() - init_time > timeout): return_value = False + print(FAIL + u"\r ╰> Erase flash of node ", failed_nodes, "FAILED %" + ENDC) break # check if it is a response message if 'bootloader' in state: @@ -675,18 +678,18 @@ def luos_flash(args): baudrate = os.getenv('LUOS_BAUDRATE', args.baudrate) - print('Luos flash subcommand with parameters :') + print("\n" + UNDERLINE + "Luos flash subcommand with parameters:" + ENDC) print('\t--baudrate : ', baudrate) print('\t--gate : ', args.gate) print('\t--target : ', args.target) print('\t--binary : ', args.binary) print('\t--port : ', args.port) - # state used to check each step + # State used to check each step machine_state = True - # list of all the nodes that may fail in each step + # List of all the nodes that may fail in each step total_fails = [] - # update firmware path + # Update firmware path global FILEPATH FILEPATH = args.binary try: @@ -697,57 +700,62 @@ def luos_flash(args): else: f.close() - # init device + # Init device device = Device(args.port, baudrate=baudrate, background_task=False) - # find routing table - state = find_network(device) + # Get routing table JSON + state = device._routing_table if state is None: return BOOTLOADER_DETECT_ERROR - # searching nodes to program in network + # Searching nodes to program in network (nodes_to_reboot, nodes_to_program) = create_target_list(args, state) - # check if we have available node to program + # Check if we have available node to program if not nodes_to_program: print(FAIL + "No target found :\n" + str(device.nodes) + ENDC) return BOOTLOADER_DETECT_ERROR - # reboot all nodes in bootloader mode - print("\n** Reboot all nodes in bootloader mode **") + # Reboot all nodes in bootloader mode + print("\n" + BOLD + "Reboot all nodes in bootloader mode:" + ENDC) for node in nodes_to_reboot: send_node_command(device, node, topic, BOOTLOADER_START) - # delay to let gate send commands + # Delay to let the gate send the commands time.sleep(0.01) - # wait before next step + # Delay to let the gate send the last command time.sleep(0.1) - # find routing table in boot mode - # its necessary to give ids to bootloader services + # Find bootloaders in the routing table state = find_network(device) if state is None: + print(FAIL + " ╰> Reboot in bootloader mode failed." + ENDC) return BOOTLOADER_DETECT_ERROR + else: + print(OKGREEN + " ╰> Reboot in bootloader mode succeed." + ENDC) - # wait before next step + # Wait before the next step time.sleep(0.4) - print("\n** Programming nodes **") - - # go to header state if node is ready + print("\n" + BOLD + "Programming nodes:" + ENDC) + # Go to header state if node is ready for node in nodes_to_program: print("─> Check if node", node, " is ready.") machine_state = send_ready_cmd(device, node, topic) if not machine_state: total_fails.append(node) machine_state = True - print(FAIL + "Node ", node, "failed to update!" + ENDC) + print(FAIL + " ╰> Node ", node, "programming failed." + ENDC) time.sleep(0.01) for node in total_fails: nodes_to_program.remove(node) - # erase node flash memory + if not len(nodes_to_program): + print(BOLD + FAIL + "Programming failed on all targets." + ENDC) + return BOOTLOADER_FLASH_ERROR + + # Erase node flash memory print("─> Erase flash memory.") machine_state, failed_nodes = erase_flash(device, topic, nodes_to_program) if not machine_state: @@ -755,7 +763,11 @@ def luos_flash(args): nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print(FAIL + "Node ", failed_nodes, " flash erasing failed!" + ENDC) + print(FAIL + " ╰> Node ", failed_nodes, " flash erasing failed!" + ENDC) + + if not len(nodes_to_program): + print(BOLD + FAIL + "Programming failed on all targets." + ENDC) + return BOOTLOADER_FLASH_ERROR # send binary data print("─> Send binary data.") @@ -763,7 +775,7 @@ def luos_flash(args): if not machine_state: total_fails.extend(failed_nodes) machine_state = True - print(FAIL + "Node ", failed_nodes, "update failed!" + ENDC) + print(FAIL + "Node ", failed_nodes, "programming failed." + ENDC) # inform the node of the end of the loading print("─> Programmation finished, waiting for acknowledge.") @@ -793,13 +805,13 @@ def luos_flash(args): time.sleep(0.1) # reboot all nodes in application mode if len(total_fails) == 0: - print("\n** Reboot all nodes in application mode **") + print(OKGREEN + BOLD + "Programming succeed." + ENDC) reboot_network(device, topic, nodes_to_reboot) device.close() return BOOTLOADER_SUCCESS else: device.close() - print(FAIL + "Nodes ", total_fails, " update failed, please reboot and retry." + ENDC) + print(FAIL + "Nodes ", total_fails, " programming failed, please reboot and retry." + ENDC) return BOOTLOADER_FLASH_ERROR # ******************************************************************************* diff --git a/pyluos/tools/discover.py b/pyluos/tools/discover.py index 19b7dd1..604e859 100644 --- a/pyluos/tools/discover.py +++ b/pyluos/tools/discover.py @@ -5,16 +5,21 @@ import serial import struct import argparse +OKGREEN = '\r\033[92m' +FAIL = '\r\033[91m' +ENDC = '\033[0m' + def serial_ports(): return Serial.available_hosts() + def serial_discover(baudrate=1000000): serial_hosts = serial_ports() available_serial = [] - print("Searching for a gate available") + print("\n\033[4mSearching for available Gates:\033[0m") for serial_host in serial_hosts: - print("Search a Gate on port " + str(serial_host)) + print("\t- Search a Gate on port " + str(serial_host)) try: port = serial.Serial(serial_host, baudrate, timeout=0.2) time.sleep(0.1) @@ -46,10 +51,10 @@ def serial_discover(baudrate=1000000): port.reset_output_buffer() port.close() - if available_serial : + if available_serial: return available_serial else: - print("... No gate detected") + print(FAIL + "... No gate detected" + ENDC) return [] def main(): @@ -63,9 +68,9 @@ def main(): gate_list = serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate)) if gate_list: - print("Available Luos gate on port : " + str(gate_list)) + print(OKGREEN + "Available Luos gate on port : " + str(gate_list) + ENDC) else: - print("No gate detected") + print(FAIL + "No gate detected" + ENDC) if __name__ == '__main__': From e2e6fe170548ddf9df19216db3880fb9c4f69099 Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Mon, 9 Oct 2023 17:25:17 +0200 Subject: [PATCH 06/10] Improve `refresh_freq` measurement and add a `max_refresh_time` measurement --- pyluos/services/service.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/pyluos/services/service.py b/pyluos/services/service.py index 3958d31..442e9d2 100644 --- a/pyluos/services/service.py +++ b/pyluos/services/service.py @@ -21,6 +21,7 @@ def widgets(*args, **kwargs): READ_TIMEOUT = 0.3 + class Service(object): possible_events = set() @@ -31,6 +32,7 @@ def __init__(self, self.type = type self.alias = alias self.refresh_freq = 0.0 + self.max_refresh_time = 0.0 self._update_time = 0.01 self._delegate = device self._value = None @@ -42,7 +44,7 @@ def __init__(self, self._luos_revision = "Unknown" self._robus_revision = "Unknown" self._killed = False - self._last_update = time.time() + self._last_update = [] self._luos_statistics = {} def __repr__(self): @@ -54,9 +56,14 @@ def _update(self, new_state): if not isinstance(new_state, dict): new_state = {new_state: ""} - if ((time.time() - self._last_update) != 0): - self.refresh_freq = ((200.0 * self.refresh_freq) + (1.0 / (time.time() - self._last_update))) / 201.0 - self._last_update = time.time() + self._last_update.append(time.time()) + if (len(self._last_update) > 1): + self.max_refresh_time = max(self.max_refresh_time, self._last_update[-1] - self._last_update[-2]) + if (self._last_update[0] < time.time() - 1.0): + while (self._last_update[0] < time.time() - 10.0): + self._last_update.pop(0) + self.refresh_freq = (len(self._last_update) / 10.0) * 0.05 + 0.95 * self.refresh_freq + if 'revision' in new_state.keys(): self._firmware_revision = new_state['revision'] if 'luos_revision' in new_state.keys(): @@ -67,21 +74,21 @@ def _update(self, new_state): def _kill(self): self._killed = True - print ("service", self.alias, "have been excluded from the network due to no responses.") + print("service", self.alias, "have been excluded from the network due to no responses.") def _push_value(self, key, new_val): - if (self._killed) : - print("service", self.alias,"is excluded.") - else : - if isinstance(new_val, float) : + if (self._killed): + print("service", self.alias, "is excluded.") + else: + if isinstance(new_val, float): self._delegate.update_cmd(self.alias, key, float(str("%.3f" % new_val))) - else : + else: self._delegate.update_cmd(self.alias, key, new_val) def _push_data(self, key, new_val, data): - if (self._killed) : - print("service", self.alias,"is excluded.") - else : + if (self._killed): + print("service", self.alias, "is excluded.") + else: self._delegate.update_data(self.alias, key, new_val, data) @property @@ -146,7 +153,7 @@ def update_time(self): @update_time.setter def update_time(self, time): self._push_value('update_time', time) - self._update_time= time + self._update_time = time # Events cb handling From 886dc7387068480da25c395fc3f6fa31fa01cdc8 Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Tue, 14 Nov 2023 15:11:49 +0100 Subject: [PATCH 07/10] increase detection timeout delay allowing big configuration to be detected. --- pyluos/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyluos/device.py b/pyluos/device.py index f897d11..2ca21f3 100644 --- a/pyluos/device.py +++ b/pyluos/device.py @@ -155,7 +155,7 @@ def _setup(self): self.logger.info("Watch out the Luos revision you are using on your board is too old to work with this revision of pyluos.\n Please consider updating Luos on your boards") return state = self._poll_once() - if (time.time() - startTime > 1): + if (time.time() - startTime > 5): retry = retry + 1 if retry > 5: # detection is not working From 2bdc53b3b314dfcf228dc270fa1a5e9f90671305 Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Wed, 22 Nov 2023 16:48:42 +0100 Subject: [PATCH 08/10] Improve bootloader look, readability, and reliabilty --- pyluos/io/ws.py | 3 +- pyluos/tools/bootloader.py | 317 +++++++++++++++++++++---------------- 2 files changed, 180 insertions(+), 140 deletions(-) diff --git a/pyluos/io/ws.py b/pyluos/io/ws.py index caa0e90..b1cd29f 100644 --- a/pyluos/io/ws.py +++ b/pyluos/io/ws.py @@ -12,6 +12,7 @@ from threading import Event, Thread from . import IOHandler + def resolve_hostname(hostname, port): # We do our own mDNS resolution # to enforce we only search for IPV4 address @@ -48,7 +49,7 @@ def __init__(self, host, port=9342, baudrate=None): host = resolve_hostname(host, port) self._ws = websocket.WebSocket() - self._ws.connect("ws://" + str(host) + ":" + str(port)+"/ws") + self._ws.connect("ws://" + str(host) + ":" + str(port) + "/ws") self._msg = queue.Queue(4096) self._running = True diff --git a/pyluos/tools/bootloader.py b/pyluos/tools/bootloader.py index e3d3157..efd3bdb 100644 --- a/pyluos/tools/bootloader.py +++ b/pyluos/tools/bootloader.py @@ -15,8 +15,7 @@ import math import crc8 import os -from ..io.serial_io import Serial -import serial +from ..io import io_from_host import struct # ******************************************************************************* @@ -45,7 +44,7 @@ RESP_TIMEOUT = 3 ERASE_TIMEOUT = 10 -PROGRAM_TIMEOUT = 5 +PROGRAM_TIMEOUT = 2 BOOTLOADER_SUCCESS = 0 BOOTLOADER_DETECT_ERROR = 1 @@ -56,34 +55,6 @@ # Function # ******************************************************************************* -# ******************************************************************************* -# @brief find routing table -# @param port connected to the luos gate -# @return an object containing routing table -# ******************************************************************************* - - -def find_network(device): - device._send({'detection': {}}) - startTime = time.time() - state = device._poll_once() - retry = 0 - while ('routing_table' not in state): - if ('route_table' in state): - print(FAIL + 'version of luos not supported' + ENDC) - return None - state = device._poll_once() - if (time.time() - startTime > 1): - retry = retry + 1 - if retry > 5: - # detection is not working - print(FAIL + 'Detection failed.' + ENDC) - return None - device._send({'detection': {}}) - startTime = time.time() - - return state - # ******************************************************************************* # @brief find nodes to program in the network # @param target list, routing table @@ -94,7 +65,6 @@ def find_network(device): def create_target_list(args, state): bypass_node = False nodes_to_program = [] - nodes_to_reboot = [] for node in state['routing_table']: # prevent programmation of node 1 bypass_node = False @@ -102,7 +72,6 @@ def create_target_list(args, state): bypass_node = True # check if node is in target list if not (bypass_node): - nodes_to_reboot.append(node['node_id']) for target in args.target: if (int(node['node_id']) == int(target)): nodes_to_program.append(node['node_id']) @@ -112,11 +81,11 @@ def create_target_list(args, state): errorList.append(target) if (len(errorList) > 1): - print("\n** Nodes " + ' '.join(errorList) + " are not available and will be ignored. **") + print(BOLD + FAIL + u"\nNodes " + ' '.join(errorList) + " are not available and will be ignored." + ENDC) elif (len(errorList) == 1): - print("** Node " + errorList[0] + " is not available and will be ignored. **") + print(BOLD + FAIL + u"** Node " + errorList[0] + " is not available and will be ignored. **" + ENDC) - return (nodes_to_reboot, nodes_to_program) + return (nodes_to_program) # ******************************************************************************* # @brief send commands @@ -131,6 +100,7 @@ def send_topic_command(device, topic, command, size=0): 'bootloader': { 'command': { 'type': command, + 'node': 0, 'topic': topic, 'size': size }, @@ -176,7 +146,7 @@ def get_binary_size(): # ******************************************************************************* -def send_ready_cmd(device, node, topic): +def send_ready_cmd(device, node, topic, verbose): return_value = True # send ready command to the node send_node_command(device, node, topic, BOOTLOADER_READY, get_binary_size()) @@ -191,7 +161,8 @@ def send_ready_cmd(device, node, topic): # don't load binary if there is not enough place in flash memory return_value = False else: - print(OKGREEN + u" ╰> Node n°", response['node'], "is ready." + ENDC) + if verbose: + print(OKGREEN + u" ╰> Node n°", response['node'], "is ready." + ENDC) return_value = True break @@ -208,32 +179,13 @@ def send_ready_cmd(device, node, topic): def waiting_erase(): - count = 0 - period = 0.4 - print("\r ", end='') - print(u"\r ╰> Erase flash ", end='') + period = 0.1 + chars = "/—\|" while (1): - time.sleep(period) - if (count == 0): - print("\r ", end='') - print(u"\r ╰> Erase flash . ", end='') - count += 1 - continue - if (count == 1): - print("\r ", end='') - print(u"\r ╰> Erase flash .. ", end='') - count += 1 - continue - if (count == 2): - print("\r ", end='') - print(u"\r ╰> Erase flash ... ", end='') - count += 1 - continue - if (count == 3): - print("\r ", end='') - print(u"\r ╰> Erase flash ", end='') - count = 0 - continue + for char in chars: + time.sleep(period) + print(u"\r ╰> Erase flash " + char , end='') + # ******************************************************************************* # @brief send erase command @@ -242,7 +194,7 @@ def waiting_erase(): # ******************************************************************************* -def erase_flash(device, topic, nodes_to_program): +def erase_flash(device, topic, nodes_to_program, verbose): return_value = True failed_nodes = [] failed_nodes.extend(nodes_to_program) @@ -264,7 +216,7 @@ def erase_flash(device, topic, nodes_to_program): # timeout for exiting loop in case of fails if (time.time() - init_time > timeout): return_value = False - print(FAIL + u"\r ╰> Erase flash of node ", failed_nodes, "FAILED %" + ENDC) + print(FAIL + u"\r ╰> Erase flash of node", failed_nodes, "FAILED %" + ENDC) break # check if it is a response message if 'bootloader' in state: @@ -274,13 +226,14 @@ def erase_flash(device, topic, nodes_to_program): if response['node'] in failed_nodes: timeout -= ERASE_TIMEOUT failed_nodes.remove(response['node']) - print(OKGREEN + u"\r ╰> Flash memory of node ", response['node'], " erased." + ENDC) + if verbose: + print(OKGREEN + u"\r ╰> Flash memory of node", response['node'], "erased." + ENDC) state = device._poll_once() # retry sending failed messages for node in failed_nodes: send_node_command(device, node, topic, BOOTLOADER_ERASE) - print(u"\r\n ╰> Retry erase memory of node ", node) + print(u"\r\n ╰> Retry erase memory of node", node) init_time = time.time() state = device._poll_once() while len(failed_nodes): @@ -295,13 +248,15 @@ def erase_flash(device, topic, nodes_to_program): # this node responded, delete it from the failed nodes list if response['node'] in failed_nodes: failed_nodes.remove(response['node']) - print(OKGREEN + u"\r ╰> Flash memory of node ", response['node'], " erased." + ENDC) + if verbose: + print(OKGREEN + u"\r ╰> Flash memory of node", response['node'], "erased." + ENDC) state = device._poll_once() + waiting_bg.terminate() if not len(failed_nodes): return_value = True + print(OKGREEN + u"\r ╰> All flash erased" + ENDC) - waiting_bg.terminate() return return_value, failed_nodes # ******************************************************************************* @@ -312,10 +267,12 @@ def erase_flash(device, topic, nodes_to_program): def loading_bar(loading_progress): - period = 0.2 + period = 0.1 + chars = "/—\|" while (1): - time.sleep(period) - print(u"\r ╰> Loading : {} %".format(loading_progress.value), end='') + for char in chars: + time.sleep(period) + print(u"\r ╰> Loading : " + char + " {:.2f} %".format(loading_progress.value), end='') # ******************************************************************************* # @brief send the binary file to the node @@ -351,22 +308,23 @@ def send_binary_data(device, topic, nodes_to_program): # send the current frame loading_state, failed_nodes = send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_program) if not loading_state: - print(FAIL + u"\r ╰> Loading of node: ", failed_nodes, "FAILED %" + ENDC) + print(FAIL + u"\r ╰> Loading of node", failed_nodes, "FAILED" + ENDC) for fail in failed_nodes: nodes_to_program.remove(fail) prev_fails.extend(failed_nodes) loading_state = True + if not len(nodes_to_program): + loading_state = False + break # update cursor position in the binary file file_offset += frame_size # update loading progress - loading_progress.value = math.trunc(frame_index / nb_frames * 100) - if not len(nodes_to_program): - break + loading_progress.value = frame_index / nb_frames * 100 # kill the progress bar at the end of the loading loading_bar_bg.terminate() if loading_state: - print(OKGREEN + u"\r ╰> Loading : 100.0 %" + ENDC) + print(OKGREEN + u"\r ╰> Loading : 100.0 % " + ENDC) if len(prev_fails): loading_state = False return loading_state, prev_fails @@ -382,7 +340,6 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog return_value = True failed_nodes = [] failed_nodes.extend(nodes_to_program) - timeout = PROGRAM_TIMEOUT * len(nodes_to_program) with open(FILEPATH, mode="rb") as f: # put the cursor at the beginning of the file @@ -391,15 +348,15 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog data_bytes = f.read(1) for sample in range(frame_size - 1): data_bytes = data_bytes + f.read(1) - send_data(device, topic, BOOTLOADER_BIN_CHUNK, frame_size, data_bytes) # pull serial data state = device._poll_once() + # wait nodes response init_time = time.time() while len(failed_nodes): # check for timeout of nodes - if (time.time() - init_time > timeout): + if (time.time() - init_time > PROGRAM_TIMEOUT): return_value = False break # check if it is a response message @@ -408,8 +365,8 @@ def send_frame_from_binary(device, topic, frame_size, file_offset, nodes_to_prog if (response['response'] == BOOTLOADER_BIN_CHUNK): # the node responsed, remove it for fails list if response['node'] in failed_nodes: - timeout -= PROGRAM_TIMEOUT failed_nodes.remove(response['node']) + time.sleep(0.001) # wait for next message state = device._poll_once() @@ -455,6 +412,7 @@ def send_data(device, topic, command, size, data): 'size': [size], 'type': command, 'topic': topic, + 'node': 0, }, } } @@ -475,6 +433,7 @@ def send_data_node(device, node, command, size, data): 'command': { 'size': [size], 'type': command, + 'topic': 1, 'node': node, }, } @@ -489,7 +448,7 @@ def send_data_node(device, node, command, size, data): # ******************************************************************************* -def send_binary_end(device, topic, nodes_to_program): +def send_binary_end(device, topic, nodes_to_program, verbose): return_value = True failed_nodes = [] failed_nodes.extend(nodes_to_program) @@ -509,7 +468,8 @@ def send_binary_end(device, topic, nodes_to_program): for response in state['bootloader']: # check each node response if (response['response'] == BOOTLOADER_BIN_END): - print(OKGREEN + u" ╰> Node", response['node'], "acknowledge received, loading is complete." + ENDC) + if verbose: + print(OKGREEN + u" ╰> Node", response['node'], "acknowledge received, loading is complete." + ENDC) # remove node from fails list if response['node'] in failed_nodes: timeout -= RESP_TIMEOUT @@ -519,7 +479,8 @@ def send_binary_end(device, topic, nodes_to_program): for node in failed_nodes: # retry sending failed messages send_node_command(device, node, topic, BOOTLOADER_BIN_END) - print(u"\r\n ╰> Retry sending end message to node ", node) + if verbose: + print(u"\r\n ╰> Retry sending end message to node ", node) state = device._poll_once() init_time = time.time() while len(failed_nodes): @@ -572,11 +533,9 @@ def compute_crc(): # ******************************************************************************* -def check_crc(device, topic, nodes_to_program): +def check_crc(device, topic, nodes_to_program, verbose): return_value = True - failed_nodes = [] - failed_nodes.extend(nodes_to_program) - timeout = RESP_TIMEOUT * len(nodes_to_program) + failed_nodes = nodes_to_program.copy() # send crc command send_topic_command(device, topic, BOOTLOADER_CRC) @@ -586,7 +545,7 @@ def check_crc(device, topic, nodes_to_program): init_time = time.time() while len(failed_nodes): # check for timeout exit - if (time.time() - init_time > timeout): + if (time.time() - init_time > RESP_TIMEOUT): return_value = False break # check the response @@ -598,9 +557,9 @@ def check_crc(device, topic, nodes_to_program): node_id = response['node'] # crc properly received if (source_crc == node_crc): - print(OKGREEN + u" ╰> CRC test for node", node_id, " : OK." + ENDC) + if verbose: + print(OKGREEN + u" ╰> CRC test for node", node_id, ": OK." + ENDC) if node_id in failed_nodes: - timeout -= RESP_TIMEOUT failed_nodes.remove(node_id) else: # not a good crc @@ -653,9 +612,11 @@ def check_crc(device, topic, nodes_to_program): # ******************************************************************************* -def reboot_network(device, topic, nodes_to_reboot): - for node in nodes_to_reboot: +def reboot_network(device, topic, nodes_to_program, verbose): + for node in nodes_to_program: send_node_command(device, node, topic, BOOTLOADER_STOP) + if verbose: + print(OKGREEN + u" ╰> Node", node, ": rebooted." + ENDC) # delay to let gate send commands time.sleep(0.01) @@ -668,7 +629,7 @@ def reboot_network(device, topic, nodes_to_reboot): def luos_flash(args): topic = 1 - + begin_date = time.time() if not (args.port): try: args.port = serial_discover(os.getenv('LUOS_BAUDRATE', args.baudrate))[0] @@ -678,12 +639,13 @@ def luos_flash(args): baudrate = os.getenv('LUOS_BAUDRATE', args.baudrate) - print("\n" + UNDERLINE + "Luos flash subcommand with parameters:" + ENDC) - print('\t--baudrate : ', baudrate) - print('\t--gate : ', args.gate) - print('\t--target : ', args.target) - print('\t--binary : ', args.binary) - print('\t--port : ', args.port) + if (args.verbose): + print("\n" + UNDERLINE + "Luos flash subcommand with parameters:" + ENDC) + print('\t--baudrate : ', baudrate) + print('\t--gate : ', args.gate) + print('\t--target : ', args.target) + print('\t--binary : ', args.binary) + print('\t--port : ', args.port) # State used to check each step machine_state = True @@ -701,7 +663,11 @@ def luos_flash(args): f.close() # Init device + if (not args.verbose): + sys.stdout = open(os.devnull, 'w') device = Device(args.port, baudrate=baudrate, background_task=False) + if (not args.verbose): + sys.stdout = sys.__stdout__ # Get routing table JSON state = device._routing_table @@ -709,7 +675,7 @@ def luos_flash(args): return BOOTLOADER_DETECT_ERROR # Searching nodes to program in network - (nodes_to_reboot, nodes_to_program) = create_target_list(args, state) + nodes_to_program = create_target_list(args, state) # Check if we have available node to program if not nodes_to_program: @@ -717,101 +683,172 @@ def luos_flash(args): return BOOTLOADER_DETECT_ERROR # Reboot all nodes in bootloader mode - print("\n" + BOLD + "Reboot all nodes in bootloader mode:" + ENDC) - for node in nodes_to_reboot: - send_node_command(device, node, topic, BOOTLOADER_START) - # Delay to let the gate send the commands - time.sleep(0.01) + print("\n" + BOLD + "Rebooting all nodes in bootloader mode." + ENDC) + + need_to_redetect = False + for node in device._nodes: + if node.id in nodes_to_program: + + if (args.verbose): + print("─> Check if node", node.id, "is in bootloader mode.") + for service in node.services: + if "boot" in service.alias: + if (args.verbose): + print(OKGREEN + " ╰> Node", node.id, "is in bootloader mode." + ENDC) + else: + need_to_redetect = True + if (args.verbose): + print(OKGREEN + " ╰> Reboot node", node.id, "in bootloader mode." + ENDC) + send_node_command(device, node.id, topic, BOOTLOADER_START) + time.sleep(0.01) - # Delay to let the gate send the last command - time.sleep(0.1) + if need_to_redetect: + # Delay to let the gate send the last command + time.sleep(3) - # Find bootloaders in the routing table - state = find_network(device) - if state is None: - print(FAIL + " ╰> Reboot in bootloader mode failed." + ENDC) - return BOOTLOADER_DETECT_ERROR - else: - print(OKGREEN + " ╰> Reboot in bootloader mode succeed." + ENDC) + # remake a detection to check if all nodes are in bootloader mode + device.close() + + if (not args.verbose): + sys.stdout = open(os.devnull, 'w') + device = Device(args.port, baudrate=baudrate, background_task=False) + if (not args.verbose): + sys.stdout = sys.__stdout__ + state = device._routing_table + if (args.verbose): + print("\n" + BOLD + "Check if all node are in bootloader mode:" + ENDC) + if state is None: + print(FAIL + " ╰> Reboot in bootloader mode failed." + ENDC) + return BOOTLOADER_DETECT_ERROR + else: + # Check if all node of the 'nodes_to_program' list is in bootloader mode + detected_node = nodes_to_program.copy() + for node in device._nodes: + if node.id in nodes_to_program: + detected_node.remove(node.id) + if (args.verbose): + print("─> Check if node", node.id, "is in bootloader mode.") + for service in node.services: + if "boot" in service.alias: + if (args.verbose): + print(OKGREEN + " ╰> Node", node.id, "is in bootloader mode." + ENDC) + else: + total_fails.append(node.id) + if (args.verbose): + print(FAIL + " ╰> Node", node.id, "reboot in bootloader mode failed." + ENDC) + if (len(detected_node) > 0): + total_fails.extend(detected_node) + print(FAIL + " ╰> Nodes", detected_node, "failed to restart in bootloader mode." + ENDC) + + for node in total_fails: + try: + nodes_to_program.remove(node) + except: + pass + if len(nodes_to_program) == 0: + print(FAIL + "Programming failed on all targets." + ENDC) + return BOOTLOADER_FLASH_ERROR # Wait before the next step time.sleep(0.4) - print("\n" + BOLD + "Programming nodes:" + ENDC) + if (args.verbose): + print("\n" + BOLD + "Programming nodes:" + ENDC) + else: + print(BOLD + "Programming nodes:" + ENDC) # Go to header state if node is ready for node in nodes_to_program: - print("─> Check if node", node, " is ready.") - machine_state = send_ready_cmd(device, node, topic) + if (args.verbose): + print("─> Check if node", node, "is ready.") + machine_state = send_ready_cmd(device, node, topic, args.verbose) if not machine_state: total_fails.append(node) machine_state = True - print(FAIL + " ╰> Node ", node, "programming failed." + ENDC) + print(FAIL + " ╰> Node", node, "programming failed." + ENDC) time.sleep(0.01) for node in total_fails: - nodes_to_program.remove(node) + try: + nodes_to_program.remove(node) + except: + pass + if len(nodes_to_program) == 0: + print(BOLD + FAIL + "Programming failed on all targets." + ENDC) + return BOOTLOADER_FLASH_ERROR if not len(nodes_to_program): print(BOLD + FAIL + "Programming failed on all targets." + ENDC) return BOOTLOADER_FLASH_ERROR # Erase node flash memory - print("─> Erase flash memory.") - machine_state, failed_nodes = erase_flash(device, topic, nodes_to_program) + print("─> Erasing flash memory.") + machine_state, failed_nodes = erase_flash(device, topic, nodes_to_program, args.verbose) if not machine_state: for fail in failed_nodes: nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print(FAIL + " ╰> Node ", failed_nodes, " flash erasing failed!" + ENDC) + print(FAIL + " ╰> Node", failed_nodes, "flash erasing failed!" + ENDC) if not len(nodes_to_program): print(BOLD + FAIL + "Programming failed on all targets." + ENDC) return BOOTLOADER_FLASH_ERROR # send binary data - print("─> Send binary data.") + print("─> Sending binary data.") machine_state, failed_nodes = send_binary_data(device, topic, nodes_to_program) if not machine_state: total_fails.extend(failed_nodes) machine_state = True - print(FAIL + "Node ", failed_nodes, "programming failed." + ENDC) + print(FAIL + "Node", failed_nodes, "programming failed." + ENDC) + + if not len(nodes_to_program): + print(BOLD + FAIL + "Programming failed on all targets." + ENDC) + return BOOTLOADER_FLASH_ERROR # inform the node of the end of the loading - print("─> Programmation finished, waiting for acknowledge.") - machine_state, failed_nodes = send_binary_end(device, topic, nodes_to_program) + if (args.verbose): + print("─> Programmation finished, waiting for acknowledgements.") + machine_state, failed_nodes = send_binary_end(device, topic, nodes_to_program, args.verbose) if not machine_state: for fail in failed_nodes: nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print(FAIL + "Node ", failed_nodes, "ACK failed!" + ENDC) + print(FAIL + "Node", failed_nodes, "application validation failed!" + ENDC) # Ask the node to send binary crc - print("─> Check binary CRC.") - machine_state, failed_nodes = check_crc(device, topic, nodes_to_program) + if (args.verbose): + print("─> Checking binary CRC.") + machine_state, failed_nodes = check_crc(device, topic, nodes_to_program, args.verbose) if not machine_state: for fail in failed_nodes: nodes_to_program.remove(fail) total_fails.extend(failed_nodes) machine_state = True - print(FAIL + "Node ", failed_nodes, "ACK failed!" + ENDC) + print(FAIL + "Node", failed_nodes, "ACK failed!" + ENDC) - # Say to the bootloader that the integrity - # of the app saved in flash has been verified + # Say to the bootloader that the integrity of the app saved in flash has been verified + if (args.verbose): + print("─> Valid application.") send_topic_command(device, topic, BOOTLOADER_APP_SAVED) # wait before next step - time.sleep(0.1) + time.sleep(1) # reboot all nodes in application mode + if (args.verbose): + print("\n" + BOLD + "Rebooting all nodes in application mode." + ENDC) + else: + print(BOLD + "Rebooting all nodes in application mode." + ENDC) + reboot_network(device, topic, nodes_to_program, args.verbose) if len(total_fails) == 0: - print(OKGREEN + BOLD + "Programming succeed." + ENDC) - reboot_network(device, topic, nodes_to_reboot) + print(OKGREEN + BOLD + "Programming succeed in {:.3f} s.".format(time.time() - begin_date) + ENDC) device.close() return BOOTLOADER_SUCCESS else: device.close() - print(FAIL + "Nodes ", total_fails, " programming failed, please reboot and retry." + ENDC) + print(BOLD + "Programming in {:.3f} s.".format(time.time() - begin_date) + ENDC) + print(FAIL + "Nodes", total_fails, "programming failed, please reboot and retry." + ENDC) return BOOTLOADER_FLASH_ERROR # ******************************************************************************* @@ -859,11 +896,12 @@ def luos_reset(args): baudrate = os.getenv('LUOS_BAUDRATE', args.baudrate) print('Luos discover subcommand on port : ', args.port) - print('\tLuos discover subcommand at baudrate : ', args.baudrate) + print('\tLuos discover subcommand at baudrate : ', baudrate) # send rescue command print('Send reset command.') - port = serial.Serial(args.port, baudrate, timeout=0.05) + # port = serial.Serial(args.port, baudrate, timeout=0.05) + port = io_from_host(host=args.port, baudrate=baudrate) rst_cmd = { 'bootloader': { 'command': { @@ -902,6 +940,7 @@ def luos_options(): # declare "flash" subcommand flash_parser = subparsers.add_parser('flash', help='tool to program luos nodes') + flash_parser.add_argument('-v', '--verbose', help='verbose mode', action='store_true') flash_parser.add_argument('port', help='port used to detect network', nargs='?') From 03d4182420dc3c30be884e70e15cd46c33caacad Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Wed, 22 Nov 2023 16:11:09 +0100 Subject: [PATCH 09/10] Improve exclusion management --- pyluos/device.py | 45 +++++++++++++++++++++++++++++++++----- pyluos/services/service.py | 5 ++--- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/pyluos/device.py b/pyluos/device.py index 2ca21f3..c40cc79 100644 --- a/pyluos/device.py +++ b/pyluos/device.py @@ -233,12 +233,38 @@ def _poll_and_up(self): # Update our model with the new state. def _update(self, new_state): if 'dead_service' in new_state.keys(): - # we have lost a service put a flag on this service - alias = new_state['dead_service'] - if hasattr(self, alias): - getattr(self, alias)._kill() - if (self._freedomLink != None): - self._freedomLink._kill(alias) + # We have lost a service put a flag on this service + service_id = new_state['dead_service'] + # Find the service. + for service in self._services: + if (service.id == service_id): + s = "************************* EXCLUSION *************************\n" + s += "* Service " + str(service.alias) + " have been excluded from the network due to no responses." + s += "\n*************************************************************" + print(s) + if (self._freedomLink != None): + self._freedomLink._kill(service.alias) + service._kill() + break + + if 'dead_node' in new_state.keys(): + # We have lost a node put a flag on all node services + node_id = new_state['dead_node'] + for node in self._nodes: + if (node.id == node_id): + s = "************************* EXCLUSION *************************\n" + s += "* Node " + str(service.alias) + "have been excluded from the network due to no responses." + s += "\nThis exclude all services from this node :" + for service in node.services: + if (self._freedomLink != None): + self._freedomLink._kill(service.alias) + service._kill() + s += "\n* Service " + str(service.alias) + " have been excluded from the network due to no responses." + + s += "\n*************************************************************" + print(s) + break + if 'assert' in new_state.keys(): # A node assert, print assert informations if (('node_id' in new_state['assert']) and ('file' in new_state['assert']) and ('line' in new_state['assert'])): @@ -246,6 +272,13 @@ def _update(self, new_state): s += "* Node " + str(new_state['assert']['node_id']) + " assert in file " + new_state['assert']['file'] + " line " + str(new_state['assert']['line']) s += "\n**********************************************************" print(s) + # Consider this service as dead. + # Find the service from it's node id. + for node in self._nodes: + if (node.id == new_state['assert']['node_id']): + for service in node.services: + service._kill() + break if (self._freedomLink != None): self._freedomLink._assert(alias) if 'services' not in new_state.keys(): diff --git a/pyluos/services/service.py b/pyluos/services/service.py index 442e9d2..2aaa225 100644 --- a/pyluos/services/service.py +++ b/pyluos/services/service.py @@ -74,11 +74,10 @@ def _update(self, new_state): def _kill(self): self._killed = True - print("service", self.alias, "have been excluded from the network due to no responses.") def _push_value(self, key, new_val): if (self._killed): - print("service", self.alias, "is excluded.") + print("service", self.alias, "have been excluded, you can no longer acess it.") else: if isinstance(new_val, float): self._delegate.update_cmd(self.alias, key, float(str("%.3f" % new_val))) @@ -87,7 +86,7 @@ def _push_value(self, key, new_val): def _push_data(self, key, new_val, data): if (self._killed): - print("service", self.alias, "is excluded.") + print("service", self.alias, "have been excluded, you can no longer acess it.") else: self._delegate.update_data(self.alias, key, new_val, data) From 0eb83618ad78df452a4012382894732de9b6b6e1 Mon Sep 17 00:00:00 2001 From: Nicolas Rabault Date: Fri, 1 Dec 2023 11:16:19 +0100 Subject: [PATCH 10/10] Replace `imp` package with `importlib` --- setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 5987637..36b3841 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,14 @@ #!/usr/bin/env python -import imp +try: + import imp + version = imp.load_source('pyluos.version', 'pyluos/version.py') +except ImportError: + from importlib.machinery import SourceFileLoader + version = SourceFileLoader('pyluos.version', 'pyluos/version.py').load_module() from setuptools import setup, find_packages -version = imp.load_source('pyluos.version', 'pyluos/version.py') - with open("README.md", "r") as fh: long_description = fh.read()