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
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ matrix:
include:
- os: linux
python: "2.7"
- os: linux
python: "3.5"
- os: linux
python: "3.6"
- os: linux
python: "3.7"
- os: linux
python: "3.8"
- os: osx
osx_image: xcode8.3
language: generic
before_install:
- if [ $TRAVIS_OS_NAME = osx ]; then brew update; fi
- if [ $TRAVIS_OS_NAME = osx ]; then brew install openssl; fi
# - if [$TRAVIS_OS_NAME = osx ]; then python -c "import fcntl; fcntl.fcntl(1, fcntl.F_SETFL, 0)"; fi

install:
# - scripts/travis.sh pip install pip-accel
- scripts/travis.sh pip install -U setuptools
- if [ $TRAVIS_OS_NAME = osx ]; then scripts/travis.sh pip install -U "\"setuptools<45"\"; else pip install -U setuptools --upgrade ; fi
- scripts/travis.sh pip install coveralls
- scripts/travis.sh pip install --requirement=requirements-checks.txt
- scripts/travis.sh pip install --requirement=requirements-tests.txt
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Version 2.4.0
* Support async moduls tls server/client
* Add local echo option
* Add exponential backoffs on retries.
* REPL - Support broadcasts.
* Fix framers using wrong unit address.
* Update documentation for serial_forwarder example
* Fix error with rtu client for `local_echo`
* Fix asyncio client not working with already running loop
* Fix passing serial arguments to async clients
Expand Down
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ VIRTUAL_ENV ?= $(WORKON_HOME)/pymodbus
PATH := $(VIRTUAL_ENV)/bin:$(PATH)
MAKE := $(MAKE) --no-print-directory
SHELL = bash
PYVER=$(shell python -c "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)")

default:
@echo 'Makefile for pymodbus'
Expand Down Expand Up @@ -37,10 +38,16 @@ check: install
@pip install --upgrade --quiet --requirement=requirements-checks.txt
@flake8


test: install
@pip install --upgrade --quiet --requirement=requirements-tests.txt
ifeq ($(PYVER),3.6)
@pytest --cov=pymodbus/ --cov-report term-missing test/test_server_asyncio.py test
@coverage report --fail-under=90 -i
else
@pytest --cov=pymodbus/ --cov-report term-missing
@coverage report --fail-under=90
@coverage report --fail-under=90 -i
endif

tox: install
@pip install --upgrade --quiet tox && tox
Expand All @@ -57,6 +64,7 @@ publish: install
twine upload dist/*
$(MAKE) clean


clean:
@rm -Rf *.egg .eggs *.egg-info *.db .cache .coverage .tox build dist docs/build htmlcov doc/_build test/.Python test/pip-selfcheck.json test/lib/ test/include/ test/bin/
@find . -depth -type d -name __pycache__ -exec rm -Rf {} \;
Expand Down
5 changes: 2 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 @@ -210,7 +210,6 @@ def run_with_no_loop():
# run_with_not_running_loop()

# Run with already running loop

# run_with_already_running_loop()

log.debug("")
8 changes: 4 additions & 4 deletions examples/common/asyncio_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
# import the various server implementations
# --------------------------------------------------------------------------- #
import asyncio
from pymodbus.server.asyncio import StartTcpServer
from pymodbus.server.asyncio import StartTlsServer
from pymodbus.server.asyncio import StartUdpServer
from pymodbus.server.asyncio import StartSerialServer
from pymodbus.server.async_io import StartTcpServer
from pymodbus.server.async_io import StartTlsServer
from pymodbus.server.async_io import StartUdpServer
from pymodbus.server.async_io import StartSerialServer

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
Expand Down
1 change: 0 additions & 1 deletion examples/common/modbus_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ def run_binary_payload_ex():
print("-" * 60)
for name, value in iteritems(decoded):
print("%s\t" % name, hex(value) if isinstance(value, int) else value)


# ----------------------------------------------------------------------- #
# close the client
Expand Down
2 changes: 1 addition & 1 deletion examples/contrib/asynchronous_asyncio_serial_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
if IS_PYTHON3 and PYTHON_VERSION >= (3, 4):
import asyncio
from serial_asyncio import create_serial_connection
from pymodbus.client.asynchronous.asyncio import ModbusClientProtocol
from pymodbus.client.asynchronous.async_io import ModbusClientProtocol
from pymodbus.transaction import ModbusAsciiFramer, ModbusRtuFramer
from pymodbus.factory import ClientDecoder
else:
Expand Down
8 changes: 7 additions & 1 deletion examples/contrib/serial_forwarder.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@
def run_serial_forwarder():
# ----------------------------------------------------------------------- #
# initialize the datastore(serial client)
# Note this would send the requests on the serial client with address = 0

# ----------------------------------------------------------------------- #
client = ModbusClient(method='rtu', port='/dev/ptyp0')
client = ModbusClient(method='rtu', port='/tmp/ptyp0')
# If required to communicate with a specified client use unit=<unit_id>
# in RemoteSlaveContext
# For e.g to forward the requests to slave with unit address 1 use
# store = RemoteSlaveContext(client, unit=1)
store = RemoteSlaveContext(client)
context = ModbusServerContext(slaves=store, single=True)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ class BaseModbusAsyncClientProtocol(AsyncModbusClientMixin):
factory = None
transport = None

async def execute(self, request=None):
"""
Executes requests asynchronously
:param request:
:return:
"""
req = self._execute(request)
resp = await asyncio.wait_for(req, timeout=self._timeout)
return resp

def connection_made(self, transport):
"""
Called when a connection is made.
Expand Down Expand Up @@ -120,9 +130,7 @@ def connected(self):
def write_transport(self, packet):
return self.transport.write(packet)


def _execute(self, request, **kwargs):

"""
Starts the producer to send the next request to
consumer.write(Frame(request))
Expand All @@ -139,7 +147,7 @@ def _dataReceived(self, data):
:param data: The data returned from the server
'''
_logger.debug("recv: " + " ".join([hex(byte2int(x)) for x in data]))
unit = self.framer.decode_data(data).get("uid", 0)
unit = self.framer.decode_data(data).get("unit", 0)
self.framer.processIncomingPacket(data, self._handleResponse, unit=unit)

def _handleResponse(self, reply, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions pymodbus/client/asynchronous/factory/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ def async_io_factory(port=None, framer=None, **kwargs):
:return: asyncio event loop and serial client
"""
import asyncio
from pymodbus.client.asynchronous.asyncio import (ModbusClientProtocol,
AsyncioModbusSerialClient)
from pymodbus.client.asynchronous.async_io import (ModbusClientProtocol,
AsyncioModbusSerialClient)
loop = kwargs.pop("loop", None) or asyncio.get_event_loop()
proto_cls = kwargs.pop("proto_cls", None) or ModbusClientProtocol

Expand Down
2 changes: 1 addition & 1 deletion pymodbus/client/asynchronous/factory/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def async_io_factory(host="127.0.0.1", port=Defaults.Port, framer=None,
:return: asyncio event loop and tcp client
"""
import asyncio
from pymodbus.client.asynchronous.asyncio import init_tcp_client
from pymodbus.client.asynchronous.async_io import init_tcp_client
loop = kwargs.get("loop") or asyncio.new_event_loop()
proto_cls = kwargs.get("proto_cls", None)
if not loop.is_running():
Expand Down
2 changes: 1 addition & 1 deletion pymodbus/client/asynchronous/factory/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def async_io_factory(host="127.0.0.1", port=Defaults.TLSPort, sslctx=None,
:return: asyncio event loop and tcp client
"""
import asyncio
from pymodbus.client.asynchronous.asyncio import init_tls_client
from pymodbus.client.asynchronous.async_io import init_tls_client
loop = kwargs.get("loop") or asyncio.new_event_loop()
proto_cls = kwargs.get("proto_cls", None)
if not loop.is_running():
Expand Down
2 changes: 1 addition & 1 deletion pymodbus/client/asynchronous/factory/udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def async_io_factory(host="127.0.0.1", port=Defaults.Port, framer=None,
:return: asyncio event loop and udp client
"""
import asyncio
from pymodbus.client.asynchronous.asyncio import init_udp_client
from pymodbus.client.asynchronous.async_io import init_udp_client
loop = kwargs.get("loop") or asyncio.get_event_loop()
proto_cls = kwargs.get("proto_cls", None)
cor = init_udp_client(proto_cls, loop, host, port)
Expand Down
25 changes: 7 additions & 18 deletions pymodbus/client/asynchronous/mixins.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import logging
import asyncio
from pymodbus.client.sync import BaseModbusClient
from pymodbus.bit_read_message import *
from pymodbus.bit_write_message import *
from pymodbus.register_read_message import *
from pymodbus.register_write_message import *
from pymodbus.diag_message import *
from pymodbus.file_message import *
from pymodbus.other_message import *
# from pymodbus.bit_read_message import *
# from pymodbus.bit_write_message import *
# from pymodbus.register_read_message import *
# from pymodbus.register_write_message import *
# from pymodbus.diag_message import *
# from pymodbus.file_message import *
# from pymodbus.other_message import *
from pymodbus.constants import Defaults

from pymodbus.factory import ClientDecoder
Expand Down Expand Up @@ -36,16 +35,6 @@ def __init__(self, framer=None, timeout=2, **kwargs):
framer or ModbusSocketFramer(ClientDecoder()), **kwargs
)

async def execute(self, request=None):
"""
Executes requests asynchronously
:param request:
:return:
"""
req = self._execute(request)
resp = await asyncio.wait_for(req, timeout=self._timeout)
return resp


class AsyncModbusClientMixin(BaseAsyncModbusClient):
"""
Expand Down
2 changes: 1 addition & 1 deletion pymodbus/client/asynchronous/tornado/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def on_receive(self, *args):
if not data:
return
LOGGER.debug("recv: " + " ".join([hex(byte2int(x)) for x in data]))
unit = self.framer.decode_data(data).get("uid", 0)
unit = self.framer.decode_data(data).get("unit", 0)
self.framer.processIncomingPacket(data, self._handle_response, unit=unit)

def execute(self, request=None):
Expand Down
4 changes: 2 additions & 2 deletions pymodbus/client/asynchronous/twisted/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def process():
from pymodbus.client.asynchronous.mixins import AsyncModbusClientMixin
from pymodbus.transaction import FifoTransactionManager, DictTransactionManager
from pymodbus.transaction import ModbusSocketFramer, ModbusRtuFramer
from pymodbus.compat import byte2int
from pymodbus.compat import byte2int
from twisted.python.failure import Failure


Expand Down Expand Up @@ -98,7 +98,7 @@ def dataReceived(self, data):

:param data: The data returned from the server
"""
unit = self.framer.decode_data(data).get("uid", 0)
unit = self.framer.decode_data(data).get("unit", 0)
self.framer.processIncomingPacket(data, self._handleResponse,
unit=unit)

Expand Down
53 changes: 43 additions & 10 deletions pymodbus/repl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ $ pip install pymodbus[repl] --upgrade

## Usage Instructions
RTU and TCP are supported as of now

```
bash-3.2$ pymodbus.console
pymodbus.console --help
Usage: pymodbus.console [OPTIONS] COMMAND [ARGS]...

Options:
--version Show the version and exit.
--verbose Verbose logs
--support-diag Support Diagnostic messages
--help Show this message and exit.
--version Show the version and exit.
--verbose Verbose logs
--broadcast-support Support broadcast messages
--help Show this message and exit.

Commands:
serial
Expand All @@ -34,8 +35,9 @@ Commands:

```
TCP Options

```
bash-3.2$ pymodbus.console tcp --help
pymodbus.console tcp --help
Usage: pymodbus.console tcp [OPTIONS]

Options:
Expand All @@ -44,14 +46,11 @@ Options:
--framer TEXT Override the default packet framer tcp|rtu
--help Show this message and exit.




```

SERIAL Options
```
bash-3.2$ pymodbus.console serial --help
pymodbus.console serial --help
Usage: pymodbus.console serial [OPTIONS]

Options:
Expand All @@ -61,18 +60,24 @@ Options:
--bytesize [5|6|7|8] Modbus RTU serial Number of data bits. Possible
values: FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS.
Defaults to 8

--parity [N|E|O|M|S] Modbus RTU serial parity. Enable parity checking.
Possible values: PARITY_NONE, PARITY_EVEN, PARITY_ODD
PARITY_MARK, PARITY_SPACE. Default to 'N'

--stopbits [1|1.5|2] Modbus RTU serial stop bits. Number of stop bits.
Possible values: STOPBITS_ONE,
STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO. Default to '1'

--xonxoff INTEGER Modbus RTU serial xonxoff. Enable software flow
control.Defaults to 0

--rtscts INTEGER Modbus RTU serial rtscts. Enable hardware (RTS/CTS)
flow control. Defaults to 0

--dsrdtr INTEGER Modbus RTU serial dsrdtr. Enable hardware (DSR/DTR)
flow control. Defaults to 0

--timeout FLOAT Modbus RTU serial read timeout. Defaults to 0.025 sec
--write-timeout FLOAT Modbus RTU serial write timeout. Defaults to 2 sec
--help Show this message and exit.
Expand Down Expand Up @@ -275,6 +280,34 @@ null

```

To Send broadcast requests, use `--broadcast-support` and send requests with unit id as `0`.
`write_coil`, `write_coils`, `write_register`, `write_registers` are supported.

```
✗ pymodbus.console --broadcast-support tcp --host 192.168.1.8 --port 5020

----------------------------------------------------------------------------
__________ _____ .___ __________ .__
\______ \___.__. / \ ____ __| _/ \______ \ ____ ______ | |
| ___< | |/ \ / \ / _ \ / __ | | _// __ \\____ \| |
| | \___ / Y ( <_> ) /_/ | | | \ ___/| |_> > |__
|____| / ____\____|__ /\____/\____ | /\ |____|_ /\___ > __/|____/
\/ \/ \/ \/ \/ \/|__|
v1.2.0 - [pymodbus, version 2.4.0]
----------------------------------------------------------------------------

> client.write_registers address=0 values=10,20,30,40 unit=0
{
"broadcasted": true
}

> client.write_registers address=0 values=10,20,30,40 unit=1
{
"address": 0,
"count": 4
}
```

## DEMO

[![asciicast](https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o.png)](https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o)
Expand Down
Loading