Skip to content

Commit 7fc8d3e

Browse files
committed
Merge branch 'dev'
2 parents ee018e0 + ea0bc1d commit 7fc8d3e

31 files changed

+891
-1128
lines changed

CHANGELOG.rst

+10-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ helps make pymodbus a better product.
77

88
:ref:`Authors`: contains a complete list of volunteers have contributed to each major version.
99

10+
Version 3.8.3
11+
-------------
12+
* Remove deprecate from payload. (#2532)
13+
* Add background parameter to servers. (#2529)
14+
* Split async_io.py and simplify server start/stop. (#2528)
15+
* Update custom_msg example to include server. (#2527)
16+
* Move repl doc to repl repo. (#2522)
17+
* Add API to set max until disconnect. (#2521)
18+
1019
Version 3.8.2
1120
-------------
1221
* Asyncio future removed from sync client. (#2514)
@@ -31,7 +40,7 @@ Version 3.8.0
3140
* Add trace API to server. (#2479)
3241
* Add trace API for client. (#2478)
3342
* Integrate TransactionManager in server. (#2475)
34-
* Rename test/sub_. (#2473)
43+
* Rename test/sub. (#2473)
3544
* Check server closes file descriptors. (#2472)
3645
* Update http_server.py (#2471)
3746
* Restrict write_registers etc to list[int]. (#2469)

MAKE_RELEASE.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ Prepare/make release on dev.
1515
* Control / Update API_changes.rst
1616
* Update CHANGELOG.rst
1717
* Add commits from last release, but selectively !
18-
git log --oneline v3.8.0..HEAD > commit.log
19-
git log --pretty="%an" v3.8.0..HEAD | sort -uf > authors.log
18+
git log --oneline v3.8.3..HEAD > commit.log
19+
git log --pretty="%an" v3.8.3..HEAD | sort -uf > authors.log
2020
update AUTHORS.rst and CHANGELOG.rst
2121
cd doc; ./build_html
2222
* rm -rf build/* dist/*

README.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ Our releases is defined as X.Y.Z, and we have strict rules what to release when:
2222

2323
Upgrade examples:
2424

25-
- 3.6.1 -> 3.6.9: just plugin the new version, no changes needed.
26-
- 3.6.1 -> 3.7.0: Smaller changes to the pymodbus calls might be needed
25+
- 3.8.1 -> 3.8.3: just plugin the new version, no changes needed.
26+
- 3.7.1 -> 3.8.0: Smaller changes to the pymodbus calls might be needed
2727
- 2.5.4 -> 3.0.0: Major changes in the application might be needed
2828

29-
Current release is `3.8.2 <https://github.com/pymodbus-dev/pymodbus/releases/tag/v3.8.2>`_.
29+
Current release is `3.8.3 <https://github.com/pymodbus-dev/pymodbus/releases/tag/v3.8.3>`_.
3030

3131
Bleeding edge (not released) is `dev <https://github.com/pymodbus-dev/pymodbus/tree/dev>`_.
3232

doc/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"sphinx_rtd_theme",
2222
"sphinx.ext.autosectionlabel"
2323
]
24-
source_suffix = [".rst"]
24+
source_suffix = {'.rst': 'restructuredtext'}
2525
root_doc = "index"
2626
project = "PyModbus"
2727
copyright = "See license"

doc/source/_static/examples.tgz

-119 Bytes
Binary file not shown.

doc/source/_static/examples.zip

1.63 KB
Binary file not shown.

doc/source/examples.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ Source: :github:`examples/client_calls.py`
7979
:noindex:
8080

8181

82-
Client custom message
83-
^^^^^^^^^^^^^^^^^^^^^
84-
Source: :github:`examples/client_custom_msg.py`
82+
Custom message
83+
^^^^^^^^^^^^^^
84+
Source: :github:`examples/custom_msg.py`
8585

86-
.. automodule:: examples.client_custom_msg
86+
.. automodule:: examples.custom_msg
8787
:undoc-members:
8888
:noindex:
8989

doc/source/repl.rst

+2-305
Large diffs are not rendered by default.

doc/source/roadmap.rst

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ It is the community that decides how pymodbus evolves NOT the maintainers !
1515

1616
The following bullet points are what the maintainers focus on:
1717

18-
- 3.8.3 bug fix release, with:
18+
- 3.8.4 bug fix release, with:
1919
- Currently not planned
2020
- 3.9.0, with:
2121
- All of branch wait_next_api
2222
- ModbusControlBlock pr slave
2323
- New custom PDU (function codes)
24+
- Simulator datastore, with simple configuration
2425
- Remove remote_datastore
25-
- Remove BinaryPayload
2626
- 4.0.0, with:
27+
- Remove BinaryPayload
28+
- Server becomes Simulator
2729
- New serial forwarder
2830
- client async with sync/async API
2931
- Only one datastore, but with different API`s
30-
- Simulator standard in server
3132
- GUI client, to analyze devices
3233
- GUI server, to simulate devices
3334

examples/contrib/serial_forwarder.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pymodbus.client import ModbusSerialClient
1212
from pymodbus.datastore import ModbusServerContext
1313
from pymodbus.datastore.remote import RemoteSlaveContext
14-
from pymodbus.server.async_io import ModbusTcpServer
14+
from pymodbus.server import ModbusTcpServer
1515

1616

1717
_logger = logging.getLogger(__file__)

examples/client_custom_msg.py examples/custom_msg.py

+31-15
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@
1515

1616
from pymodbus import FramerType
1717
from pymodbus.client import AsyncModbusTcpClient as ModbusClient
18+
from pymodbus.datastore import (
19+
ModbusSequentialDataBlock,
20+
ModbusServerContext,
21+
ModbusSlaveContext,
22+
)
1823
from pymodbus.exceptions import ModbusIOException
19-
from pymodbus.pdu import ExceptionResponse, ModbusPDU
24+
from pymodbus.pdu import ModbusPDU
2025
from pymodbus.pdu.bit_message import ReadCoilsRequest
26+
from pymodbus.server import ServerAsyncStop, StartAsyncTcpServer
2127

2228

2329
# --------------------------------------------------------------------------- #
@@ -31,7 +37,7 @@
3137
# --------------------------------------------------------------------------- #
3238

3339

34-
class CustomModbusPDU(ModbusPDU):
40+
class CustomModbusResponse(ModbusPDU):
3541
"""Custom modbus response."""
3642

3743
function_code = 55
@@ -73,7 +79,7 @@ def __init__(self, address=None, slave=1, transaction=0):
7379
"""Initialize."""
7480
super().__init__(dev_id=slave, transaction_id=transaction)
7581
self.address = address
76-
self.count = 16
82+
self.count = 2
7783

7884
def encode(self):
7985
"""Encode."""
@@ -83,14 +89,10 @@ def decode(self, data):
8389
"""Decode."""
8490
self.address, self.count = struct.unpack(">HH", data)
8591

86-
def execute(self, context):
92+
async def update_datastore(self, context: ModbusSlaveContext) -> ModbusPDU:
8793
"""Execute."""
88-
if not 1 <= self.count <= 0x7D0:
89-
return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_VALUE)
90-
if not context.validate(self.function_code, self.address, self.count):
91-
return ExceptionResponse(self.function_code, ExceptionResponse.ILLEGAL_ADDRESS)
92-
values = context.getValues(self.function_code, self.address, self.count)
93-
return CustomModbusPDU(values)
94+
_ = context
95+
return CustomModbusResponse()
9496

9597

9698
# --------------------------------------------------------------------------- #
@@ -119,14 +121,25 @@ def __init__(self, address, slave=1, transaction=0):
119121

120122
async def main(host="localhost", port=5020):
121123
"""Run versions of read coil."""
124+
store = ModbusServerContext(slaves=ModbusSlaveContext(
125+
di=ModbusSequentialDataBlock(0, [17] * 100),
126+
co=ModbusSequentialDataBlock(0, [17] * 100),
127+
hr=ModbusSequentialDataBlock(0, [17] * 100),
128+
ir=ModbusSequentialDataBlock(0, [17] * 100),
129+
),
130+
single=True
131+
)
132+
task = asyncio.create_task(StartAsyncTcpServer(
133+
context=store,
134+
address=(host, port),
135+
custom_functions=[CustomRequest])
136+
)
137+
await asyncio.sleep(0.1)
122138
async with ModbusClient(host=host, port=port, framer=FramerType.SOCKET) as client:
123139
await client.connect()
124140

125-
# create a response object to control it works
126-
CustomModbusPDU()
127-
128-
# new modbus function code.
129-
client.register(CustomModbusPDU)
141+
# add new modbus function code.
142+
client.register(CustomModbusResponse)
130143
slave=1
131144
request1 = CustomRequest(32, slave=slave)
132145
try:
@@ -140,6 +153,9 @@ async def main(host="localhost", port=5020):
140153
request2 = Read16CoilsRequest(32, slave)
141154
result = await client.execute(False, request2)
142155
print(result)
156+
await ServerAsyncStop()
157+
task.cancel()
158+
await task
143159

144160

145161
if __name__ == "__main__":

examples/server_async.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,15 @@ def setup_server(description=None, context=None, cmdline=None):
136136
return args
137137

138138

139-
async def run_async_server(args):
139+
async def run_async_server(args) -> None:
140140
"""Run server."""
141141
txt = f"### start ASYNC server, listening on {args.port} - {args.comm}"
142142
_logger.info(txt)
143-
server = None
144143
if args.comm == "tcp":
145144
address = (args.host if args.host else "", args.port if args.port else None)
146-
server = await StartAsyncTcpServer(
145+
await StartAsyncTcpServer(
147146
context=args.context, # Data storage
148147
identity=args.identity, # server identify
149-
# TBD host=
150-
# TBD port=
151148
address=address, # listen address
152149
# custom_functions=[], # allow custom handling
153150
framer=args.framer, # The framer strategy to use
@@ -160,7 +157,7 @@ async def run_async_server(args):
160157
args.host if args.host else "127.0.0.1",
161158
args.port if args.port else None,
162159
)
163-
server = await StartAsyncUdpServer(
160+
await StartAsyncUdpServer(
164161
context=args.context, # Data storage
165162
identity=args.identity, # server identify
166163
address=address, # listen address
@@ -173,7 +170,7 @@ async def run_async_server(args):
173170
elif args.comm == "serial":
174171
# socat -d -d PTY,link=/tmp/ptyp0,raw,echo=0,ispeed=9600
175172
# PTY,link=/tmp/ttyp0,raw,echo=0,ospeed=9600
176-
server = await StartAsyncSerialServer(
173+
await StartAsyncSerialServer(
177174
context=args.context, # Data storage
178175
identity=args.identity, # server identify
179176
# timeout=1, # waiting time for request to complete
@@ -190,9 +187,8 @@ async def run_async_server(args):
190187
)
191188
elif args.comm == "tls":
192189
address = (args.host if args.host else "", args.port if args.port else None)
193-
server = await StartAsyncTlsServer(
190+
await StartAsyncTlsServer(
194191
context=args.context, # Data storage
195-
host="localhost", # define tcp address where to connect to.
196192
# port=port, # on which port
197193
identity=args.identity, # server identify
198194
# custom_functions=[], # allow custom handling
@@ -210,10 +206,9 @@ async def run_async_server(args):
210206
# broadcast_enable=False, # treat slave 0 as broadcast address,
211207
# timeout=1, # waiting time for request to complete
212208
)
213-
return server
214209

215210

216-
async def async_helper():
211+
async def async_helper() -> None:
217212
"""Combine setup and run."""
218213
_logger.info("Starting...")
219214
run_args = setup_server(description="Run asynchronous server.")

examples/server_sync.py

+8-13
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,15 @@
6262
_logger.setLevel("DEBUG")
6363

6464

65-
def run_sync_server(args):
65+
def run_sync_server(args) -> None:
6666
"""Run server."""
6767
txt = f"### start SYNC server, listening on {args.port} - {args.comm}"
6868
_logger.info(txt)
69-
server = None
7069
if args.comm == "tcp":
7170
address = ("", args.port) if args.port else None
72-
server = StartTcpServer(
71+
StartTcpServer(
7372
context=args.context, # Data storage
7473
identity=args.identity, # server identify
75-
# TBD host=
76-
# TBD port=
7774
address=address, # listen address
7875
# custom_functions=[], # allow custom handling
7976
framer=args.framer, # The framer strategy to use
@@ -83,7 +80,7 @@ def run_sync_server(args):
8380
)
8481
elif args.comm == "udp":
8582
address = ("127.0.0.1", args.port) if args.port else None
86-
server = StartUdpServer(
83+
StartUdpServer(
8784
context=args.context, # Data storage
8885
identity=args.identity, # server identify
8986
address=address, # listen address
@@ -96,7 +93,7 @@ def run_sync_server(args):
9693
elif args.comm == "serial":
9794
# socat -d -d PTY,link=/tmp/ptyp0,raw,echo=0,ispeed=9600
9895
# PTY,link=/tmp/ttyp0,raw,echo=0,ospeed=9600
99-
server = StartSerialServer(
96+
StartSerialServer(
10097
context=args.context, # Data storage
10198
identity=args.identity, # server identify
10299
# timeout=1, # waiting time for request to complete
@@ -113,9 +110,8 @@ def run_sync_server(args):
113110
)
114111
elif args.comm == "tls":
115112
address = ("", args.port) if args.port else None
116-
server = StartTlsServer(
113+
StartTlsServer(
117114
context=args.context, # Data storage
118-
host="localhost", # define tcp address where to connect to.
119115
# port=port, # on which port
120116
identity=args.identity, # server identify
121117
# custom_functions=[], # allow custom handling
@@ -133,14 +129,13 @@ def run_sync_server(args):
133129
# broadcast_enable=False, # treat slave 0 as broadcast address,
134130
# timeout=1, # waiting time for request to complete
135131
)
136-
return server
137132

138133

139-
def sync_helper():
134+
def sync_helper() -> None:
140135
"""Combine setup and run."""
141136
run_args = server_async.setup_server(description="Run synchronous server.")
142-
server = run_sync_server(run_args)
143-
server.shutdown()
137+
run_sync_server(run_args)
138+
# server.shutdown()
144139

145140

146141
if __name__ == "__main__":

pymodbus/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@
1818
from pymodbus.pdu import ExceptionResponse
1919

2020

21-
__version__ = "3.8.2"
21+
__version__ = "3.8.3"
2222
__version_full__ = f"[pymodbus, version {__version__}]"

0 commit comments

Comments
 (0)