11"""Modbus Request/Response Decoders."""
22from __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
114from pymodbus .exceptions import MessageRegisterException , ModbusException
125from pymodbus .logging import Log
13-
6+ from . pdu import ModbusPDU , ExceptionResponse
147
158class 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 )):
0 commit comments