diff mbox series

[BlueZ] test: Fix payload and model opcode packing in test-mesh

Message ID 20200514022033.52864-1-inga.stotland@intel.com (mailing list archive)
State Accepted
Headers show
Series [BlueZ] test: Fix payload and model opcode packing in test-mesh | expand

Commit Message

Stotland, Inga May 14, 2020, 2:20 a.m. UTC
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(-)

Comments

Brian Gix May 14, 2020, 1:54 p.m. UTC | #1
Applied
On Wed, 2020-05-13 at 19:20 -0700, Inga Stotland wrote:
> 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):
>
diff mbox series

Patch

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):