Skip to content

Commit 9bca66d

Browse files
authored
Simplify client parent classes. (#1018)
1 parent 0923f4a commit 9bca66d

File tree

10 files changed

+70
-147
lines changed

10 files changed

+70
-147
lines changed

doc/source/library/pymodbus.client.rst

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
pymodbus\.client
22
================
33

4-
Pymodbus offers a :mod:`synchronous client <pymodbus.client>` and a :mod:`client based on asyncio <pymodbus.client.asynchronous>`.
4+
Pymodbus offers
55

6-
The documentation is divided in 2 parts , to keep it simple.
6+
a :mod:`synchronous client <pymodbus.client>` and
77

8-
First part is centered around the connection (establishing/terminating)
8+
a :mod:`client based on asyncio <pymodbus.client.asynchronous>`.
99

10-
Second part is centered around the calls to get/set information in the connected device.
10+
Using a client to set/get information from a device (server) is simple as seen in this
11+
example (more details in the chapters below)::
1112

13+
# create client object
14+
client = ModbusSerial("/dev/tty")
15+
16+
# connect to device
17+
client.start()
18+
19+
# set/get information
20+
client.read_coils(0x01)
21+
...
22+
23+
# disconnect device
24+
client.stop()
25+
26+
The documentation is in 2 parts:
27+
28+
- connect/disconnect to device(s) with different transport protocols.
29+
- set/get information independent of the chosen transport protocol.
1230

1331
.. toctree::
1432

doc/source/library/pymodbus.client.setup.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ Pymodbus offers different transport methods Serial/TCP/TLS/UDP, which are implem
55
as separate classes. Each class defines exactly on transport type.
66

77
Applications can add custom transport types as long as the new class inherits
8-
from class BaseModbusClient.
8+
from class BaseOldModbusClient.
99

1010
Applications can also custom decoders and customer framers.
1111

1212
All transport types are supplied in 2 versions:
13+
1314
a :mod:`synchronous client <pymodbus.client>` and
15+
1416
a :mod:`client based on asyncio <pymodbus.client.asynchronous>`.
1517

1618
Care have been made to ensure that large parts of the actual implementation or shared

pymodbus/client/async_udp.py

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -32,79 +32,10 @@ async def run():
3232
from pymodbus.factory import ClientDecoder
3333
from pymodbus.transaction import ModbusSocketFramer
3434
from pymodbus.client.helper_async import ModbusClientProtocol
35-
from pymodbus.client.helper_async import BaseModbusAsyncClientProtocol
3635

3736
_logger = logging.getLogger(__name__)
3837

3938

40-
class ModbusUdpClientProtocol( # pylint: disable=too-many-instance-attributes
41-
BaseModbusAsyncClientProtocol,
42-
asyncio.DatagramProtocol
43-
):
44-
r"""Modbus UDP client, asyncio based.
45-
46-
:param host: (positional) Host IP address
47-
:param port: (optional default 502) The serial port used for communication.
48-
:param protocol_class: (optional, default ModbusClientProtocol) Protocol communication class.
49-
:param modbus_decoder: (optional, default ClientDecoder) Message decoder class.
50-
:param framer: (optional, default ModbusSocketFramer) Framer class.
51-
:param timeout: (optional, default 3s) Timeout for a request.
52-
:param retries: (optional, default 3) Max number of retries pr request.
53-
:param retry_on_empty: (optional, default false) Retry on empty response.
54-
:param close_comm_on_error: (optional, default true) Close connection on error.
55-
:param strict: (optional, default true) Strict timing, 1.5 character between requests.
56-
:param source_address: (optional, default none) source address of client,
57-
:param \*\*kwargs: (optional) Extra experimental parameters for transport
58-
:return: client object
59-
"""
60-
61-
#: Factory that created this instance.
62-
factory = None
63-
64-
def __init__( # pylint: disable=too-many-arguments
65-
# Fixed parameters
66-
self,
67-
host,
68-
port=502,
69-
# Common optional paramers:
70-
protocol_class=ModbusClientProtocol,
71-
modbus_decoder=ClientDecoder,
72-
framer=ModbusSocketFramer,
73-
timeout=10,
74-
retries=3,
75-
retry_on_empty=False,
76-
close_comm_on_error=False,
77-
strict=True,
78-
79-
# UDP setup parameters
80-
source_address=None,
81-
82-
# Extra parameters for transport (experimental)
83-
**kwargs,
84-
):
85-
"""Initialize Asyncio Modbus TCP Client."""
86-
self.host = host
87-
self.port = port
88-
self.protocol_class = protocol_class
89-
self.framer = framer(modbus_decoder())
90-
self.timeout = timeout
91-
self.retries = retries
92-
self.retry_on_empty = retry_on_empty
93-
self.close_comm_on_error = close_comm_on_error
94-
self.strict = strict
95-
self.source_address = source_address
96-
self.kwargs = kwargs
97-
super().__init__(**kwargs)
98-
99-
def datagram_received(self, data, addr):
100-
"""Receive datagram."""
101-
self._data_received(data)
102-
103-
def write_transport(self, packet):
104-
"""Write transport."""
105-
return self.transport.sendto(packet)
106-
107-
10839
class AsyncModbusUdpClient:
10940
"""Actual Async UDP Client to be used.
11041
@@ -121,7 +52,7 @@ def __init__(
12152
self,
12253
host,
12354
port=502,
124-
protocol_class=ModbusUdpClientProtocol,
55+
protocol_class=ModbusClientProtocol,
12556
modbus_decoder=ClientDecoder,
12657
framer=ModbusSocketFramer,
12758
timeout=10,
@@ -193,7 +124,7 @@ def stop(self):
193124

194125
def _create_protocol(self, host=None, port=0):
195126
"""Create initialized protocol instance with factory function."""
196-
protocol = self.protocol_class(**self.kwargs)
127+
protocol = self.protocol_class(use_udp=True, **self.kwargs)
197128
protocol.host = host
198129
protocol.port = port
199130
protocol.factory = self

pymodbus/client/helper_async.py

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,24 @@
1111
from pymodbus.utilities import hexlify_packets
1212
from pymodbus.exceptions import ConnectionException
1313
from pymodbus.factory import ClientDecoder
14-
from pymodbus.client.sync_tcp import BaseModbusClient
14+
from pymodbus.client.sync_tcp import BaseOldModbusClient
1515
from pymodbus.transaction import ModbusSocketFramer
1616
from pymodbus.constants import Defaults
1717

1818
_logger = logging.getLogger(__name__)
1919

2020

21-
class BaseAsyncModbusClient(BaseModbusClient):
22-
"""This represents the base ModbusAsyncClient."""
23-
24-
def __init__(self, framer=None, timeout=2, **kwargs):
25-
"""Initialize framer module
26-
27-
:param framer: The framer to use for the protocol. Default:
28-
ModbusSocketFramer
29-
:type framer: pymodbus.transaction.ModbusSocketFramer
30-
"""
31-
self._connected = False
32-
self._timeout = timeout
33-
34-
super().__init__(framer or ModbusSocketFramer(ClientDecoder()), **kwargs)
35-
21+
class ModbusClientProtocol(
22+
BaseOldModbusClient,
23+
asyncio.Protocol,
24+
asyncio.DatagramProtocol
25+
):
26+
"""Asyncio specific implementation of asynchronous modbus client protocol."""
3627

37-
class AsyncModbusClientMixin(BaseAsyncModbusClient):
38-
"""Async Modbus client mixing for UDP and TCP clients."""
28+
#: Factory that created this instance.
29+
factory = None
30+
transport = None
31+
use_udp = False
3932

4033
def __init__(
4134
self,
@@ -44,6 +37,7 @@ def __init__(
4437
framer=None,
4538
source_address=None,
4639
timeout=None,
40+
use_udp=False,
4741
**kwargs
4842
):
4943
"""Initialize a Modbus TCP/UDP asynchronous client
@@ -55,20 +49,15 @@ def __init__(
5549
:param timeout: Timeout in seconds
5650
:param kwargs: Extra arguments
5751
"""
58-
super().__init__(framer=framer, **kwargs)
52+
self.use_udp = use_udp
53+
self._connected = False
54+
super().__init__(framer or ModbusSocketFramer(ClientDecoder()), **kwargs)
55+
5956
self.host = host
6057
self.port = port
6158
self.source_address = source_address or ("", 0)
6259
self._timeout = timeout if timeout is not None else Defaults.Timeout
6360

64-
65-
class BaseModbusAsyncClientProtocol(AsyncModbusClientMixin):
66-
"""Asyncio specific implementation of asynchronous modbus client protocol."""
67-
68-
#: Factory that created this instance.
69-
factory = None
70-
transport = None
71-
7261
async def execute(self, request=None): # pylint: disable=invalid-overridden-method
7362
"""Execute requests asynchronously.
7463
@@ -93,7 +82,7 @@ def connection_made(self, transport):
9382
self._connection_made()
9483

9584
if self.factory:
96-
self.factory.protocol_made_connection(self)
85+
self.factory.protocol_made_connection(self) # pylint: disable=no-member,useless-suppression
9786

9887
def connection_lost(self, reason):
9988
"""Call when the connection is lost or closed.
@@ -106,7 +95,7 @@ def connection_lost(self, reason):
10695
self._connection_lost(reason)
10796

10897
if self.factory:
109-
self.factory.protocol_lost_connection(self)
98+
self.factory.protocol_lost_connection(self) # pylint: disable=no-member,useless-suppression
11099

111100
def data_received(self, data):
112101
"""Call when some data is received.
@@ -165,6 +154,8 @@ def connected(self):
165154

166155
def write_transport(self, packet):
167156
"""Write transport."""
157+
if self.use_udp:
158+
return self.transport.sendto(packet)
168159
return self.transport.write(packet)
169160

170161
def _execute(self, request, **kwargs): # pylint: disable=unused-argument
@@ -218,19 +209,6 @@ def close(self):
218209
self.transport.close()
219210
self._connected = False
220211

221-
222-
class ModbusClientProtocol(BaseModbusAsyncClientProtocol, asyncio.Protocol):
223-
"""Asyncio specific implementation of asynchronous modbus client protocol."""
224-
225-
#: Factory that created this instance.
226-
factory = None
227-
transport = None
228-
229-
def data_received(self, data):
230-
"""Call when some data is received.
231-
232-
data is a non-empty bytes object containing the incoming data.
233-
234-
:param data:
235-
"""
212+
def datagram_received(self, data, addr):
213+
"""Receive datagram."""
236214
self._data_received(data)

pymodbus/client/helper_sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def mask_write_register(self, *args, **kwargs):
159159
return self.execute(request) # pylint: disable=no-member
160160

161161

162-
class BaseModbusClient(ModbusClientMixin):
162+
class BaseOldModbusClient(ModbusClientMixin):
163163
"""Interface for a modbus synchronous client.
164164
165165
Defined here are all the methods for performing the related

pymodbus/client/sync_serial.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def run():
3636

3737
import serial
3838

39-
from pymodbus.client.helper_sync import BaseModbusClient
39+
from pymodbus.client.helper_sync import BaseOldModbusClient
4040
from pymodbus.exceptions import ConnectionException
4141
from pymodbus.factory import ClientDecoder
4242
from pymodbus.transaction import ModbusRtuFramer
@@ -47,7 +47,7 @@ def run():
4747

4848

4949
class ModbusSerialClient(
50-
BaseModbusClient
50+
BaseOldModbusClient
5151
): # pylint: disable=too-many-instance-attributes
5252
r"""Modbus client for serial (RS-485) communication.
5353
@@ -117,7 +117,7 @@ def __init__( # pylint: disable=too-many-arguments
117117
self.kwargs = kwargs
118118

119119
self.socket = None
120-
BaseModbusClient.__init__(self, framer(ClientDecoder(), self), **kwargs)
120+
BaseOldModbusClient.__init__(self, framer(ClientDecoder(), self), **kwargs)
121121

122122
self.last_frame_end = None
123123
if isinstance(self.framer, ModbusRtuFramer):

pymodbus/client/sync_tcp.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def run():
3030
import socket
3131
import time
3232

33-
from pymodbus.client.helper_sync import BaseModbusClient
33+
from pymodbus.client.helper_sync import BaseOldModbusClient
3434
from pymodbus.exceptions import ConnectionException
3535
from pymodbus.factory import ClientDecoder
3636
from pymodbus.transaction import ModbusSocketFramer
@@ -39,7 +39,7 @@ def run():
3939
_logger = logging.getLogger(__name__)
4040

4141

42-
class ModbusTcpClient(BaseModbusClient): # pylint: disable=too-many-instance-attributes
42+
class ModbusTcpClient(BaseOldModbusClient): # pylint: disable=too-many-instance-attributes
4343
r"""Modbus client for TCP communication.
4444
4545
:param host: (positional) Host IP address
@@ -92,7 +92,7 @@ def __init__( # pylint: disable=too-many-arguments
9292
self.kwargs = kwargs
9393

9494
self.socket = None
95-
BaseModbusClient.__init__(self, framer(ClientDecoder(), self), **kwargs)
95+
BaseOldModbusClient.__init__(self, framer(ClientDecoder(), self), **kwargs)
9696

9797
def start(self):
9898
"""Connect to the modbus tcp server.

pymodbus/client/sync_udp.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def run():
2828
import logging
2929
import socket
3030

31-
from pymodbus.client.helper_sync import BaseModbusClient
31+
from pymodbus.client.helper_sync import BaseOldModbusClient
3232
from pymodbus.exceptions import ConnectionException
3333
from pymodbus.factory import ClientDecoder
3434
from pymodbus.transaction import ModbusSocketFramer
@@ -44,7 +44,7 @@ def run():
4444
# --------------------------------------------------------------------------- #
4545

4646

47-
class ModbusUdpClient(BaseModbusClient): # pylint: disable=too-many-instance-attributes
47+
class ModbusUdpClient(BaseOldModbusClient): # pylint: disable=too-many-instance-attributes
4848
r"""Modbus client for UDP communication.
4949
5050
:param host: (positional) Host IP address
@@ -97,7 +97,7 @@ def __init__( # pylint: disable=too-many-arguments
9797
self.kwargs = kwargs
9898

9999
self.socket = None
100-
BaseModbusClient.__init__(self, framer(ClientDecoder(), self), **kwargs)
100+
BaseOldModbusClient.__init__(self, framer(ClientDecoder(), self), **kwargs)
101101

102102
@classmethod
103103
def _get_address_family(cls, address):

0 commit comments

Comments
 (0)