Skip to content

Commit 706938d

Browse files
authored
feat: Add reconnect cooldown (#2)
1 parent 33a4157 commit 706938d

File tree

2 files changed

+14
-3
lines changed

2 files changed

+14
-3
lines changed

tcp_modbus_aio/client.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class TCPModbusClient:
5858

5959
PING_LOOP_PERIOD: ClassVar = 1
6060
CONSECUTIVE_TIMEOUTS_TO_RECONNECT: ClassVar = 5
61+
COOLDOWN_BEFORE_RECONNECTING_SEC: ClassVar = 0.01
6162

6263
def __init__(
6364
self,
@@ -129,6 +130,9 @@ def __init__(
129130
# This way, we can avoid duplicate transaction IDs via birthday paradox.
130131
self._next_transaction_id = random.randint(0, MAX_TRANSACTION_ID)
131132

133+
# Keep track of the last time we closed a connection, to implement a cooldown before reconnecting.
134+
self._last_close_time = time.perf_counter()
135+
132136
def __repr__(self) -> str:
133137
last_ping_msg = (
134138
f"{self._last_ping*1000:.1f}ms ping"
@@ -156,6 +160,11 @@ async def _get_tcp_connection(
156160
if self._reader is not None and self._writer is not None:
157161
return self._reader, self._writer
158162

163+
cooldown_elapsed = time.perf_counter() - self._last_close_time
164+
cooldown_remaining = self.COOLDOWN_BEFORE_RECONNECTING_SEC - cooldown_elapsed
165+
if cooldown_remaining > 0:
166+
await asyncio.sleep(cooldown_remaining)
167+
159168
self._lifetime_tcp_connection_num += 1
160169

161170
if self.logger is not None:
@@ -351,6 +360,8 @@ def clear_tcp_connection(self) -> None:
351360
self._reader = None
352361
self._writer = None
353362

363+
self._last_close_time = time.perf_counter()
364+
354365
async def test_connection(
355366
self, timeout: float | None = DEFAULT_MODBUS_TIMEOUT_SEC
356367
) -> None:
@@ -521,7 +532,7 @@ async def send_modbus_message(
521532
"before mbap header, likely catching up stream after timeouts"
522533
)
523534

524-
# STEP FOUR: READ THE RESPONSE PDU
535+
# STEP FIVE: READ THE RESPONSE PDU
525536
with catchtime() as read_pdu_time:
526537
response_pdu = await asyncio.wait_for(
527538
reader.readexactly(request_function.expected_response_pdu_size),
@@ -551,7 +562,7 @@ async def send_modbus_message(
551562
except OSError as e:
552563
if self.logger is not None:
553564
self.logger.warning(
554-
f"[{self}][send_modbus_message] OSError{type(e).__name__}({e}) while sending request {msg_str}, "
565+
f"[{self}][send_modbus_message] OSError({type(e).__name__})({e}) while sending request {msg_str}, "
555566
"clearing connection"
556567
)
557568

tcp_modbus_aio/ping.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ async def ping_ip(ip: str, timeout: float = 0.5) -> float | None:
3232
ping_output_str = ping_output_str.split(" ")[6]
3333
ping_output_str = ping_output_str.split("=")[1]
3434
ping_output_latency = float(ping_output_str)
35-
return ping_output_latency
35+
return ping_output_latency / 1000
3636
except Exception:
3737
return None

0 commit comments

Comments
 (0)