Modbus registers may be referenced by address or by a number. Number defines both register type (coil, discrete input, input register and holding register) and its address.
Two conversions are used simultaneously: 1. For registers with address less than 9999 offset of x0001 is used, where x defines the register type; 2. Offset x00001 is used. This convention is applicable for any address. The change adds a single point of conversion of addresses into register numbers and fixes conversion done in different handlers which were not in sync with each other. --- decoders/modbus/pd.py | 67 ++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/decoders/modbus/pd.py b/decoders/modbus/pd.py index 81aaf2a..76891bb 100644 --- a/decoders/modbus/pd.py +++ b/decoders/modbus/pd.py @@ -180,6 +180,33 @@ def calc_crc(self, last_byte): byte2 = (result & 0xFF00) >> 8 return (byte1, byte2) + def get_register_number(self, function, address): + # Register number defines both type and address of a register + # https://en.wikipedia.org/wiki/Modbus#Coil,_discrete_input,_input_register,_holding_register_numbers_and_addresses + ENTITY_TYPES = {1: 0, # Read coils + 5: 0, # Write Single Coil + 15: 0, # Write Multiple Coils + 2: 1, # Read Discrete Inputs + 4: 3, # Read Input Registers + 3: 4, # Read Multiple Holding Registers + 6: 4, # Write Single Holding Register + 16: 4, # Write Multiple Holding Registers + 22: 4, # Mask Write Register + 23: 4, # Read/Write Multiple Registers + 24: 4, # Read FIFO Queue + } + + if function not in ENTITY_TYPES: + return '-' + + entity_type = ENTITY_TYPES[function] + if address <= 9998: + register_number = '{:d}{:04d}'.format(entity_type, address + 1) + else: + register_number = '{:d}{:05d}'.format(entity_type, address + 1) + + return register_number + def parse_write_single_coil(self): '''Parse function 5, write single coil.''' self.minimum_length = 8 @@ -188,7 +215,7 @@ def parse_write_single_coil(self): address = self.half_word(2) self.puti(3, 'address', - 'Address 0x{:X} / {:d}'.format(address, address + 10000)) + 'Address 0x{:X} / {}'.format(address, self.get_register_number(5, address))) raw_value = self.half_word(4) value = 'Invalid Coil Value' @@ -208,7 +235,7 @@ def parse_write_single_register(self): address = self.half_word(2) self.puti(3, 'address', - 'Address 0x{:X} / {:d}'.format(address, address + 30000)) + 'Address 0x{:X} / {}'.format(address, self.get_register_number(6, address))) value = self.half_word(4) value_formatted = 'Register Value 0x{0:X} / {0:d}'.format(value) @@ -261,7 +288,7 @@ def parse_mask_write_register(self): address = self.half_word(2) self.puti(3, 'address', - 'Address 0x{:X} / {:d}'.format(address, address + 30001)) + 'Address 0x{:X} / {}'.format(address, self.get_register_number(22, address))) self.half_word(4) # To make sure we don't oveflow data. and_mask_1 = data[4].data @@ -470,22 +497,17 @@ def parse_write_multiple(self): if function == 15: data_unit = 'Coils' max_outputs = 0x07B0 - long_address_offset = 10001 elif function == 16: data_unit = 'Registers' max_outputs = 0x007B - long_address_offset = 30001 self.puti(1, 'function', 'Function {}: Write Multiple {}'.format(function, data_unit)) starting_address = self.half_word(2) - # Some instruction manuals use a long form name for addresses, this is - # listed here for convienience. - address_name = long_address_offset + starting_address self.puti(3, 'address', - 'Start at address 0x{:X} / {:d}'.format(starting_address, - address_name)) + 'Start at address 0x{:X} / {}'.format(starting_address, + self.get_register_number(function, starting_address))) quantity_of_outputs = self.half_word(4) if quantity_of_outputs <= max_outputs: @@ -646,13 +668,9 @@ def parse_read_data_command(self): 'Function {}: {}'.format(function, functionname)) starting_address = self.half_word(2) - # Some instruction manuals use a long form name for addresses, this is - # listed here for convienience. - # Example: holding register 60 becomes 30061. - address_name = 10000 * function + 1 + starting_address self.puti(3, 'address', - 'Start at address 0x{:X} / {:d}'.format(starting_address, - address_name)) + 'Start at address 0x{:X} / {}'.format(starting_address, + self.get_register_number(function, starting_address))) self.puti(5, 'length', 'Read {:d} units of data'.format(self.half_word(4))) @@ -681,23 +699,18 @@ def parse_write_multiple(self): data_unit = 'Coils' max_outputs = 0x07B0 ratio_bytes_data = 1/8 - long_address_offset = 10001 elif function == 16: data_unit = 'Registers' max_outputs = 0x007B ratio_bytes_data = 2 - long_address_offset = 30001 self.puti(1, 'function', 'Function {}: Write Multiple {}'.format(function, data_unit)) starting_address = self.half_word(2) - # Some instruction manuals use a long form name for addresses, this is - # listed here for convienience. - address_name = long_address_offset + starting_address self.puti(3, 'address', - 'Start at address 0x{:X} / {:d}'.format(starting_address, - address_name)) + 'Start at address 0x{:X} / {}'.format(starting_address, + self.get_register_number(function, starting_address))) quantity_of_outputs = self.half_word(4) if quantity_of_outputs <= max_outputs: @@ -778,13 +791,9 @@ def parse_read_write_registers(self): self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers') starting_address = self.half_word(2) - # Some instruction manuals use a long form name for addresses, this is - # listed here for convienience. - # Example: holding register 60 becomes 30061. - address_name = 30001 + starting_address self.puti(3, 'address', - 'Read starting at address 0x{:X} / {:d}'.format(starting_address, - address_name)) + 'Read starting at address 0x{:X} / {}'.format(starting_address, + self.get_register_number(function, starting_address))) self.puti(5, 'length', 'Read {:d} units of data'.format(self.half_word(4))) -- 2.25.1 _______________________________________________ sigrok-devel mailing list sigrok-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/sigrok-devel