Skip to content
Closed
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
3 changes: 2 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[run]
omit =
pymodbus/repl/*
pymodbus/internal/*
pymodbus/internal/*
pymodbus/server/asyncio.py
26 changes: 26 additions & 0 deletions .github/workflows/pythonpublish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Upload Python Package

on:
release:
types: [created]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ matrix:
include:
- os: linux
python: "2.7"
- os: linux
python: "3.4"
- os: linux
python: "3.5"
- os: linux
python: "3.6"
# - os: linux
# python: "3.7"
- os: linux
python: "3.7"
- os: osx
osx_image: xcode8.3
language: generic
Expand Down
38 changes: 29 additions & 9 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
Version 2.3.0
-----------------------------------------------------------
* Support Modbus TLS (client / server)
* Distribute license with source
* BinaryPayloadDecoder/Encoder now supports float16 on python3.6 and above
* Fix asyncio UDP client/server
* Minor cosmetic updates

Version 2.3.0rc1
-----------------------------------------------------------
* Asyncio Server implementation (Python 3.7 and above only)
* Bug fix for DiagnosticStatusResponse when odd sized response is received
* Remove Pycrypto from dependencies and include cryptodome instead
* Remove `SIX` requirement pinned to exact version.
* Minor bug-fixes in documentations.


Version 2.2.0
-----------------------------------------------------------
**NOTE: Supports python 3.7, async client is now moved to pymodbus/client/asychronous**
```
from pymodbus.client.asynchronous import ModbusTcpClient
```


.. code-block:: python

from pymodbus.client.asynchronous import ModbusTcpClient


* Support Python 3.7
* Fix to task cancellations and CRC errors for async serial clients.
Expand All @@ -25,13 +45,13 @@ from pymodbus.client.asynchronous import ModbusTcpClient
* Fix regression introduced in 2.2.0rc2 (Modbus sync client transaction failing)
* Minor update in factory.py, now server logs prints received request instead of only function code

```
# Now
DEBUG:pymodbus.factory:Factory Request[ReadInputRegistersRequest: 4]
# Before
DEBUG:pymodbus.factory:Factory Request[4]
.. code-block:: bash

# Now
# DEBUG:pymodbus.factory:Factory Request[ReadInputRegistersRequest: 4]
# Before
# DEBUG:pymodbus.factory:Factory Request[4]

```


Version 2.1.0
Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
include requirements.txt
include README.rst
include CHANGELOG.rst
include CHANGELOG.rst
include LICENSE
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ get lost in the noise: http://groups.google.com/group/pymodbus or
at gitter: https://gitter.im/pymodbus_dev/Lobby

------------------------------------------------------------
Pymodbus REPL (Read Evaluate Procee Loop)
Pymodbus REPL (Read Evaluate Print Loop)
------------------------------------------------------------
Starting with Pymodbus 2.x, pymodbus library comes with handy
Pymodbus REPL to quickly run the modbus clients in tcp/rtu modes.
Expand Down Expand Up @@ -205,4 +205,4 @@ Pymodbus is built on top of code developed from/by:
* Hynek Petrak, https://github.com/HynekPetrak
* Twisted Matrix

Released under the BSD License
Released under the `BSD License <LICENSE>`_
10 changes: 6 additions & 4 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,19 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc', 'm2r', 'recommonmark']

#extensions = ['sphinx.ext.autodoc', 'm2r', 'recommonmark']
extensions = ['sphinx.ext.autodoc', 'm2r']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_parsers = {
'.md': CommonMarkParser,
}
#source_parsers = {
# '.md': CommonMarkParser,
#}

source_suffix = ['.rst', '.md']
# source_suffix = '.rst'
Expand Down
2 changes: 1 addition & 1 deletion doc/source/library/REPL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Depends on [prompt_toolkit](https://python-prompt-toolkit.readthedocs.io/en/stab

Install dependencies
```
$ pip install click prompt_toolkit --upgarde
$ pip install click prompt_toolkit --upgrade
```

Or
Expand Down
10 changes: 7 additions & 3 deletions examples/common/async_asyncio_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
# Import the required asynchronous client
# ----------------------------------------------------------------------- #
from pymodbus.client.asynchronous.tcp import AsyncModbusTCPClient as ModbusClient
# from pymodbus.client.asynchronous.udp import (
# AsyncModbusUDPClient as ModbusClient)
from pymodbus.client.asynchronous.udp import (
AsyncModbusUDPClient as ModbusClient)
from pymodbus.client.asynchronous import schedulers

else:
Expand Down Expand Up @@ -141,6 +141,7 @@ def run_with_not_running_loop():
log.debug("------------------------------------------------------")
loop = asyncio.new_event_loop()
assert not loop.is_running()
asyncio.set_event_loop(loop)
new_loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020, loop=loop)
loop.run_until_complete(start_async_test(client.protocol))
loop.close()
Expand Down Expand Up @@ -191,9 +192,12 @@ def run_with_no_loop():
ModbusClient Factory creates a loop.
:return:
"""
log.debug("---------------------RUN_WITH_NO_LOOP-----------------")
loop, client = ModbusClient(schedulers.ASYNC_IO, port=5020)
loop.run_until_complete(start_async_test(client.protocol))
loop.close()
log.debug("--------DONE RUN_WITH_NO_LOOP-------------")
log.debug("")


if __name__ == '__main__':
Expand All @@ -207,5 +211,5 @@ def run_with_no_loop():

# Run with already running loop
run_with_already_running_loop()
log.debug("---------------------RUN_WITH_NO_LOOP-----------------")

log.debug("")
2 changes: 1 addition & 1 deletion examples/common/asynchronous_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def run_async_server():
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '2.2.0'
identity.MajorMinorRevision = '2.3.0'

# ----------------------------------------------------------------------- #
# run the server you want
Expand Down
155 changes: 155 additions & 0 deletions examples/common/asyncio_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#!/usr/bin/env python
"""
Pymodbus Asyncio Server Example
--------------------------------------------------------------------------

The asyncio server is implemented in pure python without any third
party libraries (unless you need to use the serial protocols which require
asyncio-pyserial). This is helpful in constrained or old environments where using
twisted is just not feasible. What follows is an example of its use:
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
import asyncio
from pymodbus.server.asyncio import StartTcpServer
from pymodbus.server.asyncio import StartUdpServer
from pymodbus.server.asyncio import StartSerialServer

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

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


async def run_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
# The datastores only respond to the addresses that they are initialized to
# Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a
# request to 0x100 will respond with an invalid address exception. This is
# because many devices exhibit this kind of behavior (but not all)::
#
# block = ModbusSequentialDataBlock(0x00, [0]*0xff)
#
# Continuing, you can choose to use a sequential or a sparse DataBlock in
# your data context. The difference is that the sequential has no gaps in
# the data while the sparse can. Once again, there are devices that exhibit
# both forms of behavior::
#
# block = ModbusSparseDataBlock({0x00: 0, 0x05: 1})
# block = ModbusSequentialDataBlock(0x00, [0]*5)
#
# Alternately, you can use the factory methods to initialize the DataBlocks
# or simply do not pass them to have them initialized to 0x00 on the full
# address range::
#
# store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create())
# store = ModbusSlaveContext()
#
# Finally, you are allowed to use the same DataBlock reference for every
# table or you may use a separate DataBlock for each table.
# This depends if you would like functions to be able to access and modify
# the same data or not::
#
# block = ModbusSequentialDataBlock(0x00, [0]*0xff)
# store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
#
# The server then makes use of a server context that allows the server to
# respond with different slave contexts for different unit ids. By default
# it will return the same context for every unit id supplied (broadcast
# mode).
# However, this can be overloaded by setting the single flag to False and
# then supplying a dictionary of unit id to context mapping::
#
# slaves = {
# 0x01: ModbusSlaveContext(...),
# 0x02: ModbusSlaveContext(...),
# 0x03: ModbusSlaveContext(...),
# }
# context = ModbusServerContext(slaves=slaves, single=False)
#
# The slave context can also be initialized in zero_mode which means that a
# request to address(0-7) will map to the address (0-7). The default is
# False which is based on section 4.4 of the specification, so address(0-7)
# will map to (1-8)::
#
# store = ModbusSlaveContext(..., zero_mode=True)
# ----------------------------------------------------------------------- #
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))

context = ModbusServerContext(slaves=store, single=True)

# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '2.3.0'

# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
# Tcp:
# immediately start serving:
await StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020), allow_reuse_address=True,
defer_start=False)

# deferred start:
# server = await StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020),
# allow_reuse_address=True, defer_start=True)
#
# asyncio.get_event_loop().call_later(20, lambda : server.serve_forever)
# await server.serve_forever()

# TCP with different framer
# StartTcpServer(context, identity=identity,
# framer=ModbusRtuFramer, address=("0.0.0.0", 5020))

# Udp:
# server = await StartUdpServer(context, identity=identity, address=("0.0.0.0", 5020),
# allow_reuse_address=True, defer_start=True)
# #
# await server.serve_forever()

# !!! SERIAL SERVER NOT IMPLEMENTED !!!
# Ascii:
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', timeout=1)

# RTU:
# StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
# port='/dev/ttyp0', timeout=.005, baudrate=9600)

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


if __name__ == "__main__":
asyncio.run(run_server())

2 changes: 1 addition & 1 deletion examples/common/callback_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def run_callback_server():
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '2.2.0'
identity.MajorMinorRevision = '2.3.0'

# ----------------------------------------------------------------------- #
# run the server you want
Expand Down
2 changes: 1 addition & 1 deletion examples/common/custom_datablock.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def run_custom_db_server():
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '2.2.0'
identity.MajorMinorRevision = '2.3.0'

# ----------------------------------------------------------------------- #
# run the server you want
Expand Down
2 changes: 1 addition & 1 deletion examples/common/custom_synchronous_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def run_server():
identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '2.1.0'
identity.MajorMinorRevision = '2.3.0'

# ----------------------------------------------------------------------- #
# run the server you want
Expand Down
Loading