Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OTAPCommunicator #14

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/OTAPCommunicator/OTAPCommunicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 :
Expand Down
12 changes: 10 additions & 2 deletions libs/SmartMeshSDK/IpMgrConnectorMux/MuxMsg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
66 changes: 36 additions & 30 deletions libs/SmartMeshSDK/protocols/otap/OTAPCommunicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, ]

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -214,24 +220,24 @@ 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))
print (msg)
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()
Expand All @@ -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
Expand All @@ -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),
Expand All @@ -304,28 +310,28 @@ 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):
self.commit_complete()

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()
Expand Down