Skip to content

Commit 67ab76a

Browse files
authored
Update transaction.py
Added retry on invalid data received and exponetial backoff delay between retries.
1 parent 89c3f25 commit 67ab76a

File tree

1 file changed

+37
-26
lines changed

1 file changed

+37
-26
lines changed

pymodbus/transaction.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import struct
77
import socket
8+
import time
89
from threading import RLock
910
from functools import partial
1011

@@ -62,7 +63,9 @@ def __init__(self, client, **kwargs):
6263
"""
6364
self.tid = Defaults.TransactionId
6465
self.client = client
66+
self.backoff = kwargs.get('backoff', Defaults.Backoff) or 0.3
6567
self.retry_on_empty = kwargs.get('retry_on_empty', Defaults.RetryOnEmpty)
68+
self.retry_on_invalid = kwargs.get('retry_on_invalid', Defaults.RetryOnInvalid)
6669
self.retries = kwargs.get('retries', Defaults.Retries) or 1
6770
self._transaction_lock = RLock()
6871
self._no_response_devices = []
@@ -146,34 +149,42 @@ def execute(self, request):
146149
full = True
147150
if not expected_response_length:
148151
expected_response_length = Defaults.ReadSize
149-
response, last_exception = self._transact(
150-
request,
151-
expected_response_length,
152-
full=full,
153-
broadcast=broadcast
154-
)
155-
if not response and (
156-
request.unit_id not in self._no_response_devices):
157-
self._no_response_devices.append(request.unit_id)
158-
elif request.unit_id in self._no_response_devices and response:
159-
self._no_response_devices.remove(request.unit_id)
160-
if not response and self.retry_on_empty and retries:
161-
while retries > 0:
162-
if hasattr(self.client, "state"):
163-
_logger.debug("RESETTING Transaction state to "
164-
"'IDLE' for retry")
165-
self.client.state = ModbusTransactionState.IDLE
166-
_logger.debug("Retry on empty - {}".format(retries))
167-
response, last_exception = self._transact(
168-
request,
169-
expected_response_length
170-
)
171-
if not response:
172-
retries -= 1
173-
continue
174-
# Remove entry
152+
retries += 1
153+
while retries > 0:
154+
response, last_exception = self._transact(
155+
request,
156+
expected_response_length,
157+
full=full,
158+
broadcast=broadcast
159+
)
160+
if not response and (
161+
request.unit_id not in self._no_response_devices):
162+
self._no_response_devices.append(request.unit_id)
163+
elif request.unit_id in self._no_response_devices and response:
175164
self._no_response_devices.remove(request.unit_id)
165+
if not response and self.retry_on_empty:
166+
_logger.debug("Retry on empty - {}".format(retries))
167+
elif not response:
168+
break
169+
if not self.retry_on_invalid:
176170
break
171+
mbap = self.client.framer.decode_data(response)
172+
if (mbap.get('unit') == request.unit_id):
173+
break
174+
if ('lenght' in mbap and expected_response_length and
175+
mbap.get('lenght') == expected_response_length):
176+
break
177+
_logger.debug("Retry on invalid - {}".format(retries))
178+
if hasattr(self.client, "state"):
179+
_logger.debug("RESETTING Transaction state to 'IDLE' for retry")
180+
self.client.state = ModbusTransactionState.IDLE
181+
if self.backoff:
182+
delay = 2 ** (self.retries - retries) * self.backoff
183+
time.sleep(delay)
184+
_logger.debug("Sleeping {}".format(delay))
185+
full = False
186+
broadcast = False
187+
retries -= 1
177188
addTransaction = partial(self.addTransaction,
178189
tid=request.transaction_id)
179190
self.client.framer.processIncomingPacket(response,

0 commit comments

Comments
 (0)