Skip to content

Commit 9d2c864

Browse files
authored
Pymodbus 1.5.0 (#285)
* Modbus sync client timing enhancements #221 Fix TCP server #256, #260 * Fix #221 timing enhancements, #188 workarounds * 1. #284 Async servers - Option to start reactor outside Start<server>Server function 2. #283 Fix BinaryPayloadDecoder/Builder issues when using with pymodbus server 3. #278 Fix issue with sync/async servers failing to handle requests with transaction id > 255 4. #221 Move timing and transcational logic to framers for sync clients 5. #221 More debug logs for sync clients 6. Misc updates with examples and minor enhancements * 1. #277 MEI message reception issue with UDP client 2. Fix unit tests 3. Update changelog * Patch 1 (#292) * Fix #289 and other misc enhancements * Replace nosetest with pytest * Update Changelog * serial sync client wait till timeout/some data is available in read buffer + update changelog * serial sync client read updates when timeout is None and Zero * fix sync client unit test and example
1 parent 180060a commit 9d2c864

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2367
-1453
lines changed

CHANGELOG.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
Version 1.5.0
2+
------------------------------------------------------------
3+
* 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
4+
5+
`client = Client(..., retry_on_empty=True)`
6+
7+
* Fix tcp servers (sync/async) not processing requests with transaction id > 255
8+
* Introduce new api to check if the received response is an error or not (response.isError())
9+
* Move timing logic to framers so that irrespective of client, correct timing logics are followed.
10+
* Move framers from transaction.py to respective modules
11+
* Fix modbus payload builder and decoder
12+
* Async servers can now have an option to defer `reactor.run()` when using `Start<Tcp/Serial/Udo>Server(...,defer_reactor_run=True)`
13+
* Fix UDP client issue while handling MEI messages (ReadDeviceInformationRequest)
14+
* Add expected response lengths for WriteMultipleCoilRequest and WriteMultipleRegisterRequest
15+
* Fix struct errors while decoding stray response
16+
* Modbus read retries works only when empty/no message is received
17+
* Change test runner from nosetest to pytest
18+
* Fix Misc examples
19+
120
Version 1.4.0
221
------------------------------------------------------------
322
* Bug fix Modbus TCP client reading incomplete data

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ check: install
3939

4040
test: install
4141
@pip install --quiet --requirement=requirements-tests.txt
42-
@nosetests --with-coverage --cover-html
42+
@py.test
4343
@coverage report --fail-under=90
4444

4545
tox: install
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
pymodbus\.framer package
2+
========================
3+
4+
Submodules
5+
----------
6+
7+
pymodbus\.framer\.ascii_framer module
8+
-------------------------------------
9+
10+
.. automodule:: pymodbus.framer.ascii_framer
11+
:members:
12+
:undoc-members:
13+
:show-inheritance:
14+
15+
pymodbus\.framer\.binary_framer module
16+
--------------------------------------
17+
18+
.. automodule:: pymodbus.framer.binary_framer
19+
:members:
20+
:undoc-members:
21+
:show-inheritance:
22+
23+
pymodbus\.framer\.rtu_framer module
24+
-----------------------------------
25+
26+
.. automodule:: pymodbus.framer.rtu_framer
27+
:members:
28+
:undoc-members:
29+
:show-inheritance:
30+
31+
pymodbus\.framer\.socket_framer module
32+
--------------------------------------
33+
34+
.. automodule:: pymodbus.framer.socket_framer
35+
:members:
36+
:undoc-members:
37+
:show-inheritance:
38+
39+
40+
Module contents
41+
---------------
42+
43+
.. automodule:: pymodbus.framer
44+
:members:
45+
:undoc-members:
46+
:show-inheritance:
47+

doc/source/library/pymodbus.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ Subpackages
88

99
pymodbus.client
1010
pymodbus.datastore
11+
pymodbus.framer
1112
pymodbus.internal
1213
pymodbus.server
1314

15+
1416
Submodules
1517
----------
1618

examples/common/asynchronous_client.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
# choose the requested modbus protocol
1717
# --------------------------------------------------------------------------- #
1818
from pymodbus.client.async import ModbusClientProtocol
19-
#from pymodbus.client.async import ModbusUdpClientProtocol
19+
from pymodbus.client.async import ModbusUdpClientProtocol
20+
from pymodbus.framer.rtu_framer import ModbusRtuFramer
2021

2122
# --------------------------------------------------------------------------- #
2223
# configure the client logging
2324
# --------------------------------------------------------------------------- #
2425
import logging
25-
logging.basicConfig()
26+
FORMAT = ('%(asctime)-15s %(threadName)-15s'
27+
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
28+
logging.basicConfig(format=FORMAT)
2629
log = logging.getLogger()
2730
log.setLevel(logging.DEBUG)
2831

@@ -34,7 +37,6 @@
3437
def dassert(deferred, callback):
3538
def _assertor(value):
3639
assert value
37-
3840
deferred.addCallback(lambda r: _assertor(callback(r)))
3941
deferred.addErrback(lambda _: _assertor(False))
4042

@@ -47,8 +49,20 @@ def _assertor(value):
4749
# --------------------------------------------------------------------------- #
4850

4951

52+
def processResponse(result):
53+
log.debug(result)
54+
55+
5056
def exampleRequests(client):
5157
rr = client.read_coils(1, 1, unit=0x02)
58+
rr.addCallback(processResponse)
59+
rr = client.read_holding_registers(1, 1, unit=0x02)
60+
rr.addCallback(processResponse)
61+
rr = client.read_discrete_inputs(1, 1, unit=0x02)
62+
rr.addCallback(processResponse)
63+
rr = client.read_input_registers(1, 1, unit=0x02)
64+
rr.addCallback(processResponse)
65+
stopAsynchronousTest(client)
5266

5367
# --------------------------------------------------------------------------- #
5468
# example requests
@@ -61,7 +75,16 @@ def exampleRequests(client):
6175
# deferred assert helper(dassert).
6276
# --------------------------------------------------------------------------- #
6377

64-
UNIT = 0x01
78+
79+
UNIT = 0x00
80+
81+
82+
def stopAsynchronousTest(client):
83+
# ----------------------------------------------------------------------- #
84+
# close the client at some time later
85+
# ----------------------------------------------------------------------- #
86+
reactor.callLater(1, client.transport.loseConnection)
87+
reactor.callLater(2, reactor.stop)
6588

6689
def beginAsynchronousTest(client):
6790
rq = client.write_coil(1, True, unit=UNIT)
@@ -99,12 +122,8 @@ def beginAsynchronousTest(client):
99122
rr = client.read_input_registers(1, 8, unit=UNIT)
100123
dassert(rq, lambda r: r.registers == [20]*8) # test the expected value
101124
dassert(rr, lambda r: r.registers == [17]*8) # test the expected value
125+
stopAsynchronousTest(client)
102126

103-
# ----------------------------------------------------------------------- #
104-
# close the client at some time later
105-
# ----------------------------------------------------------------------- #
106-
reactor.callLater(1, client.transport.loseConnection)
107-
reactor.callLater(2, reactor.stop)
108127

109128
# --------------------------------------------------------------------------- #
110129
# extra requests
@@ -134,5 +153,11 @@ def beginAsynchronousTest(client):
134153
if __name__ == "__main__":
135154
defer = protocol.ClientCreator(
136155
reactor, ModbusClientProtocol).connectTCP("localhost", 5020)
156+
157+
# TCP server with a different framer
158+
159+
# defer = protocol.ClientCreator(
160+
# reactor, ModbusClientProtocol, framer=ModbusRtuFramer).connectTCP(
161+
# "localhost", 5020)
137162
defer.addCallback(beginAsynchronousTest)
138163
reactor.run()

examples/common/asynchronous_processor.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@
2626
# configure the client logging
2727
# --------------------------------------------------------------------------- #
2828
import logging
29-
logging.basicConfig()
30-
log = logging.getLogger("pymodbus")
29+
FORMAT = ('%(asctime)-15s %(threadName)-15s'
30+
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
31+
logging.basicConfig(format=FORMAT)
32+
log = logging.getLogger()
3133
log.setLevel(logging.DEBUG)
3234

3335
# --------------------------------------------------------------------------- #
3436
# state a few constants
3537
# --------------------------------------------------------------------------- #
36-
SERIAL_PORT = "/dev/ttyp0"
38+
SERIAL_PORT = "/dev/ptyp0"
3739
STATUS_REGS = (1, 2)
3840
STATUS_COILS = (1, 3)
3941
CLIENT_DELAY = 1
@@ -173,7 +175,7 @@ def write(self, response):
173175

174176
def main():
175177
log.debug("Initializing the client")
176-
framer = ModbusFramer(ClientDecoder())
178+
framer = ModbusFramer(ClientDecoder(), client=None)
177179
reader = LoggingLineReader()
178180
factory = ExampleFactory(framer, reader)
179181
SerialModbusClient(factory, SERIAL_PORT, reactor)

examples/common/asynchronous_server.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@
1717
from pymodbus.device import ModbusDeviceIdentification
1818
from pymodbus.datastore import ModbusSequentialDataBlock
1919
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
20-
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
20+
from pymodbus.transaction import (ModbusRtuFramer,
21+
ModbusAsciiFramer,
22+
ModbusBinaryFramer)
2123

2224
# --------------------------------------------------------------------------- #
2325
# configure the service logging
2426
# --------------------------------------------------------------------------- #
2527
import logging
26-
logging.basicConfig()
28+
FORMAT = ('%(asctime)-15s %(threadName)-15s'
29+
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
30+
logging.basicConfig(format=FORMAT)
2731
log = logging.getLogger()
2832
log.setLevel(logging.DEBUG)
2933

@@ -101,18 +105,41 @@ def run_async_server():
101105
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
102106
identity.ProductName = 'Pymodbus Server'
103107
identity.ModelName = 'Pymodbus Server'
104-
identity.MajorMinorRevision = '1.0'
108+
identity.MajorMinorRevision = '1.5'
105109

106110
# ----------------------------------------------------------------------- #
107111
# run the server you want
108112
# ----------------------------------------------------------------------- #
109-
113+
114+
# TCP Server
115+
110116
StartTcpServer(context, identity=identity, address=("localhost", 5020))
111-
# StartUdpServer(context, identity=identity, address=("localhost", 502))
112-
# StartSerialServer(context, identity=identity,
113-
# port='/dev/pts/3', framer=ModbusRtuFramer)
114-
# StartSerialServer(context, identity=identity,
115-
# port='/dev/pts/3', framer=ModbusAsciiFramer)
117+
118+
# TCP Server with deferred reactor run
119+
120+
# from twisted.internet import reactor
121+
# StartTcpServer(context, identity=identity, address=("localhost", 5020),
122+
# defer_reactor_run=True)
123+
# reactor.run()
124+
125+
# Server with RTU framer
126+
# StartTcpServer(context, identity=identity, address=("localhost", 5020),
127+
# framer=ModbusRtuFramer)
128+
129+
# UDP Server
130+
# StartUdpServer(context, identity=identity, address=("127.0.0.1", 5020))
131+
132+
# RTU Server
133+
# StartSerialServer(context, identity=identity,
134+
# port='/dev/ttyp0', framer=ModbusRtuFramer)
135+
136+
# ASCII Server
137+
# StartSerialServer(context, identity=identity,
138+
# port='/dev/ttyp0', framer=ModbusAsciiFramer)
139+
140+
# Binary Server
141+
# StartSerialServer(context, identity=identity,
142+
# port='/dev/ttyp0', framer=ModbusBinaryFramer)
116143

117144

118145
if __name__ == "__main__":

0 commit comments

Comments
 (0)