Skip to content

Commit b4fd9e7

Browse files
authored
Generate pdu table direct. (#2752)
1 parent 6f98569 commit b4fd9e7

File tree

1 file changed

+25
-29
lines changed

1 file changed

+25
-29
lines changed

pymodbus/pdu/decoders.py

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Modbus Request/Response Decoders."""
22
from __future__ import annotations
33

4+
import copy
5+
46
from pymodbus.exceptions import MessageRegisterException, ModbusException
57
from pymodbus.logging import Log
68

@@ -10,46 +12,44 @@
1012
class DecodePDU:
1113
"""Decode pdu requests/responses (server/client)."""
1214

13-
_pdu_class_table: set[tuple[type[ModbusPDU], type[ModbusPDU]]] = set()
14-
_pdu_sub_class_table: set[tuple[type[ModbusPDU], type[ModbusPDU]]] = set()
15+
pdu_table: dict[int, tuple[type[ModbusPDU], type[ModbusPDU]]] = {}
16+
pdu_sub_table: dict[int, dict[int, tuple[type[ModbusPDU], type[ModbusPDU]]]] = {}
17+
1518

1619
def __init__(self, is_server: bool) -> None:
1720
"""Initialize function_tables."""
18-
inx = 0 if is_server else 1
19-
self.lookup: dict[int, type[ModbusPDU]] = {cl[inx].function_code: cl[inx] for cl in self._pdu_class_table}
20-
self.sub_lookup: dict[int, dict[int, type[ModbusPDU]]] = {}
21-
for f in self._pdu_sub_class_table:
22-
if (function_code := f[inx].function_code) not in self.sub_lookup:
23-
self.sub_lookup[function_code] = {f[inx].sub_function_code: f[inx]}
24-
else:
25-
self.sub_lookup[function_code][f[inx].sub_function_code] = f[inx]
21+
self.pdu_inx = 0 if is_server else 1
2622

2723
def lookupPduClass(self, data: bytes) -> type[ModbusPDU] | None:
2824
"""Use `function_code` to determine the class of the PDU."""
29-
func_code = int(data[1])
30-
if func_code & 0x80:
25+
if (func_code := int(data[1])) & 0x80:
3126
return ExceptionResponse
27+
if not (pdu := self.pdu_table.get(func_code, (None, None))[self.pdu_inx]):
28+
return None
29+
3230
if func_code == 0x2B: # mei message, sub_function_code is 1 byte
3331
sub_func_code = int(data[2])
34-
return self.sub_lookup[func_code].get(sub_func_code, None)
35-
if func_code == 0x08: # diag message, sub_function_code is 2 bytes
32+
elif func_code == 0x08: # diag message, sub_function_code is 2 bytes
3633
sub_func_code = int.from_bytes(data[2:4], "big")
37-
return self.sub_lookup[func_code].get(sub_func_code, None)
38-
return self.lookup.get(func_code, None)
34+
else:
35+
return pdu
36+
return self.pdu_sub_table[func_code].get(sub_func_code, (None, None))[self.pdu_inx]
3937

4038
def list_function_codes(self):
4139
"""Return list of function codes."""
42-
return list(self.lookup)
40+
return list(self.pdu_table)
4341

4442
@classmethod
4543
def add_pdu(cls, req: type[ModbusPDU], resp: type[ModbusPDU]):
4644
"""Register request/response."""
47-
cls._pdu_class_table.add((req, resp))
45+
cls.pdu_table[req.function_code] = (req, resp)
4846

4947
@classmethod
5048
def add_sub_pdu(cls, req: type[ModbusPDU], resp: type[ModbusPDU]):
5149
"""Register request/response."""
52-
cls._pdu_sub_class_table.add((req, resp))
50+
if req.function_code not in cls.pdu_sub_table:
51+
cls.pdu_sub_table[req.function_code] = {}
52+
cls.pdu_sub_table[req.function_code][req.sub_function_code] = (req, resp)
5353

5454
def register(self, custom_class: type[ModbusPDU]) -> None:
5555
"""Register a function and sub function class with the decoder."""
@@ -59,13 +59,9 @@ def register(self, custom_class: type[ModbusPDU]) -> None:
5959
". Class needs to be derived from "
6060
"`pymodbus.pdu.ModbusPDU` "
6161
)
62-
self.lookup[custom_class.function_code] = custom_class
63-
if custom_class.sub_function_code >= 0:
64-
if custom_class.function_code not in self.sub_lookup:
65-
self.sub_lookup[custom_class.function_code] = {}
66-
self.sub_lookup[custom_class.function_code][
67-
custom_class.sub_function_code
68-
] = custom_class
62+
if "pdu_table" not in self.__dict__:
63+
self.pdu_table = copy.deepcopy(DecodePDU.pdu_table)
64+
self.pdu_table[custom_class.function_code] = (custom_class, custom_class)
6965

7066
def decode(self, frame: bytes) -> ModbusPDU | None:
7167
"""Decode a frame."""
@@ -74,14 +70,14 @@ def decode(self, frame: bytes) -> ModbusPDU | None:
7470
pdu_exp = ExceptionResponse(function_code & 0x7F)
7571
pdu_exp.decode(frame[1:])
7672
return pdu_exp
77-
if not (pdu_class := self.lookup.get(function_code, None)):
73+
if not (pdu_class := self.pdu_table.get(function_code, (None, None))[self.pdu_inx]):
7874
Log.debug("decode PDU failed for function code {}", function_code)
7975
raise ModbusException(f"Unknown response {function_code}")
8076
pdu = pdu_class()
8177
pdu.decode(frame[1:])
8278
if pdu.sub_function_code >= 0:
83-
lookup = self.sub_lookup.get(pdu.function_code, {})
84-
if sub_class := lookup.get(pdu.sub_function_code, None):
79+
lookup = self.pdu_sub_table.get(pdu.function_code, {})
80+
if sub_class := lookup.get(pdu.sub_function_code, (None,None))[self.pdu_inx]:
8581
pdu = sub_class()
8682
pdu.decode(frame[1:])
8783
Log.debug("decoded PDU function_code({} sub {}) -> {} ", pdu.function_code, pdu.sub_function_code, str(pdu))

0 commit comments

Comments
 (0)