diff --git a/app/OTAPCommunicator/OTAPCommunicator.py b/app/OTAPCommunicator/OTAPCommunicator.py index 64d0851..8e7df91 100644 --- a/app/OTAPCommunicator/OTAPCommunicator.py +++ b/app/OTAPCommunicator/OTAPCommunicator.py @@ -151,6 +151,15 @@ def send_data(mac, msg, port): cbid = resp.callbackId except ApiException.APIError as ex: rc = ex.rc + # On a node with a weak signal invalid responses were received + # they caused VALUE_NOT_IN_OPTIONS exceptions to be raised. + # handle all the other exceptions to retry the send in: + # libs/SmartMeshSDK/protocols/otap/OTAPCommunicator.py->data_task + except Exception as err: + log.debug('Exception in send_data: %s', str(err)) + # 14 = VALUE_NOT_IN_OPTIONS, but in this RC context I saw it defined as NAK, + # in any case it is not handled uniquely and is an error that should be retried. + rc = 14 # always return RC, callbackId return (rc, cbid) diff --git a/libs/SmartMeshSDK/IpMgrConnectorMux/IpMgrConnectorMuxInternal.py b/libs/SmartMeshSDK/IpMgrConnectorMux/IpMgrConnectorMuxInternal.py index f0d5520..a277849 100644 --- a/libs/SmartMeshSDK/IpMgrConnectorMux/IpMgrConnectorMuxInternal.py +++ b/libs/SmartMeshSDK/IpMgrConnectorMux/IpMgrConnectorMuxInternal.py @@ -93,7 +93,7 @@ def disconnect(self, reason="") : if not self.isConnected : return try : - self.socket.send("stop") + self.socket.send(b"stop") self.socket.shutdown(socket.SHUT_RD) # start disconnection self.socket.close() except socket.error : diff --git a/libs/SmartMeshSDK/IpMgrConnectorMux/MuxMsg.py b/libs/SmartMeshSDK/IpMgrConnectorMux/MuxMsg.py index cf648b6..5046b92 100644 --- a/libs/SmartMeshSDK/IpMgrConnectorMux/MuxMsg.py +++ b/libs/SmartMeshSDK/IpMgrConnectorMux/MuxMsg.py @@ -20,7 +20,7 @@ def __init__(self, cb, ver = VERSION, magic = MAGIC, auth = AUTH): self.ver = ver self.auth = auth self.magic = magic - self.input_buffer = '' + self.input_buffer = b'' # Initialize input_buffer to an empty bytes object def getVer(self) : return self.ver @@ -42,7 +42,15 @@ def parse(self, data): ''' if not data: return - self.input_buffer += data + + # Make sure a bytes object is appended to input_buffer. + if isinstance(data, bytes): + self.input_buffer += data + elif isinstance(data, str): + self.input_buffer += data.encode() + else: + return + while self.parse_one(): pass diff --git a/libs/SmartMeshSDK/protocols/otap/OTAPCommunicator.py b/libs/SmartMeshSDK/protocols/otap/OTAPCommunicator.py index 1f42d63..fa2168c 100644 --- a/libs/SmartMeshSDK/protocols/otap/OTAPCommunicator.py +++ b/libs/SmartMeshSDK/protocols/otap/OTAPCommunicator.py @@ -36,6 +36,12 @@ def emit(self, record): def print_mac(m): return '-'.join(["%02X" % b for b in m]) +# found the mac was a tuple and the tables are lists and Python3 operators fail on type mismatch +def mac_in_mac_table(m, mlist): + for each in mlist: + if tuple(m) == tuple(each): + return True + return False BROADCAST_ADDR = 8 * [ 0xFF, ] @@ -82,7 +88,7 @@ def add_dependent(self, block_num, mac): else: num_deps = self.blocks[block_num][0] + 1 macs = self.blocks[block_num][1] - macs.append(mac) + macs.append(list(mac)) self.blocks[block_num] = (num_deps, macs) def get_meta(self, block_num): @@ -214,16 +220,15 @@ def handshake_callback(self, mac, cmd_data): if not self.state == 'Handshake': return - - if not mac in self.handshake_motes: + if not mac_in_mac_table(mac, self.handshake_motes): log.info('Duplicate handshake response for %s: %d', print_mac(mac), oh_resp.otapResult) return # add a mote that accepts the handshake to the list of motes to send to if oh_resp.otapResult == 0: # validate mac is in the handshake list - if mac in self.handshake_motes and not mac in self.incomplete_motes: - self.incomplete_motes.append(mac) + if mac_in_mac_table(mac, self.handshake_motes) and not mac_in_mac_table(mac, self.incomplete_motes): + self.incomplete_motes.append(list(mac)) else: otap_err = otap_error_string(oh_resp.otapResult) msg = "Handshake rejected (%s) by %s" % (otap_err, print_mac(mac)) @@ -231,7 +236,8 @@ def handshake_callback(self, mac, cmd_data): log.warning(msg) # TODO: handle the delay field # remove this mote from the list of expected handshakers - self.handshake_motes.remove(mac) + log.info(str(self.handshake_motes)) + self.handshake_motes.remove(list(mac)) # once the list of expected handshakers is empty, we're ready to move on if not len(self.handshake_motes): self.handshake_complete() @@ -243,30 +249,30 @@ def status_callback(self, mac, cmd_data): os_resp = OtapStatusResp() os_resp.parse(cmd_data) log.debug(str(os_resp)) - + if not self.state == 'Status': return # remove this mote from the list of motes we need status from - if mac in self.status_motes: - self.status_motes.remove(mac) + if mac_in_mac_table(mac, self.status_motes): + self.status_motes.remove(list(mac)) # if missing blocks, add_data_block if len(os_resp.missing_blocks): for b in os_resp.missing_blocks: self.transmit_list.add_dependent(b, mac) # otherwise, move the mote to the completed list elif os_resp.header.otapResult == 0: - if mac in self.incomplete_motes: + if mac_in_mac_table(mac, self.incomplete_motes): log.info('Data transmission to %s is complete' % print_mac(mac)) - self.incomplete_motes.remove(mac) - self.complete_motes.append(mac) + self.incomplete_motes.remove(list(mac)) + self.complete_motes.append(list(mac)) else: # no missing blocks, but status is an error - if mac in self.incomplete_motes: + if mac_in_mac_table(mac, self.incomplete_motes): msg = 'Status error (%s) for %s, declaring failure' % (otap_error_string(os_resp.header.otapResult), print_mac(mac)) log.error(msg) - self.incomplete_motes.remove(mac) - self.failure_motes.append(mac) + self.incomplete_motes.remove(list(mac)) + self.failure_motes.append(list(mac)) # TODO: handle the response that indicates the mote has reset or forgotten # about this OTAP session @@ -291,8 +297,8 @@ def commit_callback(self, mac, cmd_data): if not self.state == 'Commit': return - if mac in self.commit_motes: - self.commit_motes.remove(mac) + if mac_in_mac_table(mac, self.commit_motes): + self.commit_motes.remove(list(mac)) if oc_resp.otapResult == 0: fcs = self.files[self.current_file].fcs msg = '%s committed %s [FCS=0x%04x]' % (print_mac(mac), @@ -304,8 +310,8 @@ def commit_callback(self, mac, cmd_data): msg = 'Commit error (%s) on %s' % (otap_error_string(oc_resp.otapResult), print_mac(mac)) print (msg) log.error(msg) - self.complete_motes.remove(mac) - self.failure_motes.append(mac) + self.complete_motes.remove(list(mac)) + self.failure_motes.append(list(mac)) # detect when all motes have responded to the commit if not len(self.commit_motes): @@ -313,19 +319,19 @@ def commit_callback(self, mac, cmd_data): def cmd_failure_callback(self, mac, cmd_id): log.error('Command failure for %s, command %d' % (print_mac(mac), cmd_id)) - self.failure_motes.append(mac) + self.failure_motes.append(list(mac)) # TODO: remove the failed mote from the all_motes list for the next file(s)? # remove the failed mote from the internal lists - if mac in self.handshake_motes: - self.handshake_motes.remove(mac) - if mac in self.incomplete_motes: - self.incomplete_motes.remove(mac) - if mac in self.status_motes: - self.status_motes.remove(mac) - if mac in self.commit_motes: - self.commit_motes.remove(mac) - if mac in self.complete_motes: - self.complete_motes.remove(mac) + if mac_in_mac_table(mac, self.handshake_motes): + self.handshake_motes.remove(list(mac)) + if mac_in_mac_table(mac, self.incomplete_motes): + self.incomplete_motes.remove(list(mac)) + if mac_in_mac_table(mac, self.status_motes): + self.status_motes.remove(list(mac)) + if mac_in_mac_table(mac, self.commit_motes): + self.commit_motes.remove(list(mac)) + if mac_in_mac_table(mac, self.complete_motes): + self.complete_motes.remove(list(mac)) # detect if this command failure means we need to change state if self.state == 'Handshake' and not len(self.handshake_motes): self.handshake_complete()