Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
janiversen committed Oct 22, 2024
2 parents b34fa5e + 378bd5f commit a1c14c7
Show file tree
Hide file tree
Showing 81 changed files with 1,908 additions and 2,945 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ['3.9', '3.10', '3.11', '3.12']
python: ['3.9', '3.10', '3.11', '3.12', "3.13"]
include:
- python: '3.9'
run_lint: true
- python: '3.12'
- python: '3.13'
run_doc: true
run_lint: true
- os: macos-latest
Expand Down Expand Up @@ -113,13 +113,13 @@ jobs:
ruff check .
- name: pytest
if: ${{ (matrix.os != 'ubuntu-latest') || (matrix.python != '3.12') }}
if: ${{ (matrix.os != 'ubuntu-latest') || (matrix.python != '3.13') }}
run: |
env
pytest
- name: pytest coverage
if: ${{ (matrix.os == 'ubuntu-latest') && (matrix.python == '3.12') }}
if: ${{ (matrix.os == 'ubuntu-latest') && (matrix.python == '3.13') }}
run: |
env
pytest --cov
Expand Down
8 changes: 4 additions & 4 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Pymodbus version 3 family
-------------------------
Thanks to

- ahcm-dev
- AKJ7
- Alex
- Alex Ruddick
Expand Down Expand Up @@ -58,13 +59,12 @@ Thanks to
- julian
- Justin Standring
- Kenny Johansson
- Martyy
- Matthias Straka
- Kürşat Aktaş
- laund
- Logan Gunthorpe
- Marko Luther
- Logan Gunthorpe
- Marko Luther
- Martyy
- Máté Szabó
- Matthias Straka
- Matthias Urlichs
- Michel F
Expand Down
35 changes: 34 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,40 @@ helps make pymodbus a better product.

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

Version 3.7.4
-------------
* Clean PDU init. (#2399)
* Wrong close, when transaction do not match. (#2401)
* Remove unmaintained (not working) example contributions. (#2400)
* All pdu (incl. function code) tests to pdu directory. (#2397)
* Add `no_response_expected` argument to requests (#2385)
* Resubmit: Don't close/reopen tcp connection on single modbus message timeout (#2350)
* 100% test coverage for PDU. (#2394)
* Type DecodePDU. (#2392)
* Update to use DecodePDU. (#2391)
* Client/Server decoder renamed and moved to pdu. (#2390)
* Move client/server decoder to pdu. (#2388)
* Introducing PyModbus Guru on Gurubase.io (#2387)
* Remove IllegalFunctionRequest. (#2384)
* remove ModbusResponse. (#2383)
* Add typing to pdu base classes. (#2380)
* Updated roadmap.
* remove databuffer from framer. (#2379)
* Improve retries for sync client. (#2377)
* Move process test to framer tests (#2376)
* Framer do not check ids (#2375)
* Remove callback from framer. (#2374)
* Auto fill device ids for clients. (#2372)
* Reenable multidrop tests. (#2370)
* write_register/s accept bytes or int. (#2369)
* roadmap corrections.
* Added roadmap (not written in stone). (#2367)
* Update README to show python 3.13.
* Test on Python 3.13 (#2366)
* Use @abstractmethod (#2365)
* Corrected smaller documentation bugs. (#2364)
* README as landing page in readthedocs. (#2363)

Version 3.7.3
-------------
* 100% test coverage of framers (#2359)
Expand Down Expand Up @@ -44,7 +78,6 @@ Version 3.7.3
* fixed type hints for write_register and write_registers (#2309)
* Remove _header from framers. (#2305)


Version 3.7.2
-------------
* Correct README
Expand Down
5 changes: 3 additions & 2 deletions MAKE_RELEASE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ Prepare/make release on dev.
* Control / Update API_changes.rst
* Update CHANGELOG.rst
* Add commits from last release, but selectively !
git log --oneline v3.7.3..HEAD > commit.log
git log --pretty="%an" v3.7.3..HEAD | sort -uf > authors.log
git log --oneline v3.7.4..HEAD > commit.log
git log --pretty="%an" v3.7.4..HEAD | sort -uf > authors.log
update AUTHORS.rst and CHANGELOG.rst
update roadmap.rst
cd doc; ./build_html
* rm -rf build/* dist/*
* python3 -m build
Expand Down
9 changes: 6 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ PyModbus - A Python Modbus Stack
.. image:: https://pepy.tech/badge/pymodbus
:target: https://pepy.tech/project/pymodbus
:alt: Downloads
.. image:: https://img.shields.io/badge/Gurubase-Ask%20PyModbus%20Guru-006BFF
:target: https://gurubase.io/g/pymodbus
:alt: PyModbus Guru

Pymodbus is a full Modbus protocol implementation offering client/server with synchronous/asynchronous API a well as simulators.
Pymodbus is a full Modbus protocol implementation offering client/server with synchronous/asynchronous API and simulators.

Our releases is defined as X.Y.Z, and we have strict rules what to release when:

Expand All @@ -23,7 +26,7 @@ Upgrade examples:
- 3.6.1 -> 3.7.0: Smaller changes to the pymodbus calls might be needed
- 2.5.4 -> 3.0.0: Major changes in the application might be needed

Current release is `3.7.3 <https://github.com/pymodbus-dev/pymodbus/releases/tag/v3.7.3>`_.
Current release is `3.7.4 <https://github.com/pymodbus-dev/pymodbus/releases/tag/v3.7.4>`_.

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

Expand Down Expand Up @@ -57,7 +60,7 @@ Common features
* very lightweight project
* requires Python >= 3.9
* thorough test suite, that test all corners of the library
* automatically tested on Windows, Linux and MacOS combined with python 3.9 - 3.12
* automatically tested on Windows, Linux and MacOS combined with python 3.9 - 3.13
* strongly typed API (py.typed present)

The modbus protocol specification: Modbus_Application_Protocol_V1_1b3.pdf can be found on
Expand Down
5 changes: 3 additions & 2 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Please select a topic in the left hand column.
:caption: Contents:
:hidden:

source/readme
source/api_changes
source/client
source/server
Expand All @@ -17,5 +16,7 @@ Please select a topic in the left hand column.
source/examples
source/authors
source/changelog
source/api_changes
source/internals
source/roadmap

.. include:: ../README.rst
1 change: 0 additions & 1 deletion doc/source/README.rst

This file was deleted.

Binary file modified doc/source/_static/examples.tgz
Binary file not shown.
Binary file modified doc/source/_static/examples.zip
Binary file not shown.
19 changes: 13 additions & 6 deletions doc/source/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ The line :mod:`result = await client.read_coils(2, 3, slave=1)` is an example of

The last line :mod:`client.close()` closes the connection and render the object inactive.

Retry logic for async clients
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If no response is received to a request (call), it is retried (parameter retries) times, if not successful
an exception response is returned, BUT the connection is not touched.

If 3 consequitve requests (calls) do not receive a response, the connection is terminated.

Development notes
^^^^^^^^^^^^^^^^^

Expand All @@ -191,14 +199,12 @@ The logical devices represented by the device is addressed with the :mod:`slave=
With **Serial**, the comm port is defined when creating the object.
The physical devices are addressed with the :mod:`slave=` parameter.

:mod:`slave=0` is used as broadcast in order to address all devices.
However experience shows that modern devices do not allow broadcast, mostly because it is
inheriently dangerous. With :mod:`slave=0` the application can get upto 254 responses on a single request,
and this is not handled with the normal API calls!
:mod:`slave=0` is defined as broadcast in the modbus standard, but pymodbus treats is a normal device.

The simple request calls (mixin) do NOT support broadcast, if an application wants to use broadcast
it must call :mod:`client.execute` and deal with the responses.
If an application is expecting multiple responses to a broadcast request, it must call :mod:`client.execute` and deal with the responses.

If no response is expected to a request, the :mod:`no_response_expected=True` argument can be used
in the normal API calls, this will cause the call to return imidiatble with :mod:`None`


Client response handling
Expand Down Expand Up @@ -227,6 +233,7 @@ And in case of read retrieve the data depending on type of request
- :mod:`rr.bits` is set for coils / input_register requests
- :mod:`rr.registers` is set for other requests

Remark if using :mod:`no_response_expected=True` rr will always be None.

Client interface classes
------------------------
Expand Down
10 changes: 5 additions & 5 deletions doc/source/library/pymodbus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ Extra functions
:undoc-members:
:show-inheritance:

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

.. automodule:: pymodbus.payload
:members:
:undoc-members:
Expand All @@ -45,6 +40,11 @@ Extra functions
PDU classes
===========

.. automodule:: pymodbus.pdu.decoders
:members:
:undoc-members:
:show-inheritance:

.. automodule:: pymodbus.pdu.bit_read_message
:members:
:undoc-members:
Expand Down
3 changes: 2 additions & 1 deletion doc/source/repl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ Pymodbus REPL (Read Evaluate Print Loop)

.. raw:: html

<p style="color: red;"><strong>Warning:</strong> The Pymodbus REPL documentation is not updated.</p>
<p style="color: red;"><strong>Warning:</strong> The Pymodbus REPL documentation is not updated,
because it lives in a different repo.</p>

Installation
------------
Expand Down
40 changes: 40 additions & 0 deletions doc/source/roadmap.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Roadmap
=======

The roadmap is not a finite plan, but merely an expression of intentions !

Pymodbus development is mainly driven by contributors, who have an itch, and provide a solution for the community.
The maintainers are very open to these pull request, and ONLY work to secure that:

- it does not break existing usage/functionality (PR put on hold for next API change release)
- it is a generic feature (e.g. not just for serial 9.600 bps)
- it have proper test cases, to ensure against side effects.

It is important to note the maintainer do NOT reject ANY pull request that emcompases the above criteria.
It is the community that decides how pymodbus evolves NOT the maintainers !

The following bullet points are what the maintainers focus on:

- 3.7.5, bug fix release, hopefully with:
- Simplify PDU classes
- Simplify transaction manager (central control point)
- Remove ModbusControlBlock
- 3.7.6, bug fix release, with:
- Not planned
- 3.8.0, with:
- new transaction handling
- transaction 100% coverage
- skip_encode, zero_mode parameters removed
- 4.0.0, with:
- client async with sync/async API
- Only one datastore, but with different API`s
- Simulator standard in server
- GUI client, to analyze devices
- GUI server, to simulate devices

All contributions are WELCOME, and we (the maintainers) are always open to talk about ideas,
best way is via `discussions <https://github.com/pymodbus-dev/pymodbus/discussions>`_ on github.

We have lately decided, that we do strictly follow the `modbus org <https://modbus.org>`_ standard,
but we also accept vendor specific (like Huawei) pull requests, as long as they extend the standard or are actitvated with
a specific argument like --huawei.
26 changes: 14 additions & 12 deletions examples/client_custom_msg.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from pymodbus import FramerType
from pymodbus.client import AsyncModbusTcpClient as ModbusClient
from pymodbus.pdu import ModbusExceptions, ModbusRequest, ModbusResponse
from pymodbus.pdu import ModbusExceptions, ModbusPDU
from pymodbus.pdu.bit_read_message import ReadCoilsRequest


Expand All @@ -26,19 +26,20 @@
# Since the function code is already registered with the decoder factory,
# this will be decoded as a read coil response. If you implement a new
# method that is not currently implemented, you must register the request
# and response with a ClientDecoder factory.
# and response with the active DecodePDU object.
# --------------------------------------------------------------------------- #


class CustomModbusResponse(ModbusResponse):
class CustomModbusPDU(ModbusPDU):
"""Custom modbus response."""

function_code = 55
_rtu_byte_count_pos = 2

def __init__(self, values=None, slave=1, transaction=0, skip_encode=False):
"""Initialize."""
ModbusResponse.__init__(self, slave, transaction, skip_encode)
super().__init__()
super().setData(slave, transaction, skip_encode)
self.values = values or []

def encode(self):
Expand All @@ -62,15 +63,16 @@ def decode(self, data):
self.values.append(struct.unpack(">H", data[i : i + 2])[0])


class CustomModbusRequest(ModbusRequest):
class CustomRequest(ModbusPDU):
"""Custom modbus request."""

function_code = 55
_rtu_frame_size = 8

def __init__(self, address=None, slave=1, transaction=0, skip_encode=False):
"""Initialize."""
ModbusRequest.__init__(self, slave, transaction, skip_encode)
super().__init__()
super().setData(slave, transaction, skip_encode)
self.address = address
self.count = 16

Expand All @@ -89,7 +91,7 @@ def execute(self, context):
if not context.validate(self.function_code, self.address, self.count):
return self.doException(ModbusExceptions.IllegalAddress)
values = context.getValues(self.function_code, self.address, self.count)
return CustomModbusResponse(values)
return CustomModbusPDU(values)


# --------------------------------------------------------------------------- #
Expand Down Expand Up @@ -122,18 +124,18 @@ async def main(host="localhost", port=5020):
await client.connect()

# create a response object to control it works
CustomModbusResponse()
CustomModbusPDU()

# new modbus function code.
client.register(CustomModbusResponse)
client.register(CustomModbusPDU)
slave=1
request = CustomModbusRequest(32, slave=slave)
result = await client.execute(request)
request = CustomRequest(32, slave=slave)
result = await client.execute(False, request)
print(result)

# inherited request
request = Read16CoilsRequest(32, slave)
result = await client.execute(request)
result = await client.execute(False, request)
print(result)


Expand Down
Loading

0 comments on commit a1c14c7

Please sign in to comment.