[Python-modules-commits] [pymodbus] 01/03: import new upstream version 1.3.2

Wolfgang Borgert debacle at moszumanska.debian.org
Sun Aug 27 22:26:42 UTC 2017


This is an automated email from the git hooks/post-receive script.

debacle pushed a commit to branch master
in repository pymodbus.

commit 3875bc950a323e334bb9175445992e15ef355cc2
Author: W. Martin Borgert <debacle at debian.org>
Date:   Mon Aug 28 00:09:21 2017 +0200

    import new upstream version 1.3.2
---
 .gitignore                               |  2 +-
 CHANGELOG.rst                            |  9 ++++
 README.rst                               |  5 +-
 examples/common/callback-server.py       |  6 ++-
 examples/common/device-mapping           |  2 +
 examples/common/modbus-payload-server.py |  8 +--
 examples/common/modbus-payload.py        | 15 ++++--
 examples/common/performance.py           | 11 ++--
 pymodbus/client/sync.py                  |  5 +-
 pymodbus/diag_message.py                 | 40 ++++++++++++--
 pymodbus/exceptions.py                   | 14 ++---
 pymodbus/other_message.py                |  2 +-
 pymodbus/payload.py                      |  8 +++
 pymodbus/server/sync.py                  | 92 ++++++++++++++++++--------------
 pymodbus/transaction.py                  | 88 +++++++++++++++++++++++-------
 pymodbus/version.py                      |  3 +-
 test/test_client_sync.py                 |  7 +++
 test/test_diag_messages.py               | 16 +++---
 test/test_other_messages.py              |  5 +-
 test/test_payload.py                     | 10 +++-
 test/test_server_sync.py                 | 27 ++++++----
 21 files changed, 266 insertions(+), 109 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3998172..3e5d76b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,7 @@ pymodbus.egg-info/
 .tox/
 doc/api/epydoc/html/
 .vscode/
-
+.venv
 __pycache__/
 pymodbus/__pycache__/
 pymodbus/client/__pycache__/
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5ebc9db..1297df4 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,4 +1,13 @@
 
+Version 1.3.2
+------------------------------------------------------------
+* ModbusSerialServer could now be stopped when running on a seperate thread.
+* Fix issue with server and client where in the frame buffer had values from previous unsuccesful transaction
+* Fix response length calculation for ModbusASCII protocol
+* Fix response length calculation ReportSlaveIdResponse, DiagnosticStatusResponse
+* Fix never ending transaction case when response is recieved without header and CRC
+* Fix tests
+
 Version 1.3.1
 ------------------------------------------------------------
 * Recall socket recv until get a complete response
diff --git a/README.rst b/README.rst
index afb4e07..a41f14f 100644
--- a/README.rst
+++ b/README.rst
@@ -125,7 +125,6 @@ like your device tested, I accept devices via mail or by IP address.
 That said, the current work mainly involves polishing the library as
 I get time doing such tasks as:
 
-  * Add CI support
   * Make PEP-8 compatible and flake8 ready
   * Fixing bugs/feature requests
   * Architecture documentation
@@ -152,6 +151,10 @@ Use make to perform a range of activities
      make tox        run the tests on all Python versions
      make clean      cleanup all temporary files 
 
+------------------------------------------------------------
+Contributing
+------------------------------------------------------------
+Just fork the repo and raise your PR against `dev` branch.
 
 ------------------------------------------------------------
 License Information
diff --git a/examples/common/callback-server.py b/examples/common/callback-server.py
index 374787b..572b138 100755
--- a/examples/common/callback-server.py
+++ b/examples/common/callback-server.py
@@ -16,6 +16,7 @@ from pymodbus.datastore import ModbusSparseDataBlock
 from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
 from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
 
+
 #---------------------------------------------------------------------------# 
 # import the python libraries we need
 #---------------------------------------------------------------------------# 
@@ -31,7 +32,8 @@ log.setLevel(logging.DEBUG)
 
 #---------------------------------------------------------------------------# 
 # create your custom data block with callbacks
-#---------------------------------------------------------------------------# 
+#---------------------------------------------------------------------------#
+
 class CallbackDataBlock(ModbusSparseDataBlock):
     ''' A datablock that stores the new value in memory
     and passes the operation to a message queue for further
@@ -44,7 +46,7 @@ class CallbackDataBlock(ModbusSparseDataBlock):
         self.devices = devices
         self.queue = queue
 
-        values = {k:0 for k in devices.iterkeys()}
+        values = {k:0 for k in devices.keys()}
         values[0xbeef] = len(values) # the number of devices
         super(CallbackDataBlock, self).__init__(values)
 
diff --git a/examples/common/device-mapping b/examples/common/device-mapping
new file mode 100644
index 0000000..dc7c8b8
--- /dev/null
+++ b/examples/common/device-mapping
@@ -0,0 +1,2 @@
+0x0001,/dev/ptyp0
+0x0002,/dev/ptyp1
\ No newline at end of file
diff --git a/examples/common/modbus-payload-server.py b/examples/common/modbus-payload-server.py
index 3c6d195..b2b55eb 100755
--- a/examples/common/modbus-payload-server.py
+++ b/examples/common/modbus-payload-server.py
@@ -35,10 +35,10 @@ log.setLevel(logging.DEBUG)
 # build your payload
 #---------------------------------------------------------------------------# 
 builder = BinaryPayloadBuilder(endian=Endian.Little)
-builder.add_string('abcdefgh')
-builder.add_32bit_float(22.34)
-builder.add_16bit_uint(0x1234)
-builder.add_8bit_int(0x12)
+# builder.add_string('abcdefgh')
+# builder.add_32bit_float(22.34)
+# builder.add_16bit_uint(4660)
+# builder.add_8bit_int(18)
 builder.add_bits([0,1,0,1,1,0,1,0])
 
 #---------------------------------------------------------------------------# 
diff --git a/examples/common/modbus-payload.py b/examples/common/modbus-payload.py
index 03dbad3..cd34938 100755
--- a/examples/common/modbus-payload.py
+++ b/examples/common/modbus-payload.py
@@ -9,6 +9,7 @@ from pymodbus.constants import Endian
 from pymodbus.payload import BinaryPayloadDecoder
 from pymodbus.payload import BinaryPayloadBuilder
 from pymodbus.client.sync import ModbusTcpClient as ModbusClient
+from pymodbus.compat import iteritems
 
 #---------------------------------------------------------------------------# 
 # configure the client logging
@@ -34,6 +35,7 @@ client.connect()
 # - a 8 byte string 'abcdefgh'
 # - a 32 bit float 22.34
 # - a 16 bit unsigned int 0x1234
+# - another 16 bit unsigned int 0x5678
 # - an 8 bit int 0x12
 # - an 8 bit bitstring [0,1,0,1,1,0,1,0]
 #---------------------------------------------------------------------------# 
@@ -41,6 +43,7 @@ builder = BinaryPayloadBuilder(endian=Endian.Big)
 builder.add_string('abcdefgh')
 builder.add_32bit_float(22.34)
 builder.add_16bit_uint(0x1234)
+builder.add_16bit_uint(0x5678)
 builder.add_8bit_int(0x12)
 builder.add_bits([0,1,0,1,1,0,1,0])
 payload = builder.build()
@@ -57,6 +60,7 @@ result  = client.write_registers(address, payload, skip_encode=True, unit=1)
 # - a 8 byte string 'abcdefgh'
 # - a 32 bit float 22.34
 # - a 16 bit unsigned int 0x1234
+# - another 16 bit unsigned int which we will ignore
 # - an 8 bit int 0x12
 # - an 8 bit bitstring [0,1,0,1,1,0,1,0]
 #---------------------------------------------------------------------------# 
@@ -68,15 +72,16 @@ decoded = {
     'string': decoder.decode_string(8),
     'float': decoder.decode_32bit_float(),
     '16uint': decoder.decode_16bit_uint(),
+    'ignored': decoder.skip_bytes(2),
     '8int': decoder.decode_8bit_int(),
     'bits': decoder.decode_bits(),
 }
 
-print "-" * 60
-print "Decoded Data"
-print "-" * 60
-for name, value in decoded.iteritems():
-    print ("%s\t" % name), value
+print("-" * 60)
+print("Decoded Data")
+print("-" * 60)
+for name, value in iteritems(decoded):
+    print ("%s\t" % name, value)
 
 #---------------------------------------------------------------------------# 
 # close the client
diff --git a/examples/common/performance.py b/examples/common/performance.py
index 36ce11e..0a193d9 100755
--- a/examples/common/performance.py
+++ b/examples/common/performance.py
@@ -13,7 +13,7 @@ from __future__ import print_function
 import logging, os
 from time import time
 from multiprocessing import log_to_stderr
-from pymodbus.client.sync import ModbusTcpClient
+# from pymodbus.client.sync import ModbusTcpClient
 from pymodbus.client.sync import ModbusSerialClient
 
 #---------------------------------------------------------------------------# 
@@ -61,9 +61,10 @@ def single_client_test(host, cycles):
         client = ModbusSerialClient(method="rtu", port="/dev/ttyp0", baudrate=9600)
         while count < cycles:
             with _thread_lock:
-                result = client.read_holding_registers(10, 1, unit=1).getRegister(0)
+                client.read_holding_registers(10, 1, unit=1).registers[0]
                 count += 1
-    except: logger.exception("failed to run test successfully")
+    except:
+        logger.exception("failed to run test successfully")
     logger.debug("finished worker: %d" % os.getpid())
 
 #---------------------------------------------------------------------------# 
@@ -73,6 +74,10 @@ def single_client_test(host, cycles):
 # threads that was specified. We then start all the threads and block on
 # them to finish. This may need to switch to another mechanism to signal
 # finished as the process/thread start up/shut down may skew the test a bit.
+
+# RTU 32 requests/second @9600
+# TCP 31430 requests/second
+
 #---------------------------------------------------------------------------# 
 args  = (host, int(cycles * 1.0 / workers))
 procs = [Worker(target=single_client_test, args=args) for _ in range(workers)]
diff --git a/pymodbus/client/sync.py b/pymodbus/client/sync.py
index 7f85116..80946b7 100644
--- a/pymodbus/client/sync.py
+++ b/pymodbus/client/sync.py
@@ -308,7 +308,10 @@ class ModbusSerialClient(BaseModbusClient):
         self.timeout  = kwargs.get('timeout',  Defaults.Timeout)
         if self.method == "rtu":
             self._last_frame_end = 0.0
-            self._silent_interval = 3.5 * (1 + 8 + 2) / self.baudrate
+            if self.baudrate > 19200:
+                self._silent_interval = 1.75/1000  # ms
+            else:
+                self._silent_interval = 3.5 * (1 + 8 + 2) / self.baudrate
 
     @staticmethod
     def __implementation(method):
diff --git a/pymodbus/diag_message.py b/pymodbus/diag_message.py
index ead3d36..f6c02cb 100644
--- a/pymodbus/diag_message.py
+++ b/pymodbus/diag_message.py
@@ -74,7 +74,6 @@ class DiagnosticStatusRequest(ModbusRequest):
         return 1 + 2 + 2 * len(self.message)
 
 
-
 class DiagnosticStatusResponse(ModbusResponse):
     '''
     This is a base class for all of the diagnostic response functions
@@ -118,7 +117,11 @@ class DiagnosticStatusResponse(ModbusResponse):
 
         :param data: The data to decode into the function code
         '''
-        self.sub_function_code, self.message = struct.unpack('>HH', data)
+        word_len = len(data)//2
+        if len(data) % 2:
+            word_len += 1
+        data = struct.unpack('>' + 'H'*word_len, data)
+        self.sub_function_code, self.message = data[0], data[1:]
 
 
 class DiagnosticStatusSimpleRequest(DiagnosticStatusRequest):
@@ -708,6 +711,23 @@ class GetClearModbusPlusRequest(DiagnosticStatusSimpleRequest):
     '''
     sub_function_code = 0x0015
 
+    def __init__(self, **kwargs):
+        super(GetClearModbusPlusRequest, self).__init__(**kwargs)
+
+    def get_response_pdu_size(self):
+        """
+        Returns a series of 54 16-bit words (108 bytes) in the data field of the response
+        (this function differs from the usual two-byte length of the data field). The data
+        contains the statistics for the Modbus Plus peer processor in the slave device.
+        Func_code (1 byte) + Sub function code (2 byte) + Operation (2 byte) + Data (108 bytes)
+        :return:
+        """
+        if self.message == ModbusPlusOperation.GetStatistics:
+            data = 2 + 108 # byte count(2) + data (54*2)
+        else:
+            data = 0
+        return 1 + 2 + 2 + 2+ data
+
     def execute(self, *args):
         ''' Execute the diagnostic request on the given device
 
@@ -716,9 +736,23 @@ class GetClearModbusPlusRequest(DiagnosticStatusSimpleRequest):
         message = None # the clear operation does not return info
         if self.message == ModbusPlusOperation.ClearStatistics:
             _MCB.Plus.reset()
-        else: message = _MCB.Plus.encode()
+            message = self.message
+        else:
+            message = [self.message]
+            message += _MCB.Plus.encode()
         return GetClearModbusPlusResponse(message)
 
+    def encode(self):
+        '''
+        Base encoder for a diagnostic response
+        we encode the data set in self.message
+
+        :returns: The encoded packet
+        '''
+        packet = struct.pack('>H', self.sub_function_code)
+        packet += struct.pack('>H', self.message)
+        return packet
+
 
 class GetClearModbusPlusResponse(DiagnosticStatusSimpleResponse):
     '''
diff --git a/pymodbus/exceptions.py b/pymodbus/exceptions.py
index c143569..a2ad482 100644
--- a/pymodbus/exceptions.py
+++ b/pymodbus/exceptions.py
@@ -78,19 +78,19 @@ class ConnectionException(ModbusException):
         ModbusException.__init__(self, message)
 
 
-class InvalidResponseRecievedException(ModbusException):
+class InvalidMessageRecievedException(ModbusException):
     """
     Error resulting from invalid response received or decoded
     """
 
+    def __init__(self, string=""):
+        ''' Initialize the exception
 
-def __init__(self, string=""):
-    ''' Initialize the exception
+        :param string: The message to append to the error
+        '''
+        message = "[Invalid Message] %s" % string
+        ModbusException.__init__(self, message)
 
-    :param string: The message to append to the error
-    '''
-    message = "[Invalid Response] %s" % string
-    ModbusException.__init__(self, message)
 
 #---------------------------------------------------------------------------#
 # Exported symbols
diff --git a/pymodbus/other_message.py b/pymodbus/other_message.py
index 93c6cfb..b30ea75 100644
--- a/pymodbus/other_message.py
+++ b/pymodbus/other_message.py
@@ -403,7 +403,7 @@ class ReportSlaveIdResponse(ModbusResponse):
             status = ModbusStatus.SlaveOn
         else:
             status = ModbusStatus.SlaveOff
-        length  = len(self.identifier) + 2
+        length  = len(self.identifier) + 1
         packet  = int2byte(length)
         packet += self.identifier  # we assume it is already encoded
         packet += int2byte(status)
diff --git a/pymodbus/payload.py b/pymodbus/payload.py
index a34a10f..cbc7221 100644
--- a/pymodbus/payload.py
+++ b/pymodbus/payload.py
@@ -349,6 +349,14 @@ class BinaryPayloadDecoder(object):
         self._pointer += size
         return self._payload[self._pointer - size:self._pointer]
 
+    def skip_bytes(self, nbytes):
+        ''' Skip n bytes in the buffer
+
+        :param nbytes: The number of bytes to skip
+        '''
+        self._pointer += nbytes
+        return None
+
 #---------------------------------------------------------------------------#
 # Exported Identifiers
 #---------------------------------------------------------------------------#
diff --git a/pymodbus/server/sync.py b/pymodbus/server/sync.py
index 55d3180..4559513 100644
--- a/pymodbus/server/sync.py
+++ b/pymodbus/server/sync.py
@@ -28,6 +28,7 @@ _logger = logging.getLogger(__name__)
 #---------------------------------------------------------------------------#
 # Protocol Handlers
 #---------------------------------------------------------------------------#
+
 class ModbusBaseRequestHandler(socketserver.BaseRequestHandler):
     ''' Implements the modbus server protocol
 
@@ -85,34 +86,6 @@ class ModbusBaseRequestHandler(socketserver.BaseRequestHandler):
         raise NotImplementedException("Method not implemented by derived class")
 
 
-# class ModbusSingleRequestHandler(ModbusBaseRequestHandler):
-#     ''' Implements the modbus server protocol
-#
-#     This uses the socketserver.BaseRequestHandler to implement
-#     the client handler for a single client(serial clients)
-#     '''
-#
-#     def handle(self):
-#         ''' Callback when we receive any data
-#         '''
-#         while self.running:
-#             try:
-#                 data = self.request.recv(1024)
-#                 if data:
-#                     if _logger.isEnabledFor(logging.DEBUG):
-#                         _logger.debug(' '.join([hex(byte2int(x)) for x in data]))
-#                     self.framer.processIncomingPacket(data, self.execute)
-#             except Exception as msg:
-#                 # since we only have a single socket, we cannot exit
-#                 _logger.error("Socket error occurred %s" % msg)
-#
-#     def send(self, message):
-#         ''' Send a request (string) to the network
-#
-#         :param message: The unencoded modbus response
-#         '''
-
-
 class ModbusSingleRequestHandler(ModbusBaseRequestHandler):
     ''' Implements the modbus server protocol
 
@@ -132,6 +105,8 @@ class ModbusSingleRequestHandler(ModbusBaseRequestHandler):
                     self.framer.processIncomingPacket(data, self.execute)
             except Exception as msg:
                 # since we only have a single socket, we cannot exit
+                # Clear frame buffer
+                self.framer.resetFrame()
                 _logger.error("Socket error occurred %s" % msg)
 
     def send(self, message):
@@ -147,6 +122,16 @@ class ModbusSingleRequestHandler(ModbusBaseRequestHandler):
             return self.request.send(pdu)
 
 
+class CustomSingleRequestHandler(ModbusSingleRequestHandler):
+
+    def __init__(self, request, client_address, server):
+        self.request = request
+        self.client_address = client_address
+        self.server = server
+        self.running = True
+        self.setup()
+
+
 class ModbusConnectedRequestHandler(ModbusBaseRequestHandler):
     ''' Implements the modbus server protocol
 
@@ -167,6 +152,7 @@ class ModbusConnectedRequestHandler(ModbusBaseRequestHandler):
         to supply the alternative request handler class.
 
         '''
+        reset_frame = False
         while self.running:
             try:
                 data = self.request.recv(1024)
@@ -178,13 +164,18 @@ class ModbusConnectedRequestHandler(ModbusBaseRequestHandler):
             except socket.timeout as msg:
                 if _logger.isEnabledFor(logging.DEBUG):
                     _logger.debug("Socket timeout occurred %s", msg)
-                pass
+                reset_frame = True
             except socket.error as msg:
                 _logger.error("Socket error occurred %s" % msg)
                 self.running = False
             except:
                 _logger.error("Socket exception occurred %s" % traceback.format_exc() )
                 self.running = False
+                reset_frame = True
+            finally:
+                if reset_frame:
+                    self.framer.resetFrame()
+                    reset_frame = False
 
     def send(self, message):
         ''' Send a request (string) to the network
@@ -211,10 +202,12 @@ class ModbusDisconnectedRequestHandler(ModbusBaseRequestHandler):
     def handle(self):
         ''' Callback when we receive any data
         '''
+        reset_frame = False
         while self.running:
             try:
                 data, self.request = self.request
-                if not data: self.running = False
+                if not data:
+                    self.running = False
                 if _logger.isEnabledFor(logging.DEBUG):
                     _logger.debug(' '.join([hex(byte2int(x)) for x in data]))
                 # if not self.server.control.ListenOnly:
@@ -223,7 +216,15 @@ class ModbusDisconnectedRequestHandler(ModbusBaseRequestHandler):
             except socket.error as msg:
                 _logger.error("Socket error occurred %s" % msg)
                 self.running = False
-            except: self.running = False
+                reset_frame = True
+            except Exception as msg:
+                _logger.error(msg)
+                self.running = False
+                reset_frame = True
+            finally:
+                if reset_frame:
+                    self.framer.resetFrame()
+                    reset_frame = False
 
     def send(self, message):
         ''' Send a request (string) to the network
@@ -368,6 +369,8 @@ class ModbusSerialServer(object):
     server context instance.
     '''
 
+    handler = None
+
     def __init__(self, context, framer=None, identity=None, **kwargs):
         ''' Overloaded initializer for the socket server
 
@@ -402,8 +405,9 @@ class ModbusSerialServer(object):
         self.timeout  = kwargs.get('timeout',  Defaults.Timeout)
         self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves)
         self.socket   = None
-        self._connect()
-        self.is_running = True
+        if self._connect():
+            self.is_running = True
+            self._build_handler()
 
     def _connect(self):
         ''' Connect to the serial server
@@ -425,12 +429,13 @@ class ModbusSerialServer(object):
 
         :returns: A patched handler
         '''
+
         request = self.socket
         request.send = request.write
         request.recv = request.read
-        handler = ModbusSingleRequestHandler(request,
-            (self.device, self.device), self)
-        return handler
+        self.handler = CustomSingleRequestHandler(request,
+                                                  (self.device, self.device),
+                                                  self)
 
     def serve_forever(self):
         ''' Callback for connecting a new client thread
@@ -438,16 +443,23 @@ class ModbusSerialServer(object):
         :param request: The request to handle
         :param client: The address of the client
         '''
-        _logger.debug("Started thread to serve client")
-        handler = self._build_handler()
-        while self.is_running:
-            handler.handle()
+        if self._connect():
+            _logger.debug("Started thread to serve client")
+            if not self.handler:
+                self._build_handler()
+            while self.is_running:
+                self.handler.handle()
+        else:
+            _logger.error("Error opening serial port , Unable to start server!!")
 
     def server_close(self):
         ''' Callback for stopping the running server
         '''
         _logger.debug("Modbus server stopped")
         self.is_running = False
+        self.handler.finish()
+        self.handler.running = False
+        self.handler = None
         self.socket.close()
 
 
diff --git a/pymodbus/transaction.py b/pymodbus/transaction.py
index 9b54697..a70c268 100644
--- a/pymodbus/transaction.py
+++ b/pymodbus/transaction.py
@@ -7,7 +7,7 @@ import socket
 from binascii import b2a_hex, a2b_hex
 
 from pymodbus.exceptions import ModbusIOException, NotImplementedException
-from pymodbus.exceptions import InvalidResponseRecievedException
+from pymodbus.exceptions import InvalidMessageRecievedException
 from pymodbus.constants  import Defaults
 from pymodbus.interfaces import IModbusFramer
 from pymodbus.utilities  import checkCRC, computeCRC
@@ -61,7 +61,7 @@ class ModbusTransactionManager(object):
         elif isinstance(self.client.framer, ModbusRtuFramer):
             self.base_adu_size = 3 # address(1), CRC(2)
         elif isinstance(self.client.framer, ModbusAsciiFramer):
-            self.base_adu_size = 4 # Address(2), LRC(2)
+            self.base_adu_size = 7 # start(1)+ Address(2), LRC(2) + end(2)
         elif isinstance(self.client.framer, ModbusBinaryFramer):
             self.base_adu_size = 3 #, Address(1), CRC(2)
         else:
@@ -95,7 +95,8 @@ class ModbusTransactionManager(object):
         elif isinstance(self.client.framer, ModbusAsciiFramer):
             if len(response) >= 5 and int(response[3:5], 16) > 128:
                 return False
-        elif isinstance(self.client.framer, (ModbusRtuFramer, ModbusBinaryFramer)):
+        elif isinstance(self.client.framer, (ModbusRtuFramer,
+                                             ModbusBinaryFramer)):
             if len(response) >= 2 and byte2int(response[1]) > 128:
                 return False
 
@@ -108,9 +109,12 @@ class ModbusTransactionManager(object):
         retries = self.retries
         request.transaction_id = self.getNextTID()
         _logger.debug("Running transaction %d" % request.transaction_id)
+        self.client.framer.resetFrame()
         expected_response_length = None
         if hasattr(request, "get_response_pdu_size"):
             response_pdu_size = request.get_response_pdu_size()
+            if isinstance(self.client.framer, ModbusAsciiFramer):
+                response_pdu_size = response_pdu_size * 2
             if response_pdu_size:
                 expected_response_length = self._calculate_response_length(response_pdu_size)
 
@@ -121,15 +125,9 @@ class ModbusTransactionManager(object):
                 packet = self.client.framer.buildPacket(request)
                 if _logger.isEnabledFor(logging.DEBUG):
                     _logger.debug("send: " + " ".join([hex(byte2int(x)) for x in packet]))
-                self.client._send(packet)
-                exception = False
-                result = self.client._recv(expected_response_length or 1024)
-                while result and expected_response_length and len(result) < expected_response_length:
-                    if not exception and not self._check_response(result):
-                        exception = True
-                        expected_response_length = self._calculate_exception_length()
-                        continue
-                    result += self.client._recv(expected_response_length - len(result))
+                self._send(packet)
+                # exception = False
+                result = self._recv(expected_response_length or 1024)
 
                 if not result and self.retry_on_empty:
                     retries -= 1
@@ -139,19 +137,49 @@ class ModbusTransactionManager(object):
                     _logger.debug("recv: " + " ".join([hex(byte2int(x)) for x in result]))
                 self.client.framer.processIncomingPacket(result, self.addTransaction)
                 break
-            except (socket.error, ModbusIOException, InvalidResponseRecievedException) as msg:
+            except (socket.error, ModbusIOException, InvalidMessageRecievedException) as msg:
                 self.client.close()
                 _logger.debug("Transaction failed. (%s) " % msg)
                 retries -= 1
                 last_exception = msg
         response = self.getTransaction(request.transaction_id)
         if not response:
-            last_exception = last_exception or ("No Response "
-                                                "received from the remote unit")
-            response = ModbusIOException(last_exception)
+            if len(self.transactions):
+                response = self.getTransaction(tid=0)
+            else:
+                last_exception = last_exception or ("No Response "
+                                                    "received from the remote unit")
+                response = ModbusIOException(last_exception)
 
         return response
 
+    def _send(self, packet):
+        return self.client._send(packet)
+
+    def _recv(self, expected_response_length):
+        retries = self.retries
+        exception = False
+        while retries:
+            result = self.client._recv(expected_response_length or 1024)
+            while result and expected_response_length and len(
+                    result) < expected_response_length:
+                if not exception and not self._check_response(result):
+                    exception = True
+                    expected_response_length = self._calculate_exception_length()
+                    continue
+                if isinstance(self.client.framer, ModbusSocketFramer):
+                    break
+                r = self.client._recv(expected_response_length - len(result))
+                if not r:
+                    # If no response being recived there is no point in conitnuing
+                    break
+                result += r
+            if result:
+                break
+            retries -= 1
+        return result
+
+
     def addTransaction(self, request, tid=None):
         ''' Adds a transaction to the handler
 
@@ -439,7 +467,7 @@ class ModbusSocketFramer(IModbusFramer):
         if result is None:
             raise ModbusIOException("Unable to decode request")
         elif error and result.function_code < 0x80:
-            raise InvalidResponseRecievedException(result)
+            raise InvalidMessageRecievedException(result)
         else:
             self.populateResult(result)
             self.advanceFrame()
@@ -453,7 +481,7 @@ class ModbusSocketFramer(IModbusFramer):
         end of the message (python just doesn't have the resolution to
         check for millisecond delays).
         '''
-        self.__buffer = ''
+        self.__buffer = b''
         self.__header = {}
 
     def getRawFrame(self):
@@ -680,7 +708,7 @@ class ModbusRtuFramer(IModbusFramer):
         if result is None:
             raise ModbusIOException("Unable to decode request")
         elif error and result.function_code < 0x80:
-            raise InvalidResponseRecievedException(result)
+            raise InvalidMessageRecievedException(result)
         else:
             self.populateResult(result)
             self.advanceFrame()
@@ -786,6 +814,17 @@ class ModbusAsciiFramer(IModbusFramer):
         if end > 0: return a2b_hex(buffer)
         return b''
 
+    def resetFrame(self):
+        ''' Reset the entire message frame.
+        This allows us to skip ovver errors that may be in the stream.
+        It is hard to know if we are simply out of sync or if there is
+        an error in the stream as we have no way to check the start or
+        end of the message (python just doesn't have the resolution to
+        check for millisecond delays).
+        '''
+        self.__buffer = b''
+        self.__header = {'lrc':'0000', 'len':0, 'uid':0x00}
+
     def populateResult(self, result):
         ''' Populates the modbus result header
 
@@ -1018,6 +1057,17 @@ class ModbusBinaryFramer(IModbusFramer):
             array.append(d)
         return bytes(array)
 
+    def resetFrame(self):
+        ''' Reset the entire message frame.
+        This allows us to skip ovver errors that may be in the stream.
+        It is hard to know if we are simply out of sync or if there is
+        an error in the stream as we have no way to check the start or
+        end of the message (python just doesn't have the resolution to
+        check for millisecond delays).
+        '''
+        self.__buffer = b''
+        self.__header = {'crc': 0x0000, 'len': 0, 'uid': 0x00}
+
 #---------------------------------------------------------------------------#
 # Exported symbols
 #---------------------------------------------------------------------------#
diff --git a/pymodbus/version.py b/pymodbus/version.py
index 2fc9d29..f95e10a 100644
--- a/pymodbus/version.py
+++ b/pymodbus/version.py
@@ -41,7 +41,8 @@ class Version(object):
         return '[%s, version %s]' % (self.package, self.short())
 
 
-version = Version('pymodbus', 1, 3, 1)
+version = Version('pymodbus', 1, 3, 2)
+
 
 
 version.__name__ = 'pymodbus'  # fix epydoc error
diff --git a/test/test_client_sync.py b/test/test_client_sync.py
index c88d682..516b442 100644
--- a/test/test_client_sync.py
+++ b/test/test_client_sync.py
@@ -202,6 +202,13 @@ class SynchronousClientTest(unittest.TestCase):
         self.assertTrue(isinstance(ModbusSerialClient(method='binary').framer, ModbusBinaryFramer))
         self.assertRaises(ParameterException, lambda: ModbusSerialClient(method='something'))
 
+    def testSyncSerialRTUClientTimeouts(self):
+        client = ModbusSerialClient(method="rtu", baudrate=9600)
+        assert client._silent_interval == (3.5 * 11/9600)
+        client = ModbusSerialClient(method="rtu", baudrate=38400)
+        assert client._silent_interval == (1.75/1000)
+
+
     @patch("serial.Serial")
     def testBasicSyncSerialClient(self, mock_serial):
         ''' Test the basic methods for the serial sync client'''
diff --git a/test/test_diag_messages.py b/test/test_diag_messages.py
index e62b8ec..ecb0939 100644
--- a/test/test_diag_messages.py
+++ b/test/test_diag_messages.py
@@ -33,7 +33,7 @@ class SimpleDataStoreTest(unittest.TestCase):
             (ReturnSlaveBusCharacterOverrunCountRequest,    b'\x00\x12\x00\x00', b'\x00\x12\x00\x00'),
             (ReturnIopOverrunCountRequest,                  b'\x00\x13\x00\x00', b'\x00\x13\x00\x00'),
             (ClearOverrunCountRequest,                      b'\x00\x14\x00\x00', b'\x00\x14\x00\x00'),
-            (GetClearModbusPlusRequest,                     b'\x00\x15\x00\x00', b'\x00\x15' + b'\x00\x00' * 55),
+            (GetClearModbusPlusRequest,                     b'\x00\x15\x00\x00', b'\x00\x15\x00\x00' + b'\x00\x00' * 55),
         ]
 
         self.responses = [
@@ -56,7 +56,7 @@ class SimpleDataStoreTest(unittest.TestCase):
             (ReturnSlaveBusCharacterOverrunCountResponse,  b'\x00\x12\x00\x00'),
             (ReturnIopOverrunCountResponse,                b'\x00\x13\x00\x00'),
             (ClearOverrunCountResponse,                    b'\x00\x14\x00\x00'),
-            (GetClearModbusPlusResponse,                   b'\x00\x15' + b'\x00\x00' * 55),
+            (GetClearModbusPlusResponse,                   b'\x00\x15\x00\x04' + b'\x00\x00' * 55),
         ]
 
     def tearDown(self):
@@ -100,7 +100,8 @@ class SimpleDataStoreTest(unittest.TestCase):
     def testDiagnosticExecute(self):
         ''' Testing diagnostic message execution '''
         for message, encoded, executed in self.requests:
-            self.assertEqual(message().execute().encode(), executed)
+            encoded = message().execute().encode()
+            self.assertEqual(encoded, executed)
 
     def testReturnQueryDataRequest(self):
         ''' Testing diagnostic message execution '''
@@ -130,13 +131,14 @@ class SimpleDataStoreTest(unittest.TestCase):
 
     def testGetClearModbusPlusRequestExecute(self):
         ''' Testing diagnostic message execution '''
-        request = GetClearModbusPlusRequest(ModbusPlusOperation.ClearStatistics);
+        request = GetClearModbusPlusRequest(data=ModbusPlusOperation.ClearStatistics);
         response = request.execute()
-        self.assertEqual(response.message, None)
+        self.assertEqual(response.message, ModbusPlusOperation.ClearStatistics)
 
-        request = GetClearModbusPlusRequest(ModbusPlusOperation.GetStatistics);
+        request = GetClearModbusPlusRequest(data=ModbusPlusOperation.GetStatistics);
         response = request.execute()
-        self.assertEqual(response.message, [0x00] * 55)
+        resp = [ModbusPlusOperation.GetStatistics]
+        self.assertEqual(response.message, resp+[0x00] * 55)
 
 #---------------------------------------------------------------------------#
 # Main
diff --git a/test/test_other_messages.py b/test/test_other_messages.py
index 1e9437a..08b24a2 100644
--- a/test/test_other_messages.py
+++ b/test/test_other_messages.py
@@ -92,13 +92,14 @@ class ModbusOtherMessageTest(unittest.TestCase):
         self.assertEqual(request.execute().function_code, 0x11)
 
         response = ReportSlaveIdResponse(request.execute().identifier, True)
-        self.assertEqual(response.encode(), b'\x0aPymodbus\xff')
+
+        self.assertEqual(response.encode(), b'\tPymodbus\xff')
         response.decode(b'\x03\x12\x00')
         self.assertEqual(response.status, False)
         self.assertEqual(response.identifier, b'\x12\x00')
 
         response.status = False
-        self.assertEqual(response.encode(), b'\x04\x12\x00\x00')
+        self.assertEqual(response.encode(), b'\x03\x12\x00\x00')
 
 #---------------------------------------------------------------------------#
 # Main
diff --git a/test/test_payload.py b/test/test_payload.py
index 157f584..2375c95 100644
--- a/test/test_payload.py
+++ b/test/test_payload.py
@@ -31,13 +31,15 @@ class ModbusPayloadUtilityTests(unittest.TestCase):
                        b'\x01\x02\x00\x03\x00\x00\x00\x04\x00\x00\x00\x00' \
                        b'\x00\x00\x00\xff\xfe\xff\xfd\xff\xff\xff\xfc\xff' \
                        b'\xff\xff\xff\xff\xff\xff\x00\x00\xa0\x3f\x00\x00' \
-                       b'\x00\x00\x00\x00\x19\x40\x74\x65\x73\x74\x11'
+                       b'\x00\x00\x00\x00\x19\x40\x01\x00\x74\x65\x73\x74' \
+                       b'\x11'
 
         self.big_endian_payload = \
                        b'\x01\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00' \
                        b'\x00\x00\x04\xff\xff\xfe\xff\xff\xff\xfd\xff\xff' \
                        b'\xff\xff\xff\xff\xff\xfc\x3f\xa0\x00\x00\x40\x19' \
-                       b'\x00\x00\x00\x00\x00\x00\x74\x65\x73\x74\x11'
+                       b'\x00\x00\x00\x00\x00\x00\x00\x01\x74\x65\x73\x74' \
+                       b'\x11'
 
         self.bitstring = [True, False, False, False, True, False, False, False]
 
@@ -62,6 +64,7 @@ class ModbusPayloadUtilityTests(unittest.TestCase):
         builder.add_64bit_int(-4)
         builder.add_32bit_float(1.25)
         builder.add_64bit_float(6.25)
+        builder.add_16bit_uint(1)      # placeholder
         builder.add_string(b'test')
         builder.add_bits(self.bitstring)
         self.assertEqual(self.little_endian_payload, builder.to_string())
@@ -79,6 +82,7 @@ class ModbusPayloadUtilityTests(unittest.TestCase):
         builder.add_64bit_int(-4)
         builder.add_32bit_float(1.25)
         builder.add_64bit_float(6.25)
+        builder.add_16bit_uint(1)      # placeholder
         builder.add_string('test')
         builder.add_bits(self.bitstring)
         self.assertEqual(self.big_endian_payload, builder.to_string())
@@ -125,6 +129,7 @@ class ModbusPayloadUtilityTests(unittest.TestCase):
         self.assertEqual(-4,     decoder.decode_64bit_int())
         self.assertEqual(1.25,   decoder.decode_32bit_float())
         self.assertEqual(6.25,   decoder.decode_64bit_float())
+        self.assertEqual(None,   decoder.skip_bytes(2))
         self.assertEqual('test', decoder.decode_string(4).decode())
         self.assertEqual(self.bitstring, decoder.decode_bits())
 
@@ -141,6 +146,7 @@ class ModbusPayloadUtilityTests(unittest.TestCase):
         self.assertEqual(-4,     decoder.decode_64bit_int())
         self.assertEqual(1.25,   decoder.decode_32bit_float())
         self.assertEqual(6.25,   decoder.decode_64bit_float())
+        self.assertEqual(None,   decoder.skip_bytes(2))
         self.assertEqual(b'test', decoder.decode_string(4))
         self.assertEqual(self.bitstring, decoder.decode_bits())
 
diff --git a/test/test_server_sync.py b/test/test_server_sync.py
index e6a9743..348f2ff 100644
--- a/test/test_server_sync.py
+++ b/test/test_server_sync.py
@@ -33,6 +33,7 @@ class MockServer(object):
         self.threads = []
         self.context = {}
 
+
 #---------------------------------------------------------------------------#
 # Fixture
 #---------------------------------------------------------------------------#
@@ -280,24 +281,30 @@ class SynchronousServerTest(unittest.TestCase):
     #-----------------------------------------------------------------------#
     def testSerialServerConnect(self):
         with patch.object(serial, 'Serial') as mock_serial:
-            mock_serial.return_value = "socket"
-            identity = ModbusDeviceIdentification(info={0x00: 'VendorName'})
-            server = ModbusSerialServer(context=None, identity=identity)
-            self.assertEqual(server.socket, "socket")
-            self.assertEqual(server.control.Identity.VendorName, 'VendorName')
-
-            server._connect()
-            self.assertEqual(server.socket, "socket")
+                # mock_serial.return_value = "socket"
+                mock_serial.write = lambda x: len(x)
+                mock_serial.read = lambda size: '\x00' * size
+                identity = ModbusDeviceIdentification(info={0x00: 'VendorName'})
+                server = ModbusSerialServer(context=None, identity=identity, port="dummy")
+                # # mock_serial.return_value = "socket"
+                # self.assertEqual(server.socket.port, "dummy")
+                self.assertEquals(server.handler.__class__.__name__, "CustomSingleRequestHandler")
+                self.assertEqual(server.control.Identity.VendorName, 'VendorName')
+
+                server._connect()
+                # self.assertEqual(server.socket, "socket")
 
         with patch.object(serial, 'Serial') as mock_serial:
+            mock_serial.write = lambda x: len(x)
+            mock_serial.read = lambda size: '\x00' * size
             mock_serial.side_effect = serial.SerialException()
-            server = ModbusSerialServer(None)
+            server = ModbusSerialServer(None, port="dummy")
             self.assertEqual(server.socket, None)
 
     def testSerialServerServeForever(self):
         ''' test that the synchronous serial server closes correctly '''
         with patch.object(serial, 'Serial') as mock_serial:
-            with patch('pymodbus.server.sync.ModbusSingleRequestHandler') as mock_handler:
+            with patch('pymodbus.server.sync.CustomSingleRequestHandler') as mock_handler:
                 server = ModbusSerialServer(None)
                 instance = mock_handler.return_value
                 instance.handle.side_effect = server.server_close

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pymodbus.git



More information about the Python-modules-commits mailing list