Use correct packing of multi-byte values in message payload bytearray. For example, a 2-byte opcode 0x8204 is packed as 0x82 0x04, i.e. in natural order. Add transaction ID parameter to "set" commands of generic On/Off model. Server will ignore the identical commands with the same transaction ID, source and destination during a timeout period of 6 seconds. --- test/test-mesh | 94 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/test/test-mesh b/test/test-mesh index 66055e2de..38f0c0a74 100755 --- a/test/test-mesh +++ b/test/test-mesh @@ -146,6 +146,8 @@ APP_VERSION_ID = 0x0001 VENDOR_ID_NONE = 0xffff +TRANSACTION_TIMEOUT = 6 + app = None bus = None mainloop = None @@ -330,7 +332,7 @@ def print_state(state): print('ON') else: print('UNKNOWN') -class PubTimer(): +class ModTimer(): def __init__(self): self.seconds = None self.func = None @@ -473,18 +475,18 @@ class Element(dbus.service.Object): @dbus.service.method(MESH_ELEMENT_IFACE, in_signature="qqvay", out_signature="") - def MessageReceived(self, source, key, destination, data): + def MessageReceived(self, source, key, dest, data): print(('Message Received on Element %02x') % self.index, end='') print(', src=', format(source, '04x'), end='') - if isinstance(destination, int): - print(', dst=%04x' % destination) - elif isinstance(destination, dbus.Array): - dst_str = array_to_string(destination) + if isinstance(dest, int): + print(', dst=%04x' % dest) + elif isinstance(dest, dbus.Array): + dst_str = array_to_string(dest) print(', dst=' + dst_str) for model in self.models: - model.process_message(source, key, data) + model.process_message(source, dest, key, data) @dbus.service.method(MESH_ELEMENT_IFACE, in_signature="qa{sv}", out_signature="") @@ -528,7 +530,7 @@ class Model(): def get_vendor(self): return self.vendor - def process_message(self, source, key, data): + def process_message(self, source, dest, key, data): return def set_publication(self, period): @@ -576,6 +578,9 @@ class Model(): class OnOffServer(Model): def __init__(self, model_id): Model.__init__(self, model_id) + self.tid = None + self.last_src = 0x0000 + self.last_dst = 0x0000 self.cmd_ops = { 0x8201, # get 0x8202, # set 0x8203, # set unacknowledged @@ -584,48 +589,74 @@ class OnOffServer(Model): print("OnOff Server ") self.state = 0 print_state(self.state) - self.timer = PubTimer() + self.pub_timer = ModTimer() + self.t_timer = ModTimer() - def process_message(self, source, key, data): + def process_message(self, source, dest, key, data): datalen = len(data) - if datalen != 2 and datalen != 3: + if datalen != 2 and datalen != 4: # The opcode is not recognized by this model return if datalen == 2: - op_tuple=struct.unpack('<H',bytes(data)) + op_tuple=struct.unpack('>H',bytes(data)) opcode = op_tuple[0] + if opcode != 0x8201: # The opcode is not recognized by this model return print('Get state') - elif datalen == 3: - opcode,self.state=struct.unpack('<HB',bytes(data)) + elif datalen == 4: + opcode,self.state, tid = struct.unpack('>HBB', + bytes(data)) + if opcode != 0x8202 and opcode != 0x8203: # The opcode is not recognized by this model return print_state(self.state) - rsp_data = struct.pack('<HB', 0x8204, self.state) + if (self.tid != None and self.tid == tid and + self.last_src == source and + self.last_dst == dest): + # Ignore duplicate transaction + return + + self.t_timer.cancel() + self.tid = tid + self.last_src = source + self.last_dst = dest + self.t_timer.start(TRANSACTION_TIMEOUT, self.t_track) + + # Unacknowledged "set" + if opcode == 0x8203: + return + + rsp_data = struct.pack('>HB', 0x8204, self.state) self.send_message(source, key, rsp_data) + def t_track(self): + self.t_timer.cancel() + self.tid = None + self.last_src = 0x0000 + self.last_dst = 0x0000 + def set_publication(self, period): self.pub_period = period if period == 0: - self.timer.cancel() + self.pub_timer.cancel() return # We do not handle ms in this example if period < 1000: return - self.timer.start(period/1000, self.publish) + self.pub_timer.start(period/1000, self.publish) def publish(self): print('Publish') - data = struct.pack('<HB', 0x8204, self.state) + data = struct.pack('>HB', 0x8204, self.state) self.send_publication(data) ######################## @@ -634,6 +665,8 @@ class OnOffServer(Model): class OnOffClient(Model): def __init__(self, model_id): Model.__init__(self, model_id) + self.tid = 0 + self.data = None self.cmd_ops = { 0x8201, # get 0x8202, # set 0x8203, # set unacknowledged @@ -646,16 +679,23 @@ class OnOffClient(Model): def get_state(self, dest, key): opcode = 0x8201 - data = struct.pack('<H', opcode) - self._send_message(dest, key, data) + self.data = struct.pack('>H', opcode) + self._send_message(dest, key, self.data) def set_state(self, dest, key, state): opcode = 0x8202 print('Set state:', state) - data = struct.pack('<HB', opcode, state) - self._send_message(dest, key, data) + self.data = struct.pack('>HBB', opcode, state, self.tid) + self.tid = (self.tid + 1) % 255 + self._send_message(dest, key, self.data) + + def repeat(self, dest, key): + if self.data != None: + self._send_message(dest, key, self.data) + else: + print('No previous command stored') - def process_message(self, source, key, data): + def process_message(self, source, dest, key, data): print('OnOffClient process message len = ', end = '') datalen = len(data) print(datalen) @@ -664,7 +704,7 @@ class OnOffClient(Model): # The opcode is not recognized by this model return - opcode, state = struct.unpack('<HB',bytes(data)) + opcode, state = struct.unpack('>HB',bytes(data)) if opcode != 0x8204 : # The opcode is not recognized by this model @@ -919,12 +959,14 @@ class ClientMenu(Menu): self.__cmd_set_state_off), 'on': MenuItem(' - set state ON', self.__cmd_set_state_on), + 'repeat': MenuItem(' - repeat last command', + self.__cmd_repeat_transaction), 'back': MenuItem(' - back to main menu', self.__cmd_main_menu), 'quit': MenuItem(' - exit the test', app_exit) } - Menu.__init__(self, 'On/Off Clien Menu', menu_items) + Menu.__init__(self, 'On/Off Client Menu', menu_items) def __cmd_main_menu(self): switch_menu(MAIN_MENU) @@ -938,6 +980,8 @@ class ClientMenu(Menu): def __cmd_set_state_on(self): app.elements[1].models[0].set_state(dst_addr, app_idx, 1) + def __cmd_repeat_transaction(self): + app.elements[1].models[0].repeat(dst_addr, app_idx) def set_value(str_value, min, max): -- 2.26.2