[Python-modules-commits] [pymodbus] 01/07: import new upstream version 1.3.1

Wolfgang Borgert debacle at moszumanska.debian.org
Sat Jun 24 23:36:37 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 703a379712180f1a097099ca563dfc5377de6bf9
Author: W. Martin Borgert <debacle at debian.org>
Date:   Sat Jun 24 23:36:02 2017 +0200

    import new upstream version 1.3.1
---
 CHANGELOG.rst                             |  10 +++
 README.rst                                |   4 ++
 doc/pymodbus.pdf                          | Bin 509208 -> 0 bytes
 examples/common/asynchronous-client.py    |   5 +-
 examples/common/custom-datablock.py       |  11 ++--
 examples/common/performance.py            |   7 +-
 examples/common/synchronous-client-ext.py |   6 +-
 examples/common/synchronous-client.py     |  25 +++++--
 examples/contrib/message-parser.py        |  42 ++++++------
 examples/contrib/remote_server_context.py |   6 +-
 examples/gui/bottle/frontend.py           |   9 ++-
 pymodbus/bit_read_message.py              |  10 ++-
 pymodbus/client/sync.py                   |   1 -
 pymodbus/datastore/context.py             |   2 +-
 pymodbus/device.py                        |   2 +-
 pymodbus/exceptions.py                    |  15 +++++
 pymodbus/file_message.py                  |  91 -------------------------
 pymodbus/mei_message.py                   |  10 ++-
 pymodbus/other_message.py                 |  23 ++++---
 pymodbus/register_write_message.py        | 106 +++++++++++++++++++++++++++++-
 pymodbus/transaction.py                   |  89 ++++++++++++++++++++-----
 pymodbus/utilities.py                     |   2 +-
 pymodbus/version.py                       |   4 +-
 test/test_bit_read_messages.py            |  19 +++++-
 test/test_bit_write_messages.py           |   8 +++
 test/test_file_message.py                 |  62 -----------------
 test/test_other_messages.py               |   2 +-
 test/test_register_write_messages.py      |  73 ++++++++++++++++++++
 28 files changed, 406 insertions(+), 238 deletions(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 2ddb972..5ebc9db 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,4 +1,14 @@
 
+Version 1.3.1
+------------------------------------------------------------
+* Recall socket recv until get a complete response
+* Register_write_message.py: Observe skip_encode option when encoding a single register request
+* Fix wrong expected response length for coils and discrete inputs
+* Fix decode errors with ReadDeviceInformationRequest and  ReportSlaveIdRequest on Python3
+* Move MaskWriteRegisterRequest/MaskWriteRegisterResponse  to register_write_message.py from file_message.py
+* Python3 compatible examples [WIP]
+* Misc updates with examples
+
 Version 1.3.0.rc2
 ------------------------------------------------------------
 * Fix encoding problem for ReadDeviceInformationRequest method on python3
diff --git a/README.rst b/README.rst
index 582926f..afb4e07 100644
--- a/README.rst
+++ b/README.rst
@@ -4,6 +4,10 @@
 .. image:: https://badges.gitter.im/Join%20Chat.svg
    :target: https://gitter.im/pymodbus_dev/Lobby
 
+.. image:: https://readthedocs.org/projects/pymodbus-n/badge/?version=latest
+   :target: http://pymodbus-n.readthedocs.io/en/latest/?badge=latest
+   :alt: Documentation Status
+
 ============================================================
 Summary
 ============================================================
diff --git a/doc/pymodbus.pdf b/doc/pymodbus.pdf
deleted file mode 100644
index d3dc1ff..0000000
Binary files a/doc/pymodbus.pdf and /dev/null differ
diff --git a/examples/common/asynchronous-client.py b/examples/common/asynchronous-client.py
index c7e8678..2ce863f 100755
--- a/examples/common/asynchronous-client.py
+++ b/examples/common/asynchronous-client.py
@@ -30,7 +30,8 @@ log.setLevel(logging.DEBUG)
 # helper method to test deferred callbacks
 #---------------------------------------------------------------------------# 
 def dassert(deferred, callback):
-    def _assertor(value): assert(value)
+    def _assertor(value):
+        assert(value)
     deferred.addCallback(lambda r: _assertor(callback(r)))
     deferred.addErrback(lambda  _: _assertor(False))
 
@@ -121,6 +122,6 @@ def beginAsynchronousTest(client):
 # directory, or start a pymodbus server.
 #---------------------------------------------------------------------------# 
 defer = protocol.ClientCreator(reactor, ModbusClientProtocol
-        ).connectTCP("localhost", Defaults.Port)
+        ).connectTCP("localhost", 5020)
 defer.addCallback(beginAsynchronousTest)
 reactor.run()
diff --git a/examples/common/custom-datablock.py b/examples/common/custom-datablock.py
index 42376dd..481240f 100755
--- a/examples/common/custom-datablock.py
+++ b/examples/common/custom-datablock.py
@@ -8,7 +8,8 @@ written to the datastore.
 '''
 #---------------------------------------------------------------------------# 
 # import the modbus libraries we need
-#---------------------------------------------------------------------------# 
+#---------------------------------------------------------------------------#
+from __future__ import print_function
 from pymodbus.server.async import StartTcpServer
 from pymodbus.device import ModbusDeviceIdentification
 from pymodbus.datastore import ModbusSparseDataBlock
@@ -45,14 +46,14 @@ class CustomDataBlock(ModbusSparseDataBlock):
         # however make sure not to do too much work here or it will
         # block the server, espectially if the server is being written
         # to very quickly
-        print "wrote {} to {}".format(value, address)
+        print("wrote {} to {}".format(value, address))
 
 
 #---------------------------------------------------------------------------# 
 # initialize your data store
 #---------------------------------------------------------------------------# 
 
-block   = CustomDataBlock()
+block   = CustomDataBlock([0]*100)
 store   = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
 context = ModbusServerContext(slaves=store, single=True)
 
@@ -72,6 +73,6 @@ identity.MajorMinorRevision = '1.0'
 # run the server you want
 #---------------------------------------------------------------------------# 
 
-p = Process(target=device_writer, args=(queue,))
-p.start()
+# p = Process(target=device_writer, args=(queue,))
+# p.start()
 StartTcpServer(context, identity=identity, address=("localhost", 5020))
diff --git a/examples/common/performance.py b/examples/common/performance.py
index 11ee8f8..36ce11e 100755
--- a/examples/common/performance.py
+++ b/examples/common/performance.py
@@ -8,7 +8,8 @@ modbus client.
 '''
 #---------------------------------------------------------------------------# 
 # import the necessary modules
-#---------------------------------------------------------------------------# 
+#---------------------------------------------------------------------------#
+from __future__ import print_function
 import logging, os
 from time import time
 from multiprocessing import log_to_stderr
@@ -79,5 +80,5 @@ start = time()
 any(p.start() for p in procs)   # start the workers
 any(p.join()  for p in procs)   # wait for the workers to finish
 stop  = time()
-print "%d requests/second" % ((1.0 * cycles) / (stop - start))
-print "time taken to complete %s cycle by %s workers is %s seconds" % (cycles, workers, stop-start)
+print("%d requests/second" % ((1.0 * cycles) / (stop - start)))
+print("time taken to complete %s cycle by %s workers is %s seconds" % (cycles, workers, stop-start))
diff --git a/examples/common/synchronous-client-ext.py b/examples/common/synchronous-client-ext.py
index 5f84bae..df4bda3 100755
--- a/examples/common/synchronous-client-ext.py
+++ b/examples/common/synchronous-client-ext.py
@@ -70,9 +70,9 @@ rq = ReadDeviceInformationRequest(unit=1)
 rr = client.execute(rq)
 #assert(rr == None)                             # not supported by reference
 assert(rr.function_code < 0x80)                 # test that we are not an error
-assert(rr.information[0] == 'Pymodbus')  # test the vendor name
-assert(rr.information[1] == 'PM')          # test the product code
-assert(rr.information[2] == '1.0')     # test the code revision
+assert(rr.information[0] == b'Pymodbus')  # test the vendor name
+assert(rr.information[1] == b'PM')          # test the product code
+assert(rr.information[2] == b'1.0')     # test the code revision
 
 rq = ReportSlaveIdRequest(unit=1)
 rr = client.execute(rq)
diff --git a/examples/common/synchronous-client.py b/examples/common/synchronous-client.py
index 503e6fc..686c05c 100755
--- a/examples/common/synchronous-client.py
+++ b/examples/common/synchronous-client.py
@@ -66,7 +66,8 @@ client.connect()
 # The slave to query is specified in an optional parameter for each
 # individual request. This can be done by specifying the `unit` parameter
 # which defaults to `0x00`
-#---------------------------------------------------------------------------# 
+#---------------------------------------------------------------------------#
+log.debug("Reading Coils")
 rr = client.read_coils(1, 1, unit=0x01)
 
 #---------------------------------------------------------------------------# 
@@ -80,36 +81,51 @@ rr = client.read_coils(1, 1, unit=0x01)
 # blocks for the two sets, so a change to one is a change to the other.
 # Keep both of these cases in mind when testing as the following will
 # _only_ pass with the supplied async modbus server (script supplied).
-#---------------------------------------------------------------------------# 
+#---------------------------------------------------------------------------#
+log.debug("Write to a Coil and read back")
 rq = client.write_coil(0, True, unit=1)
 rr = client.read_coils(0, 1, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
 assert(rr.bits[0] == True)          # test the expected value
 
+log.debug("Write to multiple coils and read back- test 1")
 rq = client.write_coils(1, [True]*8, unit=1)
-rr = client.read_coils(1, 8, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
-assert(rr.bits == [True]*8)         # test the expected value
+rr = client.read_coils(1, 21, unit=1)
+assert(rr.function_code < 0x80)     # test that we are not an error
+resp = [True]*21
+
+# If the returned output quantity is not a multiple of eight,
+# the remaining bits in the final data byte will be padded with zeros
+# (toward the high order end of the byte).
+
+resp.extend([False]*3)
+assert(rr.bits == resp)         # test the expected value
 
+log.debug("Write to multiple coils and read back - test 2")
 rq = client.write_coils(1, [False]*8, unit=1)
 rr = client.read_coils(1, 8, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
 assert(rr.bits == [False]*8)         # test the expected value
 
 
+log.debug("Read discrete inputs")
 rr = client.read_discrete_inputs(0, 8, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
 
+log.debug("Write to a holding register and read back")
 rq = client.write_register(1, 10, unit=1)
 rr = client.read_holding_registers(1, 1, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
 assert(rr.registers[0] == 10)       # test the expected value
 
+log.debug("Write to multiple holding registers and read back")
 rq = client.write_registers(1, [10]*8, unit=1)
 rr = client.read_holding_registers(1, 8, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
 assert(rr.registers == [10]*8)      # test the expected value
 
+log.debug("Read input registers")
 rr = client.read_input_registers(1, 8, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
 
@@ -119,6 +135,7 @@ arguments = {
     'write_address':   1,
     'write_registers': [20]*8,
 }
+log.debug("Read write registeres simulataneously")
 rq = client.readwrite_registers(unit=1, **arguments)
 rr = client.read_holding_registers(1, 8, unit=1)
 assert(rq.function_code < 0x80)     # test that we are not an error
diff --git a/examples/contrib/message-parser.py b/examples/contrib/message-parser.py
index b1f35c0..b5c653b 100755
--- a/examples/contrib/message-parser.py
+++ b/examples/contrib/message-parser.py
@@ -13,7 +13,8 @@ using the supplied framers for a number of protocols:
 '''
 #---------------------------------------------------------------------------# 
 # import needed libraries
-#---------------------------------------------------------------------------# 
+#---------------------------------------------------------------------------#
+from __future__ import print_function
 import sys
 import collections
 import textwrap
@@ -52,23 +53,25 @@ class Decoder(object):
         :param message: The messge to decode
         '''
         value = message if self.encode else message.encode('hex')
-        print "="*80
-        print "Decoding Message %s" % value
-        print "="*80
+        print("="*80)
+        print("Decoding Message %s" % value)
+        print("="*80)
         decoders = [
             self.framer(ServerDecoder()),
             self.framer(ClientDecoder()),
         ]
         for decoder in decoders:
-            print "%s" % decoder.decoder.__class__.__name__
-            print "-"*80
+            print("%s" % decoder.decoder.__class__.__name__)
+            print("-"*80)
             try:
-                decoder.addToFrame(message)
+                decoder.addToFrame(message.encode())
                 if decoder.checkFrame():
                     decoder.advanceFrame()
                     decoder.processIncomingPacket(message, self.report)
-                else: self.check_errors(decoder, message)
-            except Exception, ex: self.check_errors(decoder, message)
+                else:
+                    self.check_errors(decoder, message)
+            except Exception as ex:
+                self.check_errors(decoder, message)
 
     def check_errors(self, decoder, message):
         ''' Attempt to find message errors
@@ -82,20 +85,21 @@ class Decoder(object):
 
         :param message: The message to print
         '''
-        print "%-15s = %s" % ('name', message.__class__.__name__)
+        print("%-15s = %s" % ('name', message.__class__.__name__))
         for k,v in message.__dict__.iteritems():
             if isinstance(v, dict):
-                print "%-15s =" % k
+                print("%-15s =" % k)
                 for kk,vv in v.items():
-                    print "  %-12s => %s" % (kk, vv)
+                    print("  %-12s => %s" % (kk, vv))
 
             elif isinstance(v, collections.Iterable):
-                print "%-15s =" % k
+                print("%-15s =" % k)
                 value = str([int(x) for x  in v])
                 for line in textwrap.wrap(value, 60):
-                    print "%-15s . %s" % ("", line)
-            else: print "%-15s = %s" % (k, hex(v))
-        print "%-15s = %s" % ('documentation', message.__doc__)
+                    print("%-15s . %s" % ("", line))
+            else:
+                print("%-15s = %s" % (k, hex(v)))
+        print("%-15s = %s" % ('documentation', message.__doc__))
 
 
 #---------------------------------------------------------------------------# 
@@ -166,9 +170,9 @@ def main():
     if option.debug:
         try:
             modbus_log.setLevel(logging.DEBUG)
-    	    logging.basicConfig()
-        except Exception, e:
-    	    print "Logging is not supported on this system"
+            logging.basicConfig()
+        except Exception as e:
+            print("Logging is not supported on this system- {}".format(e))
 
     framer = lookup = {
         'tcp':    ModbusSocketFramer,
diff --git a/examples/contrib/remote_server_context.py b/examples/contrib/remote_server_context.py
index e8e0270..594eeb5 100644
--- a/examples/contrib/remote_server_context.py
+++ b/examples/contrib/remote_server_context.py
@@ -66,7 +66,7 @@ class RemoteSingleSlaveContext(IModbusSlaveContext):
         :returns: True if the request in within range, False otherwise
         '''
         _logger.debug("validate[%d] %d:%d" % (fx, address, count))
-        result = context.get_callbacks[self.decode(fx)](address, count, self.unit_id)
+        result = self.context.get_callbacks[self.decode(fx)](address, count, self.unit_id)
         return result.function_code < 0x80
 
     def getValues(self, fx, address, count=1):
@@ -78,7 +78,7 @@ class RemoteSingleSlaveContext(IModbusSlaveContext):
         :returns: The requested values from a:a+c
         '''
         _logger.debug("get values[%d] %d:%d" % (fx, address, count))
-        result = context.get_callbacks[self.decode(fx)](address, count, self.unit_id)
+        result = self.context.get_callbacks[self.decode(fx)](address, count, self.unit_id)
         return self.__extract_result(self.decode(fx), result)
 
     def setValues(self, fx, address, values):
@@ -89,7 +89,7 @@ class RemoteSingleSlaveContext(IModbusSlaveContext):
         :param values: The new values to be set
         '''
         _logger.debug("set values[%d] %d:%d" % (fx, address, len(values)))
-        context.set_callbacks[self.decode(fx)](address, values, self.unit_id)
+        self.context.set_callbacks[self.decode(fx)](address, values, self.unit_id)
 
     def __str__(self):
         ''' Returns a string representation of the context
diff --git a/examples/gui/bottle/frontend.py b/examples/gui/bottle/frontend.py
index ed98e4a..fdbcb28 100644
--- a/examples/gui/bottle/frontend.py
+++ b/examples/gui/bottle/frontend.py
@@ -5,6 +5,7 @@ Pymodbus Web Frontend
 This is a simple web frontend using bottle as the web framework.
 This can be hosted using any wsgi adapter.
 '''
+from __future__ import print_function
 import json, inspect
 from bottle import route, request, Bottle
 from bottle import static_file
@@ -125,7 +126,8 @@ class ModbusApiWebApp(object):
             result  = { 'data' : values }
             result.update(Response.success)
             return result
-        except Exception, ex: log.error(ex)
+        except Exception as ex:
+            log.error(ex)
         return Response.failure
 
     def get_coils(self, address='0', count='1'):
@@ -147,11 +149,12 @@ class ModbusApiWebApp(object):
         try:
             address = int(address)
             values  = json.loads(values)
-            print values
+            print(values)
             context = self._server.store[int(store)]
             context.setValues(store, address, values)
             return Response.success
-        except Exception, ex: log.error(ex)
+        except Exception as ex:
+            log.error(ex)
         return Response.failure
 
     def post_coils(self, address='0'):
diff --git a/pymodbus/bit_read_message.py b/pymodbus/bit_read_message.py
index abe6cc1..d8624fa 100644
--- a/pymodbus/bit_read_message.py
+++ b/pymodbus/bit_read_message.py
@@ -15,6 +15,7 @@ class ReadBitsRequestBase(ModbusRequest):
     ''' Base class for Messages Requesting bit values '''
 
     _rtu_frame_size = 8
+
     def __init__(self, address, count, **kwargs):
         ''' Initializes the read request data
 
@@ -41,10 +42,15 @@ class ReadBitsRequestBase(ModbusRequest):
     
     def get_response_pdu_size(self):
         """
-        Func_code (1 byte) + Byte Count(1 byte) + Quantity of Coils (n Bytes)
+        Func_code (1 byte) + Byte Count(1 byte) + Quantity of Coils (n Bytes)/8,
+        if the remainder is different of 0 then N = N+1
         :return: 
         """
-        return 1 + 1 + self.count
+        count = self.count//8
+        if self.count % 8:
+            count += 1
+
+        return 1 + 1 + count
     
     def __str__(self):
         ''' Returns a string representation of the instance
diff --git a/pymodbus/client/sync.py b/pymodbus/client/sync.py
index 90f09f2..7f85116 100644
--- a/pymodbus/client/sync.py
+++ b/pymodbus/client/sync.py
@@ -141,7 +141,6 @@ class ModbusTcpClient(BaseModbusClient):
         '''
         if self.socket: return True
         try:
-            address = (self.host, self.port)
             self.socket = socket.create_connection((self.host, self.port),
                 timeout=self.timeout, source_address=self.source_address)
         except socket.error as msg:
diff --git a/pymodbus/datastore/context.py b/pymodbus/datastore/context.py
index f90c9f9..a5e311a 100644
--- a/pymodbus/datastore/context.py
+++ b/pymodbus/datastore/context.py
@@ -7,7 +7,7 @@ from pymodbus.compat import iteritems, itervalues
 #---------------------------------------------------------------------------#
 # Logging
 #---------------------------------------------------------------------------#
-import logging;
+import logging
 _logger = logging.getLogger(__name__)
 
 
diff --git a/pymodbus/device.py b/pymodbus/device.py
index d2fb8b8..05fbadf 100644
--- a/pymodbus/device.py
+++ b/pymodbus/device.py
@@ -459,7 +459,7 @@ class ModbusCountersHandler(object):
 
 
 #---------------------------------------------------------------------------#
-# Main server controll block
+# Main server control block
 #---------------------------------------------------------------------------#
 class ModbusControlBlock(Singleton):
     '''
diff --git a/pymodbus/exceptions.py b/pymodbus/exceptions.py
index 490fdb1..c143569 100644
--- a/pymodbus/exceptions.py
+++ b/pymodbus/exceptions.py
@@ -77,6 +77,21 @@ class ConnectionException(ModbusException):
         message = "[Connection] %s" % string
         ModbusException.__init__(self, message)
 
+
+class InvalidResponseRecievedException(ModbusException):
+    """
+    Error resulting from invalid response received or decoded
+    """
+
+
+def __init__(self, string=""):
+    ''' Initialize the exception
+
+    :param string: The message to append to the error
+    '''
+    message = "[Invalid Response] %s" % string
+    ModbusException.__init__(self, message)
+
 #---------------------------------------------------------------------------#
 # Exported symbols
 #---------------------------------------------------------------------------#
diff --git a/pymodbus/file_message.py b/pymodbus/file_message.py
index a141883..15fba11 100644
--- a/pymodbus/file_message.py
+++ b/pymodbus/file_message.py
@@ -279,96 +279,6 @@ class WriteFileRecordResponse(ModbusResponse):
             if decoded[0] == 0x06: self.records.append(record)
 
 
-class MaskWriteRegisterRequest(ModbusRequest):
-    '''
-    This function code is used to modify the contents of a specified holding
-    register using a combination of an AND mask, an OR mask, and the
-    register's current contents. The function can be used to set or clear
-    individual bits in the register.
-    '''
-    function_code = 0x16
-    _rtu_frame_size = 10
-    
-
-    def __init__(self, address=0x0000, and_mask=0xffff, or_mask=0x0000, **kwargs):
-        ''' Initializes a new instance
-
-        :param address: The mask pointer address (0x0000 to 0xffff)
-        :param and_mask: The and bitmask to apply to the register address
-        :param or_mask: The or bitmask to apply to the register address
-        '''
-        ModbusRequest.__init__(self, **kwargs)
-        self.address  = address
-        self.and_mask = and_mask
-        self.or_mask  = or_mask
-
-    def encode(self):
-        ''' Encodes the request packet
-
-        :returns: The byte encoded packet
-        '''
-        return struct.pack('>HHH', self.address, self.and_mask, self.or_mask)
-
-    def decode(self, data):
-        ''' Decodes the incoming request
-
-        :param data: The data to decode into the address
-        '''
-        self.address, self.and_mask, self.or_mask = struct.unpack('>HHH', data)
-
-    def execute(self, context):
-        ''' Run a mask write register request against the store
-
-        :param context: The datastore to request from
-        :returns: The populated response
-        '''
-        if not (0x0000 <= self.and_mask <= 0xffff):
-            return self.doException(merror.IllegalValue)
-        if not (0x0000 <= self.or_mask <= 0xffff):
-            return self.doException(merror.IllegalValue)
-        if not context.validate(self.function_code, self.address, 1):
-            return self.doException(merror.IllegalAddress)
-        values = context.getValues(self.function_code, self.address, 1)[0]
-        values = ((values & self.and_mask) | self.or_mask)
-        context.setValues(self.function_code, self.address, [values])
-        return MaskWriteRegisterResponse(self.address, self.and_mask, self.or_mask)
-
-
-class MaskWriteRegisterResponse(ModbusResponse):
-    '''
-    The normal response is an echo of the request. The response is returned
-    after the register has been written.
-    '''
-    function_code = 0x16
-    _rtu_frame_size = 10
-
-    def __init__(self, address=0x0000, and_mask=0xffff, or_mask=0x0000, **kwargs):
-        ''' Initializes a new instance
-
-        :param address: The mask pointer address (0x0000 to 0xffff)
-        :param and_mask: The and bitmask applied to the register address
-        :param or_mask: The or bitmask applied to the register address
-        '''
-        ModbusResponse.__init__(self, **kwargs)
-        self.address  = address
-        self.and_mask = and_mask
-        self.or_mask  = or_mask
-
-    def encode(self):
-        ''' Encodes the response
-
-        :returns: The byte encoded message
-        '''
-        return struct.pack('>HHH', self.address, self.and_mask, self.or_mask)
-
-    def decode(self, data):
-        ''' Decodes a the response
-
-        :param data: The packet data to decode
-        '''
-        self.address, self.and_mask, self.or_mask = struct.unpack('>HHH', data)
-
-
 class ReadFifoQueueRequest(ModbusRequest):
     '''
     This function code allows to read the contents of a First-In-First-Out
@@ -481,6 +391,5 @@ __all__ = [
     "FileRecord",
     "ReadFileRecordRequest", "ReadFileRecordResponse",
     "WriteFileRecordRequest", "WriteFileRecordResponse",
-    "MaskWriteRegisterRequest", "MaskWriteRegisterResponse",
     "ReadFifoQueueRequest", "ReadFifoQueueResponse",
 ]
diff --git a/pymodbus/mei_message.py b/pymodbus/mei_message.py
index af64a89..1896766 100644
--- a/pymodbus/mei_message.py
+++ b/pymodbus/mei_message.py
@@ -10,7 +10,7 @@ from pymodbus.pdu import ModbusResponse
 from pymodbus.device import ModbusControlBlock
 from pymodbus.device import DeviceInformationFactory
 from pymodbus.pdu import ModbusExceptions as merror
-from pymodbus.compat import iteritems, byte2int
+from pymodbus.compat import iteritems, byte2int, IS_PYTHON3
 
 _MCB = ModbusControlBlock()
 
@@ -132,7 +132,13 @@ class ReadDeviceInformationResponse(ModbusResponse):
 
         for (object_id, data) in iteritems(self.information):
             packet += struct.pack('>BB', object_id, len(data))
-            packet += data.encode()
+            if IS_PYTHON3:
+                if isinstance(data, bytes):
+                    packet += data
+                else:
+                    packet += data.encode()
+            else:
+                packet += data.encode()
         return packet
 
     def decode(self, data):
diff --git a/pymodbus/other_message.py b/pymodbus/other_message.py
index 7adcb74..93c6cfb 100644
--- a/pymodbus/other_message.py
+++ b/pymodbus/other_message.py
@@ -43,7 +43,7 @@ class ReadExceptionStatusRequest(ModbusRequest):
         '''
         pass
 
-    def execute(self):
+    def execute(self, context=None):
         ''' Run a read exeception status request against the store
 
         :returns: The populated response
@@ -144,7 +144,7 @@ class GetCommEventCounterRequest(ModbusRequest):
         '''
         pass
 
-    def execute(self):
+    def execute(self, context=None):
         ''' Run a read exeception status request against the store
 
         :returns: The populated response
@@ -249,7 +249,7 @@ class GetCommEventLogRequest(ModbusRequest):
         '''
         pass
 
-    def execute(self):
+    def execute(self, context=None):
         ''' Run a read exeception status request against the store
 
         :returns: The populated response
@@ -359,12 +359,12 @@ class ReportSlaveIdRequest(ModbusRequest):
         '''
         pass
 
-    def execute(self):
+    def execute(self, context=None):
         ''' Run a read exeception status request against the store
 
         :returns: The populated response
         '''
-        identifier = b'\x70\x79\x6d\x6f\x64\x62\x75\x73'
+        identifier = b'Pymodbus'
         return ReportSlaveIdResponse(identifier)
 
     def __str__(self):
@@ -392,14 +392,17 @@ class ReportSlaveIdResponse(ModbusResponse):
         ModbusResponse.__init__(self, **kwargs)
         self.identifier = identifier
         self.status = status
+        self.byte_count = None
 
     def encode(self):
         ''' Encodes the response
 
         :returns: The byte encoded message
         '''
-        if self.status: status = ModbusStatus.SlaveOn
-        else: status = ModbusStatus.SlaveOff
+        if self.status:
+            status = ModbusStatus.SlaveOn
+        else:
+            status = ModbusStatus.SlaveOff
         length  = len(self.identifier) + 2
         packet  = int2byte(length)
         packet += self.identifier  # we assume it is already encoded
@@ -414,8 +417,8 @@ class ReportSlaveIdResponse(ModbusResponse):
 
         :param data: The packet data to decode
         '''
-        length = byte2int(data[0])
-        self.identifier = data[1:length + 1]
+        self.byte_count = byte2int(data[0])
+        self.identifier = data[1:self.byte_count + 1]
         status = byte2int(data[-1])
         self.status = status == ModbusStatus.SlaveOn
 
@@ -425,7 +428,7 @@ class ReportSlaveIdResponse(ModbusResponse):
         :returns: The string representation of the response
         '''
         arguments = (self.function_code, self.identifier, self.status)
-        return "ResportSlaveIdResponse(%d, %d, %d)" % arguments
+        return "ResportSlaveIdResponse(%s, %s, %s)" % arguments
 
 #---------------------------------------------------------------------------#
 # TODO Make these only work on serial
diff --git a/pymodbus/register_write_message.py b/pymodbus/register_write_message.py
index 9800405..06e2293 100644
--- a/pymodbus/register_write_message.py
+++ b/pymodbus/register_write_message.py
@@ -35,9 +35,12 @@ class WriteSingleRegisterRequest(ModbusRequest):
 
         :returns: The encoded packet
         '''
+        packet = struct.pack('>H', self.address)
         if self.skip_encode:
-            return self.value
-        return struct.pack('>HH', self.address, self.value)
+            packet += self.value
+        else:
+            packet += struct.pack('>H', self.value)
+        return packet
 
     def decode(self, data):
         ''' Decode a write single register packet packet request
@@ -73,7 +76,7 @@ class WriteSingleRegisterRequest(ModbusRequest):
 
         :returns: A string representation of the instance
         '''
-        return "WriteRegisterRequest %d => %d" % (self.address, self.value)
+        return "WriteRegisterRequest %d" % self.address
 
 
 class WriteSingleRegisterResponse(ModbusResponse):
@@ -238,10 +241,107 @@ class WriteMultipleRegistersResponse(ModbusResponse):
         params = (self.address, self.count)
         return "WriteMultipleRegisterResponse (%d,%d)" % params
 
+class MaskWriteRegisterRequest(ModbusRequest):
+    '''
+    This function code is used to modify the contents of a specified holding
+    register using a combination of an AND mask, an OR mask, and the
+    register's current contents. The function can be used to set or clear
+    individual bits in the register.
+    '''
+    function_code = 0x16
+    _rtu_frame_size = 10
+
+    def __init__(self, address=0x0000, and_mask=0xffff, or_mask=0x0000,
+                 **kwargs):
+        ''' Initializes a new instance
+
+        :param address: The mask pointer address (0x0000 to 0xffff)
+        :param and_mask: The and bitmask to apply to the register address
+        :param or_mask: The or bitmask to apply to the register address
+        '''
+        ModbusRequest.__init__(self, **kwargs)
+        self.address = address
+        self.and_mask = and_mask
+        self.or_mask = or_mask
+
+    def encode(self):
+        ''' Encodes the request packet
+
+        :returns: The byte encoded packet
+        '''
+        return struct.pack('>HHH', self.address, self.and_mask,
+                           self.or_mask)
+
+    def decode(self, data):
+        ''' Decodes the incoming request
+
+        :param data: The data to decode into the address
+        '''
+        self.address, self.and_mask, self.or_mask = struct.unpack('>HHH',
+                                                                  data)
+
+    def execute(self, context):
+        ''' Run a mask write register request against the store
+
+        :param context: The datastore to request from
+        :returns: The populated response
+        '''
+        if not (0x0000 <= self.and_mask <= 0xffff):
+            return self.doException(merror.IllegalValue)
+        if not (0x0000 <= self.or_mask <= 0xffff):
+            return self.doException(merror.IllegalValue)
+        if not context.validate(self.function_code, self.address, 1):
+            return self.doException(merror.IllegalAddress)
+        values = context.getValues(self.function_code, self.address, 1)[0]
+        values = ((values & self.and_mask) | self.or_mask)
+        context.setValues(self.function_code, self.address, [values])
+        return MaskWriteRegisterResponse(self.address, self.and_mask,
+                                         self.or_mask)
+
+
+class MaskWriteRegisterResponse(ModbusResponse):
+    '''
+    The normal response is an echo of the request. The response is returned
+    after the register has been written.
+    '''
+    function_code = 0x16
+    _rtu_frame_size = 10
+
+    def __init__(self, address=0x0000, and_mask=0xffff, or_mask=0x0000,
+                 **kwargs):
+        ''' Initializes a new instance
+
+        :param address: The mask pointer address (0x0000 to 0xffff)
+        :param and_mask: The and bitmask applied to the register address
+        :param or_mask: The or bitmask applied to the register address
+        '''
+        ModbusResponse.__init__(self, **kwargs)
+        self.address = address
+        self.and_mask = and_mask
+        self.or_mask = or_mask
+
+    def encode(self):
+        ''' Encodes the response
+
+        :returns: The byte encoded message
+        '''
+        return struct.pack('>HHH', self.address, self.and_mask,
+                           self.or_mask)
+
+    def decode(self, data):
+        ''' Decodes a the response
+
+        :param data: The packet data to decode
+        '''
+        self.address, self.and_mask, self.or_mask = struct.unpack('>HHH',
+                                                                  data)
+
+
 #---------------------------------------------------------------------------#
 # Exported symbols
 #---------------------------------------------------------------------------#
 __all__ = [
     "WriteSingleRegisterRequest", "WriteSingleRegisterResponse",
     "WriteMultipleRegistersRequest", "WriteMultipleRegistersResponse",
+    "MaskWriteRegisterRequest", "MaskWriteRegisterResponse"
 ]
diff --git a/pymodbus/transaction.py b/pymodbus/transaction.py
index 78a6326..9b54697 100644
--- a/pymodbus/transaction.py
+++ b/pymodbus/transaction.py
@@ -7,6 +7,7 @@ import socket
 from binascii import b2a_hex, a2b_hex
 
 from pymodbus.exceptions import ModbusIOException, NotImplementedException
+from pymodbus.exceptions import InvalidResponseRecievedException
 from pymodbus.constants  import Defaults
 from pymodbus.interfaces import IModbusFramer
 from pymodbus.utilities  import checkCRC, computeCRC
@@ -68,10 +69,38 @@ class ModbusTransactionManager(object):
 
     def _calculate_response_length(self, expected_pdu_size):
         if self.base_adu_size == -1:
-            return 1024
+            return None
         else:
             return self.base_adu_size + expected_pdu_size
 
+    def _calculate_exception_length(self):
+        ''' Returns the length of the Modbus Exception Response according to
+        the type of Framer.
+        '''
+        if isinstance(self.client.framer, ModbusSocketFramer):
+            return self.base_adu_size + 2  # Fcode(1), ExcecptionCode(1)
+        elif isinstance(self.client.framer, ModbusAsciiFramer):
+            return self.base_adu_size + 4  # Fcode(2), ExcecptionCode(2)
+        elif isinstance(self.client.framer, (ModbusRtuFramer, ModbusBinaryFramer)):
+            return self.base_adu_size + 2  # Fcode(1), ExcecptionCode(1)
+
+        return None
+
+    def _check_response(self, response):
+        ''' Checks if the response is a Modbus Exception.
+        '''
+        if isinstance(self.client.framer, ModbusSocketFramer):
+            if len(response) >= 8 and byte2int(response[7]) > 128:
+                return False
+        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)):
+            if len(response) >= 2 and byte2int(response[1]) > 128:
+                return False
+
+        return True
+
     def execute(self, request):
         ''' Starts the producer to send the next request to
         consumer.write(Frame(request))
@@ -79,31 +108,49 @@ class ModbusTransactionManager(object):
         retries = self.retries
         request.transaction_id = self.getNextTID()
         _logger.debug("Running transaction %d" % request.transaction_id)
+        expected_response_length = None
         if hasattr(request, "get_response_pdu_size"):
             response_pdu_size = request.get_response_pdu_size()
-            expected_response_length = self._calculate_response_length(response_pdu_size)
-        else:
-            expected_response_length = 1024
+            if response_pdu_size:
+                expected_response_length = self._calculate_response_length(response_pdu_size)
 
         while retries > 0:
             try:
+                last_exception = None
                 self.client.connect()
-                self.client._send(self.client.framer.buildPacket(request))
-                result = self.client._recv(expected_response_length)
+                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))
 
                 if not result and self.retry_on_empty:
                     retries -= 1
                     continue
+
                 if _logger.isEnabledFor(logging.DEBUG):
                     _logger.debug("recv: " + " ".join([hex(byte2int(x)) for x in result]))
                 self.client.framer.processIncomingPacket(result, self.addTransaction)
-                break;
-            except socket.error as msg:
+                break
+            except (socket.error, ModbusIOException, InvalidResponseRecievedException) as msg:
                 self.client.close()
                 _logger.debug("Transaction failed. (%s) " % msg)
                 retries -= 1
-        return self.getTransaction(request.transaction_id)
+                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)
 
+        return response
 
     def addTransaction(self, request, tid=None):
         ''' Adds a transaction to the handler
@@ -391,9 +438,12 @@ class ModbusSocketFramer(IModbusFramer):
         result = self.decoder.decode(data)
         if result is None:
             raise ModbusIOException("Unable to decode request")
-        self.populateResult(result)
-        self.advanceFrame()
-        callback(result)  # defer or push to a thread?
+        elif error and result.function_code < 0x80:
+            raise InvalidResponseRecievedException(result)
+        else:
+            self.populateResult(result)
+            self.advanceFrame()
+            callback(result)  # defer or push to a thread?
 
     def resetFrame(self):
         ''' Reset the entire message frame.
@@ -629,9 +679,12 @@ class ModbusRtuFramer(IModbusFramer):
         result = self.decoder.decode(data)
         if result is None:
             raise ModbusIOException("Unable to decode request")
-        self.populateResult(result)
-        self.advanceFrame()
-        callback(result)  # defer or push to a thread?
+        elif error and result.function_code < 0x80:
+            raise InvalidResponseRecievedException(result)
+        else:
+            self.populateResult(result)
+            self.advanceFrame()
... 311 lines suppressed ...

-- 
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