22import serial
33import time
44import sys
5-
5+ from functools import partial
66from pymodbus .constants import Defaults
77from pymodbus .utilities import hexlify_packets , ModbusTransactionState
88from pymodbus .factory import ClientDecoder
@@ -230,7 +230,35 @@ def _recv(self, size):
230230 """
231231 if not self .socket :
232232 raise ConnectionException (self .__str__ ())
233- return self .socket .recv (size )
233+ # socket.recv(size) waits until it gets some data from the host but
234+ # not necessarily the entire response that can be fragmented in
235+ # many packets.
236+ # To avoid the splitted responses to be recognized as invalid
237+ # messages and to be discarded, loops socket.recv until full data
238+ # is received or timeout is expired.
239+ # If timeout expires returns the read data, also if its length is
240+ # less than the expected size.
241+ self .socket .setblocking (0 )
242+ begin = time .time ()
243+
244+ data = b''
245+ if size is not None :
246+ while len (data ) < size :
247+ try :
248+ data += self .socket .recv (size - len (data ))
249+ except socket .error :
250+ pass
251+ if not self .timeout or (time .time () - begin > self .timeout ):
252+ break
253+ else :
254+ while True :
255+ try :
256+ data += self .socket .recv (1 )
257+ except socket .error :
258+ pass
259+ if not self .timeout or (time .time () - begin > self .timeout ):
260+ break
261+ return data
234262
235263 def is_socket_open (self ):
236264 return True if self .socket is not None else False
@@ -423,6 +451,16 @@ def close(self):
423451 self .socket .close ()
424452 self .socket = None
425453
454+ def _in_waiting (self ):
455+ in_waiting = ("in_waiting" if hasattr (
456+ self .socket , "in_waiting" ) else "inWaiting" )
457+
458+ if in_waiting == "in_waiting" :
459+ waitingbytes = getattr (self .socket , in_waiting )
460+ else :
461+ waitingbytes = getattr (self .socket , in_waiting )()
462+ return waitingbytes
463+
426464 def _send (self , request ):
427465 """ Sends data on the underlying socket
428466
@@ -438,13 +476,7 @@ def _send(self, request):
438476 raise ConnectionException (self .__str__ ())
439477 if request :
440478 try :
441- in_waiting = ("in_waiting" if hasattr (
442- self .socket , "in_waiting" ) else "inWaiting" )
443-
444- if in_waiting == "in_waiting" :
445- waitingbytes = getattr (self .socket , in_waiting )
446- else :
447- waitingbytes = getattr (self .socket , in_waiting )()
479+ waitingbytes = self ._in_waiting ()
448480 if waitingbytes :
449481 result = self .socket .read (waitingbytes )
450482 if _logger .isEnabledFor (logging .WARNING ):
@@ -457,6 +489,19 @@ def _send(self, request):
457489 return size
458490 return 0
459491
492+ def _wait_for_data (self ):
493+ if self .timeout is not None and self .timeout != 0 :
494+ condition = partial (lambda start , timeout : (time .time () - start ) <= timeout , timeout = self .timeout )
495+ else :
496+ condition = partial (lambda dummy1 , dummy2 : True , dummy2 = None )
497+ start = time .time ()
498+ while condition (start ):
499+ size = self ._in_waiting ()
500+ if size :
501+ break
502+ time .sleep (0.01 )
503+ return size
504+
460505 def _recv (self , size ):
461506 """ Reads data from the underlying descriptor
462507
@@ -465,6 +510,8 @@ def _recv(self, size):
465510 """
466511 if not self .socket :
467512 raise ConnectionException (self .__str__ ())
513+ if size is None :
514+ size = self ._wait_for_data ()
468515 result = self .socket .read (size )
469516 return result
470517
0 commit comments