Skip to content

Commit 1615b9e

Browse files
committed
complete test_server_task.
1 parent 5881019 commit 1615b9e

File tree

10 files changed

+300
-283
lines changed

10 files changed

+300
-283
lines changed

API_changes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
PyModbus - API changes.
33
=======================
44

5+
-------------
6+
Version 3.2.0
7+
-------------
8+
- StartAsync<type>Server, removed defer_start argument, return is None.
9+
instead of using defer_start instantiate the Modbus<type>Server directly.
10+
511
-------------
612
Version 3.1.0
713
-------------

pymodbus/client/base.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,11 @@ async def execute(self, request=None): # pylint: disable=invalid-overridden-met
297297
if self.params.broadcast_enable and not request.unit_id:
298298
resp = b"Broadcast write sent - no response expected"
299299
else:
300-
resp = await asyncio.wait_for(req, timeout=self.params.timeout)
300+
try:
301+
resp = await asyncio.wait_for(req, timeout=self.params.timeout)
302+
except asyncio.exceptions.TimeoutError:
303+
self.connection_lost("trying to send")
304+
raise
301305
return resp
302306

303307
def connection_made(self, transport):
@@ -309,30 +313,27 @@ def connection_made(self, transport):
309313
self._connection_made()
310314

311315
if self.factory:
312-
self.factory.protocol_made_connection( # pylint: disable=no-member,useless-suppression
313-
self
314-
)
316+
self.factory.protocol_made_connection(self) # pylint: disable=no-member
315317

316318
async def close(self): # pylint: disable=invalid-overridden-method
317319
"""Close connection."""
318320
if self.transport:
319321
self.transport.close()
320-
while self.transport is not None:
321-
await asyncio.sleep(0.1)
322322
self._connected = False
323323

324324
def connection_lost(self, reason):
325325
"""Call when the connection is lost or closed.
326326
327327
The argument is either an exception object or None
328328
"""
329-
self.transport = None
330-
self._connection_lost(reason)
331-
329+
if self.transport:
330+
self.transport.abort()
331+
if hasattr(self.transport, "_sock"):
332+
self.transport._sock.close() # pylint: disable=protected-access
333+
self.transport = None
332334
if self.factory:
333-
self.factory.protocol_lost_connection( # pylint: disable=no-member,useless-suppression
334-
self
335-
)
335+
self.factory.protocol_lost_connection(self) # pylint: disable=no-member
336+
self._connection_lost(reason)
336337

337338
def data_received(self, data):
338339
"""Call when some data is received.

pymodbus/client/serial.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __init__(
7070
self.params.handle_local_echo = handle_local_echo
7171
self.loop = None
7272
self._connected_event = asyncio.Event()
73+
self._reconnect_task = None
7374

7475
async def close(self): # pylint: disable=invalid-overridden-method
7576
"""Stop connection."""
@@ -81,8 +82,14 @@ async def close(self): # pylint: disable=invalid-overridden-method
8182
self.protocol.transport.close()
8283
if self.protocol:
8384
await self.protocol.close()
85+
self.protocol = None
8486
await asyncio.sleep(0.1)
8587

88+
# if there is an unfinished delayed reconnection attempt pending, cancel it
89+
if self._reconnect_task:
90+
self._reconnect_task.cancel()
91+
self._reconnect_task = None
92+
8693
def _create_protocol(self):
8794
"""Create protocol."""
8895
protocol = ModbusClientProtocol(
@@ -118,6 +125,8 @@ async def connect(self): # pylint: disable=invalid-overridden-method
118125
Log.info("Connected to {}", self.params.port)
119126
except Exception as exc: # pylint: disable=broad-except
120127
Log.warning("Failed to connect: {}", exc)
128+
if self.delay_ms > 0:
129+
self._launch_reconnect()
121130
return self.connected
122131

123132
def protocol_made_connection(self, protocol):
@@ -131,19 +140,36 @@ def protocol_made_connection(self, protocol):
131140

132141
def protocol_lost_connection(self, protocol):
133142
"""Notify lost connection."""
134-
if self.connected:
135-
Log.info("Serial lost connection.")
136-
if protocol is not self.protocol:
137-
Log.error("Serial: protocol is not self.protocol.")
138-
139-
self._connected_event.clear()
140-
if self.protocol is not None:
141-
del self.protocol
142-
self.protocol = None
143-
# if self.host:
144-
# asyncio.asynchronous(self._reconnect())
143+
Log.info("Serial lost connection.")
144+
if protocol is not self.protocol:
145+
Log.error("Serial: protocol is not self.protocol.")
146+
147+
self._connected_event.clear()
148+
if self.protocol is not None:
149+
del self.protocol
150+
self.protocol = None
151+
if self.delay_ms:
152+
self._launch_reconnect()
153+
154+
def _launch_reconnect(self):
155+
"""Launch delayed reconnection coroutine"""
156+
if self._reconnect_task:
157+
Log.warning(
158+
"Ignoring launch of delayed reconnection, another is in progress"
159+
)
145160
else:
146-
Log.error("Serial, lost_connection but not connected.")
161+
# store the future in a member variable so we know we have a pending reconnection attempt
162+
# also prevents its garbage collection
163+
self._reconnect_task = asyncio.create_task(self._reconnect())
164+
165+
async def _reconnect(self):
166+
"""Reconnect."""
167+
Log.debug("Waiting {} ms before next connection attempt.", self.delay_ms)
168+
await asyncio.sleep(self.delay_ms / 1000)
169+
self.delay_ms = min(2 * self.delay_ms, self.params.reconnect_delay_max)
170+
171+
self._reconnect_task = None
172+
return await self.connect()
147173

148174

149175
class ModbusSerialClient(ModbusBaseClient):

pymodbus/client/tcp.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def __init__(
5454
self.loop = None
5555
self.connected = False
5656
self.delay_ms = self.params.reconnect_delay
57-
self._reconnect_future = None
57+
self._reconnect_task = None
5858

5959
async def connect(self): # pylint: disable=invalid-overridden-method
6060
"""Initiate connection to start client."""
@@ -70,21 +70,20 @@ async def connect(self): # pylint: disable=invalid-overridden-method
7070

7171
async def close(self): # pylint: disable=invalid-overridden-method
7272
"""Stop client."""
73-
74-
# if there is an unfinished delayed reconnection attempt pending, cancel it
75-
if self._reconnect_future:
76-
self._reconnect_future.cancel()
77-
self._reconnect_future = None
78-
79-
# prevent reconnect:
8073
self.delay_ms = 0
8174
if self.connected:
8275
if self.protocol.transport:
76+
self.protocol.transport.abort()
8377
self.protocol.transport.close()
8478
if self.protocol:
8579
await self.protocol.close()
80+
self.protocol = None
8681
await asyncio.sleep(0.1)
8782

83+
if self._reconnect_task:
84+
self._reconnect_task.cancel()
85+
self._reconnect_task = None
86+
8887
def _create_protocol(self):
8988
"""Create initialized protocol instance with factory function."""
9089
protocol = ModbusClientProtocol(
@@ -156,22 +155,20 @@ def protocol_lost_connection(self, protocol):
156155

157156
def _launch_reconnect(self):
158157
"""Launch delayed reconnection coroutine"""
159-
if self._reconnect_future:
158+
if self._reconnect_task:
160159
Log.warning(
161160
"Ignoring launch of delayed reconnection, another is in progress"
162161
)
163162
else:
164-
# store the future in a member variable so we know we have a pending reconnection attempt
165-
# also prevents its garbage collection
166-
self._reconnect_future = asyncio.ensure_future(self._reconnect())
163+
self._reconnect_task = asyncio.create_task(self._reconnect())
167164

168165
async def _reconnect(self):
169166
"""Reconnect."""
170167
Log.debug("Waiting {} ms before next connection attempt.", self.delay_ms)
171168
await asyncio.sleep(self.delay_ms / 1000)
172169
self.delay_ms = min(2 * self.delay_ms, self.params.reconnect_delay_max)
173170

174-
self._reconnect_future = None
171+
self._reconnect_task = None
175172
return await self._connect()
176173

177174

pymodbus/client/tls.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"""Modbus client async TLS communication."""
2-
import asyncio
32
import socket
43
import ssl
54

@@ -101,7 +100,7 @@ async def _connect(self):
101100
except Exception as exc: # pylint: disable=broad-except
102101
Log.warning("Failed to connect: {}", exc)
103102
if self.delay_ms > 0:
104-
asyncio.ensure_future(self._reconnect())
103+
self._launch_reconnect()
105104
return
106105
Log.info("Connected to {}:{}.", self.params.host, self.params.port)
107106
self.reset_delay()

pymodbus/client/udp.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,18 @@ def __init__(
5050
self.params.host = host
5151
self.params.port = port
5252
self.params.source_address = source_address
53-
53+
self._reconnect_task = None
5454
self.loop = asyncio.get_event_loop()
5555
self.connected = False
5656
self.delay_ms = self.params.reconnect_delay
57+
self._reconnect_task = None
5758
self.reset_delay()
5859

5960
async def connect(self): # pylint: disable=invalid-overridden-method
6061
"""Start reconnecting asynchronous udp client.
6162
6263
:meta private:
6364
"""
64-
# force reconnect if required:
65-
host = self.params.host
66-
await self.close()
67-
self.params.host = host
68-
6965
# get current loop, if there are no loop a RuntimeError will be raised
7066
self.loop = asyncio.get_running_loop()
7167
Log.debug("Connecting to {}:{}.", self.params.host, self.params.port)
@@ -83,15 +79,20 @@ async def close(self): # pylint: disable=invalid-overridden-method
8379
8480
:meta private:
8581
"""
86-
# prevent reconnect:
8782
self.delay_ms = 0
8883
if self.connected:
8984
if self.protocol.transport:
85+
self.protocol.transport.abort()
9086
self.protocol.transport.close()
9187
if self.protocol:
9288
await self.protocol.close()
89+
self.protocol = None
9390
await asyncio.sleep(0.1)
9491

92+
if self._reconnect_task:
93+
self._reconnect_task.cancel()
94+
self._reconnect_task = None
95+
9596
def _create_protocol(self, host=None, port=0):
9697
"""Create initialized protocol instance with factory function."""
9798
protocol = ModbusClientProtocol(
@@ -127,7 +128,7 @@ async def _connect(self):
127128
return endpoint
128129
except Exception as exc: # pylint: disable=broad-except
129130
Log.warning("Failed to connect: {}", exc)
130-
asyncio.ensure_future(self._reconnect())
131+
self._reconnect_task = asyncio.ensure_future(self._reconnect())
131132

132133
def protocol_made_connection(self, protocol):
133134
"""Notify successful connection.
@@ -146,22 +147,25 @@ def protocol_lost_connection(self, protocol):
146147
147148
:meta private:
148149
"""
149-
if self.connected:
150-
Log.info("Protocol lost connection.")
151-
if protocol is not self.protocol:
152-
Log.error(
153-
"Factory protocol callback called "
154-
"from unexpected protocol instance."
155-
)
156-
157-
self.connected = False
158-
if self.protocol is not None:
159-
del self.protocol
160-
self.protocol = None
161-
if self.delay_ms > 0:
162-
asyncio.create_task(self._reconnect())
150+
Log.info("Protocol lost connection.")
151+
if protocol is not self.protocol:
152+
Log.error("Factory protocol cb from unexpected protocol instance.")
153+
154+
self.connected = False
155+
if self.protocol is not None:
156+
del self.protocol
157+
self.protocol = None
158+
if self.delay_ms > 0:
159+
self._launch_reconnect()
160+
161+
def _launch_reconnect(self):
162+
"""Launch delayed reconnection coroutine"""
163+
if self._reconnect_task:
164+
Log.warning(
165+
"Ignoring launch of delayed reconnection, another is in progress"
166+
)
163167
else:
164-
Log.error("Factory protocol connect callback called while connected.")
168+
self._reconnect_task = asyncio.create_task(self._reconnect())
165169

166170
async def _reconnect(self):
167171
"""Reconnect."""

0 commit comments

Comments
 (0)