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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
Version 1.5.0
------------------------------------------------------------
* Improve transaction speeds for sync clients (RTU/ASCII), now retry on empty happens only when retry_on_empty kwarg is passed to client during intialization

`client = Client(..., retry_on_empty=True)`

* Fix tcp servers (sync/async) not processing requests with transaction id > 255
* Introduce new api to check if the received response is an error or not (response.isError())
* Move timing logic to framers so that irrespective of client, correct timing logics are followed.
* Move framers from transaction.py to respective modules
* Fix modbus payload builder and decoder
* Async servers can now have an option to defer `reactor.run()` when using `Start<Tcp/Serial/Udo>Server(...,defer_reactor_run=True)`
* Fix UDP client issue while handling MEI messages (ReadDeviceInformationRequest)
* Add expected response lengths for WriteMultipleCoilRequest and WriteMultipleRegisterRequest
* Fix struct errors while decoding stray response
* Modbus read retries works only when empty/no message is received
* Change test runner from nosetest to pytest
* Fix Misc examples

Version 1.4.0
------------------------------------------------------------
* Bug fix Modbus TCP client reading incomplete data
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ check: install

test: install
@pip install --quiet --requirement=requirements-tests.txt
@nosetests --with-coverage --cover-html
@py.test
@coverage report --fail-under=90

tox: install
Expand Down
47 changes: 47 additions & 0 deletions doc/source/library/pymodbus.framer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pymodbus\.framer package
========================

Submodules
----------

pymodbus\.framer\.ascii_framer module
-------------------------------------

.. automodule:: pymodbus.framer.ascii_framer
:members:
:undoc-members:
:show-inheritance:

pymodbus\.framer\.binary_framer module
--------------------------------------

.. automodule:: pymodbus.framer.binary_framer
:members:
:undoc-members:
:show-inheritance:

pymodbus\.framer\.rtu_framer module
-----------------------------------

.. automodule:: pymodbus.framer.rtu_framer
:members:
:undoc-members:
:show-inheritance:

pymodbus\.framer\.socket_framer module
--------------------------------------

.. automodule:: pymodbus.framer.socket_framer
:members:
:undoc-members:
:show-inheritance:


Module contents
---------------

.. automodule:: pymodbus.framer
:members:
:undoc-members:
:show-inheritance:

2 changes: 2 additions & 0 deletions doc/source/library/pymodbus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ Subpackages

pymodbus.client
pymodbus.datastore
pymodbus.framer
pymodbus.internal
pymodbus.server


Submodules
----------

Expand Down
43 changes: 34 additions & 9 deletions examples/common/asynchronous_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
# choose the requested modbus protocol
# --------------------------------------------------------------------------- #
from pymodbus.client.async import ModbusClientProtocol
#from pymodbus.client.async import ModbusUdpClientProtocol
from pymodbus.client.async import ModbusUdpClientProtocol
from pymodbus.framer.rtu_framer import ModbusRtuFramer

# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

Expand All @@ -34,7 +37,6 @@
def dassert(deferred, callback):
def _assertor(value):
assert value

deferred.addCallback(lambda r: _assertor(callback(r)))
deferred.addErrback(lambda _: _assertor(False))

Expand All @@ -47,8 +49,20 @@ def _assertor(value):
# --------------------------------------------------------------------------- #


def processResponse(result):
log.debug(result)


def exampleRequests(client):
rr = client.read_coils(1, 1, unit=0x02)
rr.addCallback(processResponse)
rr = client.read_holding_registers(1, 1, unit=0x02)
rr.addCallback(processResponse)
rr = client.read_discrete_inputs(1, 1, unit=0x02)
rr.addCallback(processResponse)
rr = client.read_input_registers(1, 1, unit=0x02)
rr.addCallback(processResponse)
stopAsynchronousTest(client)

# --------------------------------------------------------------------------- #
# example requests
Expand All @@ -61,7 +75,16 @@ def exampleRequests(client):
# deferred assert helper(dassert).
# --------------------------------------------------------------------------- #

UNIT = 0x01

UNIT = 0x00


def stopAsynchronousTest(client):
# ----------------------------------------------------------------------- #
# close the client at some time later
# ----------------------------------------------------------------------- #
reactor.callLater(1, client.transport.loseConnection)
reactor.callLater(2, reactor.stop)

def beginAsynchronousTest(client):
rq = client.write_coil(1, True, unit=UNIT)
Expand Down Expand Up @@ -99,12 +122,8 @@ def beginAsynchronousTest(client):
rr = client.read_input_registers(1, 8, unit=UNIT)
dassert(rq, lambda r: r.registers == [20]*8) # test the expected value
dassert(rr, lambda r: r.registers == [17]*8) # test the expected value
stopAsynchronousTest(client)

# ----------------------------------------------------------------------- #
# close the client at some time later
# ----------------------------------------------------------------------- #
reactor.callLater(1, client.transport.loseConnection)
reactor.callLater(2, reactor.stop)

# --------------------------------------------------------------------------- #
# extra requests
Expand Down Expand Up @@ -134,5 +153,11 @@ def beginAsynchronousTest(client):
if __name__ == "__main__":
defer = protocol.ClientCreator(
reactor, ModbusClientProtocol).connectTCP("localhost", 5020)

# TCP server with a different framer

# defer = protocol.ClientCreator(
# reactor, ModbusClientProtocol, framer=ModbusRtuFramer).connectTCP(
# "localhost", 5020)
defer.addCallback(beginAsynchronousTest)
reactor.run()
10 changes: 6 additions & 4 deletions examples/common/asynchronous_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger("pymodbus")
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

# --------------------------------------------------------------------------- #
# state a few constants
# --------------------------------------------------------------------------- #
SERIAL_PORT = "/dev/ttyp0"
SERIAL_PORT = "/dev/ptyp0"
STATUS_REGS = (1, 2)
STATUS_COILS = (1, 3)
CLIENT_DELAY = 1
Expand Down Expand Up @@ -173,7 +175,7 @@ def write(self, response):

def main():
log.debug("Initializing the client")
framer = ModbusFramer(ClientDecoder())
framer = ModbusFramer(ClientDecoder(), client=None)
reader = LoggingLineReader()
factory = ExampleFactory(framer, reader)
SerialModbusClient(factory, SERIAL_PORT, reactor)
Expand Down
45 changes: 36 additions & 9 deletions examples/common/asynchronous_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
from pymodbus.transaction import (ModbusRtuFramer,
ModbusAsciiFramer,
ModbusBinaryFramer)

# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

Expand Down Expand Up @@ -101,18 +105,41 @@ def run_async_server():
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '1.0'
identity.MajorMinorRevision = '1.5'

# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #


# TCP Server

StartTcpServer(context, identity=identity, address=("localhost", 5020))
# StartUdpServer(context, identity=identity, address=("localhost", 502))
# StartSerialServer(context, identity=identity,
# port='/dev/pts/3', framer=ModbusRtuFramer)
# StartSerialServer(context, identity=identity,
# port='/dev/pts/3', framer=ModbusAsciiFramer)

# TCP Server with deferred reactor run

# from twisted.internet import reactor
# StartTcpServer(context, identity=identity, address=("localhost", 5020),
# defer_reactor_run=True)
# reactor.run()

# Server with RTU framer
# StartTcpServer(context, identity=identity, address=("localhost", 5020),
# framer=ModbusRtuFramer)

# UDP Server
# StartUdpServer(context, identity=identity, address=("127.0.0.1", 5020))

# RTU Server
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', framer=ModbusRtuFramer)

# ASCII Server
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', framer=ModbusAsciiFramer)

# Binary Server
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', framer=ModbusBinaryFramer)


if __name__ == "__main__":
Expand Down
Loading