Skip to content

Commit fc43617

Browse files
committed
Client/Server framer as enum.
1 parent 1c37fe9 commit fc43617

File tree

15 files changed

+148
-118
lines changed

15 files changed

+148
-118
lines changed

API_changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Versions (X.Y.Z) where Z > 0 e.g. 3.0.1 do NOT have API changes!
44

55
API changes 3.6.0 (future)
66
--------------------------
7+
- framer= is an enum: pymodbus.Framer, but still accept a framer class
78

89

910
API changes 3.5.0

examples/client_custom_msg.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@
1414
import logging
1515
import struct
1616

17+
from pymodbus import Framer
1718
from pymodbus.bit_read_message import ReadCoilsRequest
1819
from pymodbus.client import AsyncModbusTcpClient as ModbusClient
19-
20-
# --------------------------------------------------------------------------- #
21-
# import the various server implementations
22-
# --------------------------------------------------------------------------- #
2320
from pymodbus.pdu import ModbusExceptions, ModbusRequest, ModbusResponse
24-
from pymodbus.transaction import ModbusSocketFramer
2521

2622

2723
# --------------------------------------------------------------------------- #
@@ -130,7 +126,7 @@ def __init__(self, address, **kwargs):
130126

131127
async def main(host="localhost", port=5020):
132128
"""Run versions of read coil."""
133-
with ModbusClient(host=host, port=port, framer=ModbusSocketFramer) as client:
129+
with ModbusClient(host=host, port=port, framer_name=Framer.SOCKET) as client:
134130
await client.connect()
135131

136132
# new modbus function code.

examples/client_performance.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
import asyncio
1717
import time
1818

19+
from pymodbus import Framer
1920
from pymodbus.client import AsyncModbusSerialClient, ModbusSerialClient
20-
from pymodbus.transaction import ModbusRtuFramer
2121

2222

2323
LOOP_COUNT = 1000
@@ -29,7 +29,7 @@ def run_sync_client_test():
2929
print("--- Testing sync client v3.4.1")
3030
client = ModbusSerialClient(
3131
"/dev/ttys007",
32-
framer=ModbusRtuFramer,
32+
framer_name=Framer.RTU,
3333
baudrate=9600,
3434
)
3535
client.connect()
@@ -56,7 +56,7 @@ async def run_async_client_test():
5656
print("--- Testing async client v3.4.1")
5757
client = AsyncModbusSerialClient(
5858
"/dev/ttys007",
59-
framer=ModbusRtuFramer,
59+
framer_name=Framer.RTU,
6060
baudrate=9600,
6161
)
6262
await client.connect()

pymodbus/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
"""
55

66
__all__ = [
7+
"Framer",
78
"pymodbus_apply_logging_config",
89
"__version__",
910
"__version_full__",
1011
]
1112

13+
from pymodbus.framer import Framer
1214
from pymodbus.logging import pymodbus_apply_logging_config
1315

1416

pymodbus/client/base.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@
2020
class ModbusBaseClient(ModbusClientMixin, ModbusProtocol):
2121
"""**ModbusBaseClient**
2222
23-
**Parameters common to all clients**:
24-
25-
:param framer: (optional) Modbus Framer class.
26-
:param timeout: (optional) Timeout for a request, in seconds.
27-
:param retries: (optional) Max number of retries per request.
28-
:param retry_on_empty: (optional) Retry on empty response.
29-
:param close_comm_on_error: (optional) Close connection on error.
30-
:param strict: (optional) Strict timing, 1.5 character between requests.
31-
:param broadcast_enable: (optional) True to treat id 0 as broadcast address.
32-
:param reconnect_delay: (optional) Minimum delay in milliseconds before reconnecting.
33-
:param reconnect_delay_max: (optional) Maximum delay in milliseconds before reconnecting.
34-
:param on_reconnect_callback: (optional) Function that will be called just before a reconnection attempt.
35-
:param no_resend_on_retry: (optional) Do not resend request when retrying due to missing response.
36-
:param kwargs: (optional) Experimental parameters.
23+
Optional parameters:
24+
25+
:param framer: Modbus Framer class.
26+
:param timeout: Timeout for a request, in seconds.
27+
:param retries: Max number of retries per request.
28+
:param retry_on_empty: Retry on empty response.
29+
:param close_comm_on_error: Close connection on error.
30+
:param strict: Strict timing, 1.5 character between requests.
31+
:param broadcast_enable: True to treat id 0 as broadcast address.
32+
:param reconnect_delay: Minimum delay in milliseconds before reconnecting.
33+
:param reconnect_delay_max: Maximum delay in milliseconds before reconnecting.
34+
:param on_reconnect_callback: Function that will be called just before a reconnection attempt.
35+
:param no_resend_on_retry: Do not resend request when retrying due to missing response.
36+
:param kwargs: Experimental parameters.
3737
3838
.. tip::
3939
**reconnect_delay** doubles automatically with each unsuccessful connect, from
@@ -62,7 +62,7 @@ class _params:
6262

6363
def __init__( # pylint: disable=too-many-arguments
6464
self,
65-
framer: type[ModbusFramer] = None,
65+
framer_class: type[ModbusFramer] = None,
6666
timeout: float = 3,
6767
retries: int = 3,
6868
retry_on_empty: bool = False,
@@ -114,7 +114,7 @@ def __init__( # pylint: disable=too-many-arguments
114114
self.slaves: list[int] = []
115115

116116
# Common variables.
117-
self.framer = framer(ClientDecoder(), self)
117+
self.framer = framer_class(ClientDecoder(), self)
118118
self.transaction = DictTransactionManager(
119119
self, retries=retries, retry_on_empty=retry_on_empty, **kwargs
120120
)

pymodbus/client/mixin.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Modbus Client Common."""
2+
from __future__ import annotations
3+
24
import struct
35
from enum import Enum
4-
from typing import Any, List, Tuple, Union
6+
from typing import Any
57

68
import pymodbus.bit_read_message as pdu_bit_read
79
import pymodbus.bit_write_message as pdu_bit_write
@@ -381,7 +383,7 @@ def diag_get_comm_event_log(self, **kwargs: Any) -> ModbusResponse:
381383
def write_coils(
382384
self,
383385
address: int,
384-
values: Union[List[bool], bool],
386+
values: list[bool] | bool,
385387
slave: int = 0,
386388
**kwargs: Any,
387389
) -> ModbusResponse:
@@ -398,7 +400,7 @@ def write_coils(
398400
)
399401

400402
def write_registers(
401-
self, address: int, values: Union[List[int], int], slave: int = 0, **kwargs: Any
403+
self, address: int, values: list[int] | int, slave: int = 0, **kwargs: Any
402404
) -> ModbusResponse:
403405
"""Write registers (code 0x10).
404406
@@ -423,7 +425,7 @@ def report_slave_id(self, slave: int = 0, **kwargs: Any) -> ModbusResponse:
423425
"""
424426
return self.execute(pdu_other_msg.ReportSlaveIdRequest(slave, **kwargs))
425427

426-
def read_file_record(self, records: List[Tuple], **kwargs: Any) -> ModbusResponse:
428+
def read_file_record(self, records: list[tuple], **kwargs: Any) -> ModbusResponse:
427429
"""Read file record (code 0x14).
428430
429431
:param records: List of (Reference type, File number, Record Number, Record Length)
@@ -432,7 +434,7 @@ def read_file_record(self, records: List[Tuple], **kwargs: Any) -> ModbusRespons
432434
"""
433435
return self.execute(pdu_file_msg.ReadFileRecordRequest(records, **kwargs))
434436

435-
def write_file_record(self, records: List[Tuple], **kwargs: Any) -> ModbusResponse:
437+
def write_file_record(self, records: list[tuple], **kwargs: Any) -> ModbusResponse:
436438
"""Write file record (code 0x15).
437439
438440
:param records: List of (Reference type, File number, Record Number, Record Length)
@@ -465,7 +467,7 @@ def readwrite_registers(
465467
read_address: int = 0,
466468
read_count: int = 0,
467469
write_address: int = 0,
468-
values: Union[List[int], int] = 0,
470+
values: list[int] | int = 0,
469471
slave: int = 0,
470472
**kwargs,
471473
) -> ModbusResponse:
@@ -534,8 +536,8 @@ class DATATYPE(Enum):
534536

535537
@classmethod
536538
def convert_from_registers(
537-
cls, registers: List[int], data_type: DATATYPE
538-
) -> Union[int, float, str]:
539+
cls, registers: list[int], data_type: DATATYPE
540+
) -> int | float | str:
539541
"""Convert registers to int/float/str.
540542
541543
:param registers: list of registers received from e.g. read_holding_registers()
@@ -558,8 +560,8 @@ def convert_from_registers(
558560

559561
@classmethod
560562
def convert_to_registers(
561-
cls, value: Union[int, float, str], data_type: DATATYPE
562-
) -> List[int]:
563+
cls, value: int | float | str, data_type: DATATYPE
564+
) -> list[int]:
563565
"""Convert int/float/str to registers (16/32/64 bit).
564566
565567
:param value: value to be converted

pymodbus/client/serial.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""Modbus client async serial communication."""
2+
from __future__ import annotations
3+
24
import asyncio
35
import time
46
from contextlib import suppress
57
from functools import partial
6-
from typing import Any, Type
8+
from typing import Any
79

810
from pymodbus.client.base import ModbusBaseClient
911
from pymodbus.exceptions import ConnectionException
10-
from pymodbus.framer import ModbusFramer
11-
from pymodbus.framer.rtu_framer import ModbusRtuFramer
12+
from pymodbus.framer import FRAMER_NAME_TO_CLASS, Framer
1213
from pymodbus.logging import Log
1314
from pymodbus.transport import CommType
1415
from pymodbus.utilities import ModbusTransactionState
@@ -27,7 +28,6 @@ class AsyncModbusSerialClient(ModbusBaseClient, asyncio.Protocol):
2728
2829
Optional parameters:
2930
30-
:param framer: Framer class.
3131
:param baudrate: Bits per second.
3232
:param bytesize: Number of bits per byte 7-8.
3333
:param parity: 'E'ven, 'O'dd or 'N'one
@@ -36,6 +36,7 @@ class AsyncModbusSerialClient(ModbusBaseClient, asyncio.Protocol):
3636
3737
Common optional parameters:
3838
39+
:param framer: Framer enum name
3940
:param timeout: Timeout for a request, in seconds.
4041
:param retries: Max number of retries per request.
4142
:param retry_on_empty: Retry on empty response.
@@ -65,7 +66,7 @@ async def run():
6566
def __init__(
6667
self,
6768
port: str,
68-
framer: Type[ModbusFramer] = ModbusRtuFramer,
69+
framer: Framer = Framer.RTU,
6970
baudrate: int = 19200,
7071
bytesize: int = 8,
7172
parity: str = "N",
@@ -74,9 +75,10 @@ def __init__(
7475
) -> None:
7576
"""Initialize Asyncio Modbus Serial Client."""
7677
asyncio.Protocol.__init__(self)
78+
framer_class = FRAMER_NAME_TO_CLASS.get(framer, framer)
7779
ModbusBaseClient.__init__(
7880
self,
79-
framer=framer,
81+
framer_class=framer_class, # type: ignore[arg-type]
8082
CommType=CommType.SERIAL,
8183
host=port,
8284
baudrate=baudrate,
@@ -106,7 +108,6 @@ class ModbusSerialClient(ModbusBaseClient):
106108
107109
Optional parameters:
108110
109-
:param framer: Framer class.
110111
:param baudrate: Bits per second.
111112
:param bytesize: Number of bits per byte 7-8.
112113
:param parity: 'E'ven, 'O'dd or 'N'one
@@ -115,6 +116,7 @@ class ModbusSerialClient(ModbusBaseClient):
115116
116117
Common optional parameters:
117118
119+
:param framer: Framer enum name
118120
:param timeout: Timeout for a request, in seconds.
119121
:param retries: Max number of retries per request.
120122
:param retry_on_empty: Retry on empty response.
@@ -150,7 +152,7 @@ def run():
150152
def __init__(
151153
self,
152154
port: str,
153-
framer: Type[ModbusFramer] = ModbusRtuFramer,
155+
framer: Framer = Framer.RTU,
154156
baudrate: int = 19200,
155157
bytesize: int = 8,
156158
parity: str = "N",
@@ -160,9 +162,10 @@ def __init__(
160162
"""Initialize Modbus Serial Client."""
161163
self.transport = None
162164
kwargs["use_sync"] = True
165+
framer_class = FRAMER_NAME_TO_CLASS.get(framer, framer)
163166
ModbusBaseClient.__init__(
164167
self,
165-
framer=framer,
168+
framer_class=framer_class, # type: ignore[arg-type]
166169
CommType=CommType.SERIAL,
167170
host=port,
168171
baudrate=baudrate,
@@ -207,10 +210,9 @@ def connect(self): # pylint: disable=invalid-overridden-method
207210
baudrate=self.comm_params.baudrate,
208211
parity=self.comm_params.parity,
209212
)
210-
if isinstance(self.framer, ModbusRtuFramer):
211-
if self.params.strict:
212-
self.socket.interCharTimeout = self.inter_char_timeout
213-
self.last_frame_end = None
213+
if self.params.strict:
214+
self.socket.interCharTimeout = self.inter_char_timeout
215+
self.last_frame_end = None
214216
except serial.SerialException as msg:
215217
Log.error("{}", msg)
216218
self.close()

0 commit comments

Comments
 (0)