Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions pymodbus/client/sync.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import socket
import serial
import time

from pymodbus.constants import Defaults
from pymodbus.factory import ClientDecoder
Expand Down Expand Up @@ -303,6 +304,8 @@ def __init__(self, method='ascii', **kwargs):
self.parity = kwargs.get('parity', Defaults.Parity)
self.baudrate = kwargs.get('baudrate', Defaults.Baudrate)
self.timeout = kwargs.get('timeout', Defaults.Timeout)
self._last_frame_end = 0.0
self._silent_interval = 3.5 * (1 + 8 + 2) / self.baudrate

@staticmethod
def __implementation(method):
Expand Down Expand Up @@ -331,6 +334,7 @@ def connect(self):
except serial.SerialException, msg:
_logger.error(msg)
self.close()
self._last_frame_end = time.time()
return self.socket != None

def close(self):
Expand All @@ -343,13 +347,34 @@ def close(self):
def _send(self, request):
''' Sends data on the underlying socket

If receive buffer still holds some data then flush it.

Sleep if last send finished less than 3.5 character
times ago.

:param request: The encoded request to send
:return: The number of bytes written
'''
if not self.socket:
raise ConnectionException(self.__str__())
if request:
return self.socket.write(request)
ts = time.time()
if ts < self._last_frame_end + self._silent_interval:
_logger.debug("will sleep to wait for 3.5 char")
time.sleep(self._last_frame_end + self._silent_interval - ts)

try:
waitingbytes = self.socket.inWaiting()
if waitingbytes:
result = self.socket.read(waitingbytes)
if _logger.isEnabledFor(logging.WARNING):
_logger.warning("cleanup recv buffer before send: " + " ".join([hex(ord(x)) for x in result]))
except NotImplementedError:
pass

size = self.socket.write(request)
self._last_frame_end = time.time()
return size
return 0

def _recv(self, size):
Expand All @@ -360,7 +385,9 @@ def _recv(self, size):
'''
if not self.socket:
raise ConnectionException(self.__str__())
return self.socket.read(size)
result = self.socket.read(size)
self._last_frame_end = time.time()
return result

def __str__(self):
''' Builds a string representation of the connection
Expand Down
8 changes: 8 additions & 0 deletions pymodbus/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ def buildPacket(self, message):
raise NotImplementedException(
"Method not implemented by derived class")

def getResponseSize(self, message):
''' Returns expected packet size of response for this request

:returns: The expected packet size
'''
raise NotImplementedException(
"Method not implemented by derived class")


class IModbusSlaveContext(object):
'''
Expand Down
7 changes: 7 additions & 0 deletions pymodbus/pdu.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ def doException(self, exception):
(self.function_code, exception))
return ExceptionResponse(self.function_code, exception)

def getResponseSize(self):
''' Returns expected packet size of response for this request

:raises: A not implemented exception
'''
raise NotImplementedException()


class ModbusResponse(ModbusPDU):
''' Base class for a modbus response PDU
Expand Down
7 changes: 7 additions & 0 deletions pymodbus/register_read_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ def decode(self, data):
'''
self.address, self.count = struct.unpack('>HH', data)

def getResponseSize(self):
''' Returns expected packet size of response for this request

:returns: The expected packet size
'''
return 1 + 2 * self.count

def __str__(self):
''' Returns a string representation of the instance

Expand Down
14 changes: 14 additions & 0 deletions pymodbus/register_write_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ def execute(self, context):
values = context.getValues(self.function_code, self.address, 1)
return WriteSingleRegisterResponse(self.address, values[0])

def getResponseSize(self):
''' Returns expected packet size of response for this request

:returns: The expected packet size
'''
return 2 + 2

def __str__(self):
''' Returns a string representation of the instance

Expand Down Expand Up @@ -181,6 +188,13 @@ def execute(self, context):
context.setValues(self.function_code, self.address, self.values)
return WriteMultipleRegistersResponse(self.address, self.count)

def getResponseSize(self):
''' Returns expected packet size of response for this request

:returns: The expected packet size
'''
return 2 + 2

def __str__(self):
''' Returns a string representation of the instance

Expand Down
27 changes: 22 additions & 5 deletions pymodbus/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import socket
from binascii import b2a_hex, a2b_hex

from pymodbus.exceptions import ModbusIOException
from pymodbus.exceptions import ModbusIOException, NotImplementedException
from pymodbus.constants import Defaults
from pymodbus.interfaces import IModbusFramer
from pymodbus.utilities import checkCRC, computeCRC
Expand Down Expand Up @@ -62,10 +62,19 @@ def execute(self, request):
try:
self.client.connect()
self.client._send(self.client.framer.buildPacket(request))
# I need to fix this to read the header and the result size,
# as this may not read the full result set, but right now
# it should be fine...
result = self.client._recv(1024)
try:
recvsize = self.client.framer.getResponseSize(request)
except NotImplementedException:
recvsize = 0;

# TODO: read first only as much as needed to check Modbus
# exception and then all remaining bytes. Otherwise
# exception always takes _timeout_ seconds.
if recvsize:
result = self.client._recv(recvsize)
else:
result = self.client._recv(1024)

if not result and self.retry_on_empty:
retries -= 1
continue
Expand Down Expand Up @@ -553,6 +562,14 @@ def buildPacket(self, message):
packet += struct.pack(">H", computeCRC(packet))
return packet

def getResponseSize(self, message):
''' Returns expected packet size of response for request

:param message: Request message
:returns: The expected packet size
'''
return 1 + 1 + message.getResponseSize() + 2


#---------------------------------------------------------------------------#
# Modbus ASCII Message
Expand Down