Skip to content

Commit f7e2d21

Browse files
committed
Simplify pdu lookup.
1 parent 1e07d7f commit f7e2d21

File tree

7 files changed

+70
-58
lines changed

7 files changed

+70
-58
lines changed

pymodbus/pdu/bit_message.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pymodbus.constants import ExcCodes, ModbusStatus
77
from pymodbus.datastore import ModbusDeviceContext
88

9+
from .decoders import DecodePDU
910
from .exceptionresponse import ExceptionResponse
1011
from .pdu import ModbusPDU, pack_bitstring, unpack_bitstring
1112

@@ -163,3 +164,8 @@ def encode(self) -> bytes:
163164
def decode(self, data: bytes) -> None:
164165
"""Decode a write coils response."""
165166
self.address, self.count = struct.unpack(">HH", data[:4])
167+
168+
DecodePDU.add_pdu(ReadCoilsRequest, ReadCoilsResponse)
169+
DecodePDU.add_pdu(ReadDiscreteInputsRequest, ReadDiscreteInputsResponse)
170+
DecodePDU.add_pdu(WriteSingleCoilRequest, WriteSingleCoilResponse)
171+
DecodePDU.add_pdu(WriteMultipleCoilsRequest, WriteMultipleCoilsResponse)

pymodbus/pdu/decoders.py

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,32 @@
11
"""Modbus Request/Response Decoders."""
22
from __future__ import annotations
33

4-
import pymodbus.pdu.bit_message as bit_msg
5-
import pymodbus.pdu.diag_message as diag_msg
6-
import pymodbus.pdu.file_message as file_msg
7-
import pymodbus.pdu.mei_message as mei_msg
8-
import pymodbus.pdu.other_message as o_msg
9-
import pymodbus.pdu.pdu as base
10-
import pymodbus.pdu.register_message as reg_msg
114
from pymodbus.exceptions import MessageRegisterException, ModbusException
125
from pymodbus.logging import Log
13-
6+
from .pdu import ModbusPDU, ExceptionResponse
147

158
class DecodePDU:
169
"""Decode pdu requests/responses (server/client)."""
1710

18-
_pdu_class_table: set[tuple[type[base.ModbusPDU], type[base.ModbusPDU]]] = {
19-
(reg_msg.ReadHoldingRegistersRequest, reg_msg.ReadHoldingRegistersResponse),
20-
(bit_msg.ReadDiscreteInputsRequest, bit_msg.ReadDiscreteInputsResponse),
21-
(reg_msg.ReadInputRegistersRequest, reg_msg.ReadInputRegistersResponse),
22-
(bit_msg.ReadCoilsRequest, bit_msg.ReadCoilsResponse),
23-
(bit_msg.WriteMultipleCoilsRequest, bit_msg.WriteMultipleCoilsResponse),
24-
(reg_msg.WriteMultipleRegistersRequest, reg_msg.WriteMultipleRegistersResponse),
25-
(reg_msg.WriteSingleRegisterRequest, reg_msg.WriteSingleRegisterResponse),
26-
(bit_msg.WriteSingleCoilRequest, bit_msg.WriteSingleCoilResponse),
27-
(reg_msg.ReadWriteMultipleRegistersRequest, reg_msg.ReadWriteMultipleRegistersResponse),
28-
(diag_msg.DiagnosticBase, diag_msg.DiagnosticBase),
29-
(o_msg.ReadExceptionStatusRequest, o_msg.ReadExceptionStatusResponse),
30-
(o_msg.GetCommEventCounterRequest, o_msg.GetCommEventCounterResponse),
31-
(o_msg.GetCommEventLogRequest, o_msg.GetCommEventLogResponse),
32-
(o_msg.ReportDeviceIdRequest, o_msg.ReportDeviceIdResponse),
33-
(file_msg.ReadFileRecordRequest, file_msg.ReadFileRecordResponse),
34-
(file_msg.WriteFileRecordRequest, file_msg.WriteFileRecordResponse),
35-
(reg_msg.MaskWriteRegisterRequest, reg_msg.MaskWriteRegisterResponse),
36-
(file_msg.ReadFifoQueueRequest, file_msg.ReadFifoQueueResponse),
37-
(mei_msg.ReadDeviceInformationRequest, mei_msg.ReadDeviceInformationResponse),
38-
}
39-
40-
_pdu_sub_class_table: set[tuple[type[base.ModbusPDU], type[base.ModbusPDU]]] = {
41-
(diag_msg.ReturnQueryDataRequest, diag_msg.ReturnQueryDataResponse),
42-
(diag_msg.RestartCommunicationsOptionRequest, diag_msg.RestartCommunicationsOptionResponse),
43-
(diag_msg.ReturnDiagnosticRegisterRequest, diag_msg.ReturnDiagnosticRegisterResponse),
44-
(diag_msg.ChangeAsciiInputDelimiterRequest, diag_msg.ChangeAsciiInputDelimiterResponse),
45-
(diag_msg.ForceListenOnlyModeRequest, diag_msg.ForceListenOnlyModeResponse),
46-
(diag_msg.ClearCountersRequest, diag_msg.ClearCountersResponse),
47-
(diag_msg.ReturnBusMessageCountRequest, diag_msg.ReturnBusMessageCountResponse),
48-
(diag_msg.ReturnBusCommunicationErrorCountRequest, diag_msg.ReturnBusCommunicationErrorCountResponse),
49-
(diag_msg.ReturnBusExceptionErrorCountRequest, diag_msg.ReturnBusExceptionErrorCountResponse),
50-
(diag_msg.ReturnDeviceMessageCountRequest, diag_msg.ReturnDeviceMessageCountResponse),
51-
(diag_msg.ReturnDeviceNoResponseCountRequest, diag_msg.ReturnDeviceNoResponseCountResponse),
52-
(diag_msg.ReturnDeviceNAKCountRequest, diag_msg.ReturnDeviceNAKCountResponse),
53-
(diag_msg.ReturnDeviceBusyCountRequest, diag_msg.ReturnDeviceBusyCountResponse),
54-
(diag_msg.ReturnDeviceBusCharacterOverrunCountRequest, diag_msg.ReturnDeviceBusCharacterOverrunCountResponse),
55-
(diag_msg.ReturnIopOverrunCountRequest, diag_msg.ReturnIopOverrunCountResponse),
56-
(diag_msg.ClearOverrunCountRequest, diag_msg.ClearOverrunCountResponse),
57-
(diag_msg.GetClearModbusPlusRequest, diag_msg.GetClearModbusPlusResponse),
58-
(mei_msg.ReadDeviceInformationRequest, mei_msg.ReadDeviceInformationResponse),
59-
}
11+
_pdu_class_table: set[tuple[type[ModbusPDU], type[ModbusPDU]]] = set()
12+
_pdu_sub_class_table: set[tuple[type[ModbusPDU], type[ModbusPDU]]] = set()
6013

6114
def __init__(self, is_server: bool) -> None:
6215
"""Initialize function_tables."""
6316
inx = 0 if is_server else 1
64-
self.lookup: dict[int, type[base.ModbusPDU]] = {cl[inx].function_code: cl[inx] for cl in self._pdu_class_table}
65-
self.sub_lookup: dict[int, dict[int, type[base.ModbusPDU]]] = {}
17+
self.lookup: dict[int, type[ModbusPDU]] = {cl[inx].function_code: cl[inx] for cl in self._pdu_class_table}
18+
self.sub_lookup: dict[int, dict[int, type[ModbusPDU]]] = {}
6619
for f in self._pdu_sub_class_table:
6720
if (function_code := f[inx].function_code) not in self.sub_lookup:
6821
self.sub_lookup[function_code] = {f[inx].sub_function_code: f[inx]}
6922
else:
7023
self.sub_lookup[function_code][f[inx].sub_function_code] = f[inx]
7124

72-
def lookupPduClass(self, data: bytes) -> type[base.ModbusPDU] | None:
25+
def lookupPduClass(self, data: bytes) -> type[ModbusPDU] | None:
7326
"""Use `function_code` to determine the class of the PDU."""
7427
func_code = int(data[1])
7528
if func_code & 0x80:
76-
return base.ExceptionResponse
29+
return ExceptionResponse
7730
if func_code == 0x2B: # mei message, sub_function_code is 1 byte
7831
sub_func_code = int(data[2])
7932
return self.sub_lookup[func_code].get(sub_func_code, None)
@@ -82,9 +35,19 @@ def lookupPduClass(self, data: bytes) -> type[base.ModbusPDU] | None:
8235
return self.sub_lookup[func_code].get(sub_func_code, None)
8336
return self.lookup.get(func_code, None)
8437

85-
def register(self, custom_class: type[base.ModbusPDU]) -> None:
38+
@classmethod
39+
def add_pdu(cls, req: type[ModbusPDU], resp: type[ModbusPDU]):
40+
"""Register request/response."""
41+
cls._pdu_class_table.add((req, resp))
42+
43+
@classmethod
44+
def add_sub_pdu(cls, req: type[ModbusPDU], resp: type[ModbusPDU]):
45+
"""Register request/response."""
46+
cls._pdu_sub_class_table.add((req, resp))
47+
48+
def register(self, custom_class: type[ModbusPDU]) -> None:
8649
"""Register a function and sub function class with the decoder."""
87-
if not issubclass(custom_class, base.ModbusPDU):
50+
if not issubclass(custom_class, ModbusPDU):
8851
raise MessageRegisterException(
8952
f'"{custom_class.__class__.__name__}" is Not a valid Modbus Message'
9053
". Class needs to be derived from "
@@ -98,11 +61,11 @@ def register(self, custom_class: type[base.ModbusPDU]) -> None:
9861
custom_class.sub_function_code
9962
] = custom_class
10063

101-
def decode(self, frame: bytes) -> base.ModbusPDU | None:
64+
def decode(self, frame: bytes) -> ModbusPDU | None:
10265
"""Decode a frame."""
10366
try:
10467
if (function_code := int(frame[0])) > 0x80:
105-
pdu_exp = base.ExceptionResponse(function_code & 0x7F)
68+
pdu_exp = ExceptionResponse(function_code & 0x7F)
10669
pdu_exp.decode(frame[1:])
10770
return pdu_exp
10871
if not (pdu_class := self.lookup.get(function_code, None)):

pymodbus/pdu/diag_message.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pymodbus.constants import ModbusPlusOperation
88
from pymodbus.datastore import ModbusDeviceContext
99

10+
from .decoders import DecodePDU
1011
from .device import ModbusControlBlock
1112
from .pdu import ModbusPDU, pack_bitstring
1213

@@ -381,3 +382,22 @@ class GetClearModbusPlusResponse(DiagnosticBase):
381382
"""GetClearModbusPlusResponse."""
382383

383384
sub_function_code = 0x0015
385+
386+
DecodePDU.add_pdu(DiagnosticBase, DiagnosticBase)
387+
DecodePDU.add_sub_pdu(ReturnQueryDataRequest, ReturnQueryDataResponse)
388+
DecodePDU.add_sub_pdu(RestartCommunicationsOptionRequest, RestartCommunicationsOptionResponse)
389+
DecodePDU.add_sub_pdu(ReturnDiagnosticRegisterRequest, ReturnDiagnosticRegisterResponse)
390+
DecodePDU.add_sub_pdu(ChangeAsciiInputDelimiterRequest, ChangeAsciiInputDelimiterResponse)
391+
DecodePDU.add_sub_pdu(ForceListenOnlyModeRequest, ForceListenOnlyModeResponse)
392+
DecodePDU.add_sub_pdu(ClearCountersRequest, ClearCountersResponse)
393+
DecodePDU.add_sub_pdu(ReturnBusMessageCountRequest, ReturnBusMessageCountResponse)
394+
DecodePDU.add_sub_pdu(ReturnBusCommunicationErrorCountRequest, ReturnBusCommunicationErrorCountResponse)
395+
DecodePDU.add_sub_pdu(ReturnBusExceptionErrorCountRequest, ReturnBusExceptionErrorCountResponse)
396+
DecodePDU.add_sub_pdu(ReturnDeviceMessageCountRequest, ReturnDeviceMessageCountResponse)
397+
DecodePDU.add_sub_pdu(ReturnDeviceNoResponseCountRequest, ReturnDeviceNoResponseCountResponse)
398+
DecodePDU.add_sub_pdu(ReturnDeviceNAKCountRequest, ReturnDeviceNAKCountResponse)
399+
DecodePDU.add_sub_pdu(ReturnDeviceBusyCountRequest, ReturnDeviceBusyCountResponse)
400+
DecodePDU.add_sub_pdu(ReturnDeviceBusCharacterOverrunCountRequest, ReturnDeviceBusCharacterOverrunCountResponse)
401+
DecodePDU.add_sub_pdu(ReturnIopOverrunCountRequest, ReturnIopOverrunCountResponse)
402+
DecodePDU.add_sub_pdu(ClearOverrunCountRequest, ClearOverrunCountResponse)
403+
DecodePDU.add_sub_pdu(GetClearModbusPlusRequest, GetClearModbusPlusResponse)

pymodbus/pdu/file_message.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pymodbus.datastore import ModbusDeviceContext
88
from pymodbus.exceptions import ModbusException
99

10+
from .decoders import DecodePDU
1011
from .pdu import ModbusPDU
1112

1213

@@ -276,3 +277,7 @@ def decode(self, data: bytes) -> None:
276277
for index in range(0, count - 4):
277278
idx = 4 + index * 2
278279
self.values.append(struct.unpack(">H", data[idx : idx + 2])[0])
280+
281+
DecodePDU.add_pdu(ReadFileRecordRequest, ReadFileRecordResponse)
282+
DecodePDU.add_pdu(WriteFileRecordRequest, WriteFileRecordResponse)
283+
DecodePDU.add_pdu(ReadFifoQueueRequest, ReadFifoQueueResponse)

pymodbus/pdu/mei_message.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pymodbus.constants import DeviceInformation, ExcCodes, MoreData
77
from pymodbus.datastore import ModbusDeviceContext
88

9+
from .decoders import DecodePDU
910
from .device import DeviceInformationFactory, ModbusControlBlock
1011
from .exceptionresponse import ExceptionResponse
1112
from .pdu import ModbusPDU
@@ -153,3 +154,6 @@ def decode(self, data: bytes) -> None:
153154
self.information[object_id],
154155
data[count - object_length : count],
155156
]
157+
158+
DecodePDU.add_pdu(ReadDeviceInformationRequest, ReadDeviceInformationResponse)
159+
DecodePDU.add_sub_pdu(ReadDeviceInformationRequest, ReadDeviceInformationResponse)

pymodbus/pdu/other_message.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pymodbus.constants import ModbusStatus
77
from pymodbus.datastore import ModbusDeviceContext
88

9+
from .decoders import DecodePDU
910
from .device import DeviceInformationFactory, ModbusControlBlock
1011
from .pdu import ModbusPDU
1112

@@ -208,3 +209,8 @@ def decode(self, data: bytes) -> None:
208209
self.identifier = data[1 : self.byte_count + 1]
209210
status = int(data[-1])
210211
self.status = status == ID_ON
212+
213+
DecodePDU.add_pdu(ReadExceptionStatusRequest, ReadExceptionStatusResponse)
214+
DecodePDU.add_pdu(GetCommEventCounterRequest, GetCommEventCounterResponse)
215+
DecodePDU.add_pdu(GetCommEventLogRequest, GetCommEventLogResponse)
216+
DecodePDU.add_pdu(ReportDeviceIdRequest, ReportDeviceIdResponse)

pymodbus/pdu/register_message.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pymodbus.datastore import ModbusDeviceContext
1111
from pymodbus.exceptions import ModbusIOException
1212

13+
from .decoders import DecodePDU
1314
from .exceptionresponse import ExceptionResponse
1415
from .pdu import ModbusPDU
1516

@@ -381,3 +382,10 @@ def encode(self) -> bytes:
381382
def decode(self, data: bytes) -> None:
382383
"""Decode a the response."""
383384
self.address, self.and_mask, self.or_mask = struct.unpack(">HHH", data[:6])
385+
386+
DecodePDU.add_pdu(ReadHoldingRegistersRequest, ReadHoldingRegistersResponse)
387+
DecodePDU.add_pdu(ReadInputRegistersRequest, ReadInputRegistersResponse)
388+
DecodePDU.add_pdu(WriteMultipleRegistersRequest, WriteMultipleRegistersResponse)
389+
DecodePDU.add_pdu(WriteSingleRegisterRequest, WriteSingleRegisterResponse)
390+
DecodePDU.add_pdu(ReadWriteMultipleRegistersRequest, ReadWriteMultipleRegistersResponse)
391+
DecodePDU.add_pdu(MaskWriteRegisterRequest, MaskWriteRegisterResponse)

0 commit comments

Comments
 (0)