@@ -64,8 +64,10 @@ def __init__(self, client, **kwargs):
6464 self .tid = Defaults .TransactionId
6565 self .client = client
6666 self .backoff = kwargs .get ('backoff' , Defaults .Backoff ) or 0.3
67- self .retry_on_empty = kwargs .get ('retry_on_empty' , Defaults .RetryOnEmpty )
68- self .retry_on_invalid = kwargs .get ('retry_on_invalid' , Defaults .RetryOnInvalid )
67+ self .retry_on_empty = kwargs .get ('retry_on_empty' ,
68+ Defaults .RetryOnEmpty )
69+ self .retry_on_invalid = kwargs .get ('retry_on_invalid' ,
70+ Defaults .RetryOnInvalid )
6971 self .retries = kwargs .get ('retries' , Defaults .Retries ) or 1
7072 self ._transaction_lock = RLock ()
7173 self ._no_response_devices = []
@@ -108,6 +110,25 @@ def _calculate_exception_length(self):
108110
109111 return None
110112
113+ def _validate_response (self , request , response , exp_resp_len ):
114+ """
115+ Validate Incoming response against request
116+ :param request: Request sent
117+ :param response: Response received
118+ :param exp_resp_len: Expected response length
119+ :return: New transactions state
120+ """
121+ if not response :
122+ return False
123+
124+ mbap = self .client .framer .decode_data (response )
125+ if mbap .get ('unit' ) != request .unit_id or mbap .get ('fcode' ) != request .function_code :
126+ return False
127+
128+ if 'length' in mbap and exp_resp_len :
129+ return mbap .get ('length' ) == exp_resp_len
130+ return True
131+
111132 def execute (self , request ):
112133 """ Starts the producer to send the next request to
113134 consumer.write(Frame(request))
@@ -132,6 +153,7 @@ def execute(self, request):
132153 self ._transact (request , None , broadcast = True )
133154 response = b'Broadcast write sent - no response expected'
134155 else :
156+ invalid_response = False
135157 expected_response_length = None
136158 if not isinstance (self .client .framer , ModbusSocketFramer ):
137159 if hasattr (request , "get_response_pdu_size" ):
@@ -149,35 +171,47 @@ def execute(self, request):
149171 full = True
150172 if not expected_response_length :
151173 expected_response_length = Defaults .ReadSize
152- retries += 1
174+ # retries += 1
153175 while retries > 0 :
154176 response , last_exception = self ._transact (
155177 request ,
156178 expected_response_length ,
157179 full = full ,
158180 broadcast = broadcast
159181 )
160- if not response and (
161- request .unit_id not in self ._no_response_devices ):
182+ valid_response = self ._validate_response (
183+ request , response , expected_response_length
184+ )
185+ if not response and request .unit_id \
186+ not in self ._no_response_devices :
162187 self ._no_response_devices .append (request .unit_id )
163188 elif request .unit_id in self ._no_response_devices and response :
164189 self ._no_response_devices .remove (request .unit_id )
165190 if not response and self .retry_on_empty :
166191 _logger .debug ("Retry on empty - {}" .format (retries ))
192+ retries -= 1
193+ _logger .debug ("Changing transaction state from "
194+ "'WAITING_FOR_REPLY' to 'RETRYING'" )
195+ self .client .state = ModbusTransactionState .RETRYING
196+ continue
167197 elif not response :
168198 break
169- if not self .retry_on_invalid :
170- break
171199 mbap = self .client .framer .decode_data (response )
172- if ( mbap .get ('unit' ) == request .unit_id ) :
200+ if mbap .get ('unit' ) == request .unit_id :
173201 break
174202 if ('length' in mbap and expected_response_length and
175- mbap .get ('length' ) == expected_response_length ):
203+ mbap .get ('length' ) == expected_response_length and
204+ mbap .get ('fcode' ) == request .function_code ):
205+ break
206+ else :
207+ invalid_response = True
208+ if invalid_response and not self .retry_on_invalid :
176209 break
177210 _logger .debug ("Retry on invalid - {}" .format (retries ))
178211 if hasattr (self .client , "state" ):
179- _logger .debug ("RESETTING Transaction state to 'IDLE' for retry" )
180- self .client .state = ModbusTransactionState .IDLE
212+ _logger .debug ("RESETTING Transaction "
213+ "state to 'RETRY' for retry" )
214+ self .client .state = ModbusTransactionState .RETRYING
181215 if self .backoff :
182216 delay = 2 ** (self .retries - retries ) * self .backoff
183217 time .sleep (delay )
@@ -206,14 +240,26 @@ def execute(self, request):
206240 "'TRANSACTION_COMPLETE'" )
207241 self .client .state = (
208242 ModbusTransactionState .TRANSACTION_COMPLETE )
243+ self .client .close ()
209244 return response
210245 except ModbusIOException as ex :
211246 # Handle decode errors in processIncomingPacket method
212247 _logger .exception (ex )
248+ self .client .close ()
213249 self .client .state = ModbusTransactionState .TRANSACTION_COMPLETE
214250 return ex
215251
216- def _transact (self , packet , response_length , full = False , broadcast = False ):
252+ def _retry (self , packet , response_length , full = False ):
253+ self .client .connect ()
254+ in_waiting = self .client ._in_waiting ()
255+ if in_waiting :
256+ if response_length == in_waiting :
257+ result = self ._recv (response_length , full )
258+ return result , None
259+ return self ._transact (packet , response_length , full = full )
260+
261+ def _transact (self , packet , response_length ,
262+ full = False , broadcast = False ):
217263 """
218264 Does a Write and Read transaction
219265 :param packet: packet to be sent
@@ -229,6 +275,11 @@ def _transact(self, packet, response_length, full=False, broadcast=False):
229275 if _logger .isEnabledFor (logging .DEBUG ):
230276 _logger .debug ("SEND: " + hexlify_packets (packet ))
231277 size = self ._send (packet )
278+ if isinstance (size , bytes ) and self .client .state == ModbusTransactionState .RETRYING :
279+ _logger .debug ("Changing transaction state from "
280+ "'RETRYING' to 'PROCESSING REPLY'" )
281+ self .client .state = ModbusTransactionState .PROCESSING_REPLY
282+ return size , None
232283 if broadcast :
233284 if size :
234285 _logger .debug ("Changing transaction state from 'SENDING' "
@@ -244,6 +295,7 @@ def _transact(self, packet, response_length, full=False, broadcast=False):
244295 if local_echo_packet != packet :
245296 return b'' , "Wrong local echo"
246297 result = self ._recv (response_length , full )
298+ # result2 = self._recv(response_length, full)
247299 if _logger .isEnabledFor (logging .DEBUG ):
248300 _logger .debug ("RECV: " + hexlify_packets (result ))
249301
@@ -255,7 +307,7 @@ def _transact(self, packet, response_length, full=False, broadcast=False):
255307 result = b''
256308 return result , last_exception
257309
258- def _send (self , packet ):
310+ def _send (self , packet , retrying = False ):
259311 return self .client .framer .sendPacket (packet )
260312
261313 def _recv (self , expected_response_length , full ):
0 commit comments