[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