Skip to content

Commit 40d388a

Browse files
committed
feat: Add Modbus response PDU truncation by byte count
Signed-off-by: David Rapan <david@rapan.cz>
1 parent 2a1c1e1 commit 40d388a

3 files changed

Lines changed: 83 additions & 69 deletions

File tree

custom_components/solarman/pysolarman/umodbus/client/serial/rtu.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,12 @@ def send_message(adu, serial_port):
226226

227227

228228
function_code_to_function_map = {
229-
FUNCTION_CODE.READ_COILS: lambda slave_id, address, count, **kwargs: read_coils(slave_id, address, count),
230-
FUNCTION_CODE.READ_DISCRETE_INPUTS: lambda slave_id, address, count, **kwargs: read_discrete_inputs(slave_id, address, count),
231-
FUNCTION_CODE.READ_HOLDING_REGISTERS: lambda slave_id, address, count, **kwargs: read_holding_registers(slave_id, address, count),
232-
FUNCTION_CODE.READ_INPUT_REGISTERS: lambda slave_id, address, count, **kwargs: read_input_registers(slave_id, address, count),
233-
FUNCTION_CODE.WRITE_SINGLE_COIL: lambda slave_id, address, data, **kwargs: write_single_coil(slave_id, address, data),
234-
FUNCTION_CODE.WRITE_SINGLE_REGISTER: lambda slave_id, address, data, **kwargs: write_single_register(slave_id, address, data),
235-
FUNCTION_CODE.WRITE_MULTIPLE_COILS: lambda slave_id, address, data, **kwargs: write_multiple_coils(slave_id, address, data),
236-
FUNCTION_CODE.WRITE_MULTIPLE_REGISTERS: lambda slave_id, address, data, **kwargs: write_multiple_registers(slave_id, address, data)
229+
FUNCTION_CODE.READ_COILS: lambda slave_id, address, count, **_: read_coils(slave_id, address, count),
230+
FUNCTION_CODE.READ_DISCRETE_INPUTS: lambda slave_id, address, count, **_: read_discrete_inputs(slave_id, address, count),
231+
FUNCTION_CODE.READ_HOLDING_REGISTERS: lambda slave_id, address, count, **_: read_holding_registers(slave_id, address, count),
232+
FUNCTION_CODE.READ_INPUT_REGISTERS: lambda slave_id, address, count, **_: read_input_registers(slave_id, address, count),
233+
FUNCTION_CODE.WRITE_SINGLE_COIL: lambda slave_id, address, data, **_: write_single_coil(slave_id, address, data),
234+
FUNCTION_CODE.WRITE_SINGLE_REGISTER: lambda slave_id, address, data, **_: write_single_register(slave_id, address, data),
235+
FUNCTION_CODE.WRITE_MULTIPLE_COILS: lambda slave_id, address, data, **_: write_multiple_coils(slave_id, address, data),
236+
FUNCTION_CODE.WRITE_MULTIPLE_REGISTERS: lambda slave_id, address, data, **_: write_multiple_registers(slave_id, address, data)
237237
}

custom_components/solarman/pysolarman/umodbus/client/tcp.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,12 @@ def send_message(adu, sock):
270270

271271

272272
function_code_to_function_map = {
273-
FUNCTION_CODE.READ_COILS: lambda slave_id, address, count, **kwargs: read_coils(slave_id, address, count),
274-
FUNCTION_CODE.READ_DISCRETE_INPUTS: lambda slave_id, address, count, **kwargs: read_discrete_inputs(slave_id, address, count),
275-
FUNCTION_CODE.READ_HOLDING_REGISTERS: lambda slave_id, address, count, **kwargs: read_holding_registers(slave_id, address, count),
276-
FUNCTION_CODE.READ_INPUT_REGISTERS: lambda slave_id, address, count, **kwargs: read_input_registers(slave_id, address, count),
277-
FUNCTION_CODE.WRITE_SINGLE_COIL: lambda slave_id, address, data, **kwargs: write_single_coil(slave_id, address, data),
278-
FUNCTION_CODE.WRITE_SINGLE_REGISTER: lambda slave_id, address, data, **kwargs: write_single_register(slave_id, address, data),
279-
FUNCTION_CODE.WRITE_MULTIPLE_COILS: lambda slave_id, address, data, **kwargs: write_multiple_coils(slave_id, address, data),
280-
FUNCTION_CODE.WRITE_MULTIPLE_REGISTERS: lambda slave_id, address, data, **kwargs: write_multiple_registers(slave_id, address, data)
273+
FUNCTION_CODE.READ_COILS: lambda slave_id, address, count, **_: read_coils(slave_id, address, count),
274+
FUNCTION_CODE.READ_DISCRETE_INPUTS: lambda slave_id, address, count, **_: read_discrete_inputs(slave_id, address, count),
275+
FUNCTION_CODE.READ_HOLDING_REGISTERS: lambda slave_id, address, count, **_: read_holding_registers(slave_id, address, count),
276+
FUNCTION_CODE.READ_INPUT_REGISTERS: lambda slave_id, address, count, **_: read_input_registers(slave_id, address, count),
277+
FUNCTION_CODE.WRITE_SINGLE_COIL: lambda slave_id, address, data, **_: write_single_coil(slave_id, address, data),
278+
FUNCTION_CODE.WRITE_SINGLE_REGISTER: lambda slave_id, address, data, **_: write_single_register(slave_id, address, data),
279+
FUNCTION_CODE.WRITE_MULTIPLE_COILS: lambda slave_id, address, data, **_: write_multiple_coils(slave_id, address, data),
280+
FUNCTION_CODE.WRITE_MULTIPLE_REGISTERS: lambda slave_id, address, data, **_: write_multiple_registers(slave_id, address, data)
281281
}

custom_components/solarman/pysolarman/umodbus/functions.py

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -347,27 +347,26 @@ def create_from_response_pdu(cls, resp_pdu, req_pdu):
347347
:param quantity: Number of coils read.
348348
:return: Instance of :class:`ReadCoils`.
349349
"""
350-
read_coils = cls()
351-
read_coils.quantity = struct.unpack('>H', req_pdu[-2:])[0]
352-
byte_count = struct.unpack('>B', resp_pdu[1:2])[0]
353-
354-
fmt = '>' + ('B' * byte_count)
355-
bytes_ = struct.unpack(fmt, resp_pdu[2:])
350+
instance = cls()
351+
instance.quantity = struct.unpack('>H', req_pdu[-2:])[0]
352+
instance.byte_count = struct.unpack('>B', resp_pdu[1:2])[0]
356353

354+
fmt = '>' + ('B' * instance.byte_count)
357355
data = list()
358356

359-
for i, value in enumerate(bytes_):
360-
padding = 8 if (read_coils.quantity - (8 * i)) // 8 > 0 \
361-
else read_coils.quantity % 8
357+
for i, value in enumerate(struct.unpack(fmt, resp_pdu[2:2 + instance.byte_count])):
358+
padding = 8 if (instance.quantity - (8 * i)) // 8 > 0 \
359+
else instance.quantity % 8
362360

363361
fmt = '{{0:0{padding}b}}'.format(padding=padding)
364362

365363
# Create binary representation of integer, convert it to a list
366364
# and reverse the list.
367365
data = data + [int(i) for i in fmt.format(value)][::-1]
368366

369-
read_coils.data = data
370-
return read_coils
367+
instance.data = data
368+
369+
return instance
371370

372371
def execute(self, slave_id, route_map):
373372
""" Execute the Modbus function registered for a route.
@@ -555,27 +554,26 @@ def create_from_response_pdu(cls, resp_pdu, req_pdu):
555554
:param quantity: Number of inputs read.
556555
:return: Instance of :class:`ReadDiscreteInputs`.
557556
"""
558-
read_discrete_inputs = cls()
559-
read_discrete_inputs.quantity = struct.unpack('>H', req_pdu[-2:])[0]
560-
byte_count = struct.unpack('>B', resp_pdu[1:2])[0]
561-
562-
fmt = '>' + ('B' * byte_count)
563-
bytes_ = struct.unpack(fmt, resp_pdu[2:])
557+
instance = cls()
558+
instance.quantity = struct.unpack('>H', req_pdu[-2:])[0]
559+
instance.byte_count = struct.unpack('>B', resp_pdu[1:2])[0]
564560

561+
fmt = '>' + ('B' * instance.byte_count)
565562
data = list()
566563

567-
for i, value in enumerate(bytes_):
568-
padding = 8 if (read_discrete_inputs.quantity - (8 * i)) // 8 > 0 \
569-
else read_discrete_inputs.quantity % 8
564+
for i, value in enumerate(struct.unpack(fmt, resp_pdu[2:2 + instance.byte_count])):
565+
padding = 8 if (instance.quantity - (8 * i)) // 8 > 0 \
566+
else instance.quantity % 8
570567

571568
fmt = '{{0:0{padding}b}}'.format(padding=padding)
572569

573570
# Create binary representation of integer, convert it to a list
574571
# and reverse the list.
575572
data = data + [int(i) for i in fmt.format(value)][::-1]
576573

577-
read_discrete_inputs.data = data
578-
return read_discrete_inputs
574+
instance.data = data
575+
576+
return instance
579577

580578
def execute(self, slave_id, route_map):
581579
""" Execute the Modbus function registered for a route.
@@ -742,15 +740,17 @@ def create_from_response_pdu(cls, resp_pdu, req_pdu):
742740
:param quantity: Number of coils read.
743741
:return: Instance of :class:`ReadCoils`.
744742
"""
745-
read_holding_registers = cls()
746-
read_holding_registers.quantity = struct.unpack('>H', req_pdu[-2:])[0]
747-
read_holding_registers.byte_count = \
748-
struct.unpack('>B', resp_pdu[1:2])[0]
749-
750-
fmt = '>' + (conf.TYPE_CHAR * read_holding_registers.quantity)
751-
read_holding_registers.data = list(struct.unpack(fmt, resp_pdu[2:]))
743+
instance = cls()
744+
instance.quantity = struct.unpack('>H', req_pdu[-2:])[0]
745+
instance.byte_count = struct.unpack('>B', resp_pdu[1:2])[0]
746+
instance.data = list(
747+
struct.unpack(
748+
'>' + (conf.TYPE_CHAR * instance.quantity),
749+
resp_pdu[2:2 + instance.byte_count]
750+
)
751+
)
752752

753-
return read_holding_registers
753+
return instance
754754

755755
def execute(self, slave_id, route_map):
756756
""" Execute the Modbus function registered for a route.
@@ -917,13 +917,17 @@ def create_from_response_pdu(cls, resp_pdu, req_pdu):
917917
:param quantity: Number of coils read.
918918
:return: Instance of :class:`ReadCoils`.
919919
"""
920-
read_input_registers = cls()
921-
read_input_registers.quantity = struct.unpack('>H', req_pdu[-2:])[0]
922-
923-
fmt = '>' + (conf.TYPE_CHAR * read_input_registers.quantity)
924-
read_input_registers.data = list(struct.unpack(fmt, resp_pdu[2:]))
920+
instance = cls()
921+
instance.quantity = struct.unpack('>H', req_pdu[-2:])[0]
922+
instance.byte_count = struct.unpack('>B', resp_pdu[1:2])[0]
923+
instance.data = list(
924+
struct.unpack(
925+
'>' + (conf.TYPE_CHAR * instance.quantity),
926+
resp_pdu[2:2 + instance.byte_count]
927+
)
928+
)
925929

926-
return read_input_registers
930+
return instance
927931

928932
def execute(self, slave_id, route_map):
929933
""" Execute the Modbus function registered for a route.
@@ -1078,15 +1082,15 @@ def create_from_response_pdu(cls, resp_pdu):
10781082
:param resp_pdu: Byte array with request PDU.
10791083
:return: Instance of :class:`WriteSingleCoil`.
10801084
"""
1081-
write_single_coil = cls()
1085+
instance = cls()
10821086

10831087
address, value = struct.unpack('>HH', resp_pdu[1:5])
10841088
value = 1 if value == 0xFF00 else value
10851089

1086-
write_single_coil.address = address
1087-
write_single_coil.data = value
1090+
instance.address = address
1091+
instance.data = value
10881092

1089-
return write_single_coil
1093+
return instance
10901094

10911095
def execute(self, slave_id, route_map):
10921096
""" Execute the Modbus function registered for a route.
@@ -1221,14 +1225,14 @@ def create_from_response_pdu(cls, resp_pdu):
12211225
:param resp_pdu: Byte array with request PDU.
12221226
:return: Instance of :class:`WriteSingleRegister`.
12231227
"""
1224-
write_single_register = cls()
1228+
instance = cls()
12251229

12261230
address, value = struct.unpack('>H' + conf.TYPE_CHAR, resp_pdu[1:5])
12271231

1228-
write_single_register.address = address
1229-
write_single_register.data = value
1232+
instance.address = address
1233+
instance.data = value
12301234

1231-
return write_single_register
1235+
return instance
12321236

12331237
def execute(self, slave_id, route_map):
12341238
""" Execute the Modbus function registered for a route.
@@ -1432,14 +1436,19 @@ def create_response_pdu(self):
14321436

14331437
@classmethod
14341438
def create_from_response_pdu(cls, resp_pdu):
1435-
write_multiple_coils = cls()
1439+
""" Create instance from response PDU.
1440+
1441+
:param resp_pdu: Byte array with request PDU.
1442+
:return: Instance of :class:`WriteMultipleCoils`.
1443+
"""
1444+
instance = cls()
14361445

14371446
starting_address, data = struct.unpack('>HH', resp_pdu[1:5])
14381447

1439-
write_multiple_coils.starting_address = starting_address
1440-
write_multiple_coils.data = data
1448+
instance.starting_address = starting_address
1449+
instance.data = data
14411450

1442-
return write_multiple_coils
1451+
return instance
14431452

14441453
def execute(self, slave_id, route_map):
14451454
""" Execute the Modbus function registered for a route.
@@ -1546,7 +1555,7 @@ def create_from_request_pdu(cls, pdu):
15461555
:param pdu: A request PDU.
15471556
:return: Instance of this class.
15481557
"""
1549-
_, starting_address, quantity, byte_count = \
1558+
_, starting_address, _, byte_count = \
15501559
struct.unpack('>BHHB', pdu[:6])
15511560

15521561
# Values are 16 bit, so each value takes up 2 bytes.
@@ -1580,14 +1589,19 @@ def create_response_pdu(self):
15801589

15811590
@classmethod
15821591
def create_from_response_pdu(cls, resp_pdu):
1583-
write_multiple_registers = cls()
1592+
""" Create instance from response PDU.
1593+
1594+
:param resp_pdu: Byte array with request PDU.
1595+
:return: Instance of :class:`WriteMultipleRegisters`.
1596+
"""
1597+
instance = cls()
15841598

15851599
starting_address, data = struct.unpack('>HH', resp_pdu[1:5])
15861600

1587-
write_multiple_registers.starting_address = starting_address
1588-
write_multiple_registers.data = data
1601+
instance.starting_address = starting_address
1602+
instance.data = data
15891603

1590-
return write_multiple_registers
1604+
return instance
15911605

15921606
def execute(self, slave_id, route_map):
15931607
""" Execute the Modbus function registered for a route.

0 commit comments

Comments
 (0)