Skip to content

Commit

Permalink
1. update requirements
Browse files Browse the repository at this point in the history
2. Fix examples
3. Fix #494 - handle_local_echo
4. Fix #500 -- asyncio serial client with already running loop
5. Fix #486 - Pass serial args for asyncio serial client
6. Fix #490 - Typo in decode_data for socker_framer
7. Fix #385 - Support timeouts to break out of responspe await when server goes offline
8. Misc updates
  • Loading branch information
dhoomakethu committed Jul 3, 2020
1 parent b0b8957 commit e402d9f
Show file tree
Hide file tree
Showing 17 changed files with 233 additions and 182 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ install:
@test -d "$(VIRTUAL_ENV)" || mkdir -p "$(VIRTUAL_ENV)"
@test -x "$(VIRTUAL_ENV)/bin/python" || virtualenv --quiet "$(VIRTUAL_ENV)"
@test -x "$(VIRTUAL_ENV)/bin/pip" || easy_install pip
@pip install --quiet --requirement=requirements.txt
@pip install --upgrade --quiet --requirement=requirements.txt
@pip uninstall --yes pymodbus &>/dev/null || true
@pip install --quiet --no-deps --ignore-installed .

Expand All @@ -38,15 +38,15 @@ check: install
@flake8

test: install
@pip install --quiet --requirement=requirements-tests.txt
@pip install --upgrade --quiet --requirement=requirements-tests.txt
@pytest --cov=pymodbus/ --cov-report term-missing
@coverage report --fail-under=90

tox: install
@pip install --quiet tox && tox
@pip install --upgrade --quiet tox && tox

docs: install
@pip install --quiet --requirement=requirements-docs.txt
@pip install --upgrade --quiet --requirement=requirements-docs.txt
@cd doc && make clean && make html

publish: install
Expand Down
8 changes: 4 additions & 4 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 @@ -207,9 +207,9 @@ def run_with_no_loop():
run_with_no_loop()

# Run with loop not yet started
run_with_not_running_loop()
# run_with_not_running_loop()

# Run with already running loop
run_with_already_running_loop()
# run_with_already_running_loop()

log.debug("")
194 changes: 103 additions & 91 deletions examples/common/modbus_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
log = logging.getLogger()
log.setLevel(logging.INFO)

ORDER_DICT = {
"<": "LITTLE",
">": "BIG"
}


def run_binary_payload_ex():
# ----------------------------------------------------------------------- #
Expand Down Expand Up @@ -71,97 +76,104 @@ def run_binary_payload_ex():
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #

# ----------------------------------------------------------------------- #
builder = BinaryPayloadBuilder(byteorder=Endian.Big,
wordorder=Endian.Little)
builder.add_string('abcdefgh')
builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0])
builder.add_8bit_int(-0x12)
builder.add_8bit_uint(0x12)
builder.add_16bit_int(-0x5678)
builder.add_16bit_uint(0x1234)
builder.add_32bit_int(-0x1234)
builder.add_32bit_uint(0x12345678)
builder.add_16bit_float(12.34)
builder.add_16bit_float(-12.34)
builder.add_32bit_float(22.34)
builder.add_32bit_float(-22.34)
builder.add_64bit_int(-0xDEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_float(123.45)
builder.add_64bit_float(-123.45)
payload = builder.to_registers()
print("-" * 60)
print("Writing Registers")
print("-" * 60)
print(payload)
print("\n")
payload = builder.build()
address = 0
# Can write registers
# registers = builder.to_registers()
# client.write_registers(address, registers, unit=1)

# Or can write encoded binary string
client.write_registers(address, payload, skip_encode=True, unit=1)
# ----------------------------------------------------------------------- #
# If you need to decode a collection of registers in a weird layout, the
# payload decoder can help you as well.
#
# Here we demonstrate decoding a random register layout, unpacked it looks
# like the following:
#
# - a 8 byte string 'abcdefgh'
# - a 32 bit float 22.34
# - a 16 bit unsigned int 0x1234
# - another 16 bit unsigned int which we will ignore
# - an 8 bit int 0x12
# - an 8 bit bitstring [0,1,0,1,1,0,1,0]
# ----------------------------------------------------------------------- #
address = 0x0
count = len(payload)
result = client.read_holding_registers(address, count, unit=1)
print("-" * 60)
print("Registers")
print("-" * 60)
print(result.registers)
print("\n")
decoder = BinaryPayloadDecoder.fromRegisters(result.registers,
byteorder=Endian.Big,
wordorder=Endian.Little)

assert decoder._byteorder == builder._byteorder, \
"Make sure byteorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder"

assert decoder._wordorder == builder._wordorder, \
"Make sure wordorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder"


decoded = OrderedDict([
('string', decoder.decode_string(8)),
('bits', decoder.decode_bits()),
('8int', decoder.decode_8bit_int()),
('8uint', decoder.decode_8bit_uint()),
('16int', decoder.decode_16bit_int()),
('16uint', decoder.decode_16bit_uint()),
('32int', decoder.decode_32bit_int()),
('32uint', decoder.decode_32bit_uint()),
('16float', decoder.decode_16bit_float()),
('16float2', decoder.decode_16bit_float()),
('32float', decoder.decode_32bit_float()),
('32float2', decoder.decode_32bit_float()),
('64int', decoder.decode_64bit_int()),
('64uint', decoder.decode_64bit_uint()),
('ignore', decoder.skip_bytes(8)),
('64float', decoder.decode_64bit_float()),
('64float2', decoder.decode_64bit_float()),
])

print("-" * 60)
print("Decoded Data")
print("-" * 60)
for name, value in iteritems(decoded):
print("%s\t" % name, hex(value) if isinstance(value, int) else value)
combos = [(wo, bo) for wo in [Endian.Big, Endian.Little] for bo in [Endian.Big, Endian.Little]]
for wo, bo in combos:
print("-" * 60)
print("Word Order: {}".format(ORDER_DICT[wo]))
print("Byte Order: {}".format(ORDER_DICT[bo]))
print()
builder = BinaryPayloadBuilder(byteorder=bo,
wordorder=wo)
strng = "abcdefgh"
builder.add_string(strng)
builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0])
builder.add_8bit_int(-0x12)
builder.add_8bit_uint(0x12)
builder.add_16bit_int(-0x5678)
builder.add_16bit_uint(0x1234)
builder.add_32bit_int(-0x1234)
builder.add_32bit_uint(0x12345678)
builder.add_16bit_float(12.34)
builder.add_16bit_float(-12.34)
builder.add_32bit_float(22.34)
builder.add_32bit_float(-22.34)
builder.add_64bit_int(-0xDEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_uint(0x12345678DEADBEEF)
builder.add_64bit_float(123.45)
builder.add_64bit_float(-123.45)
payload = builder.to_registers()
print("-" * 60)
print("Writing Registers")
print("-" * 60)
print(payload)
print("\n")
payload = builder.build()
address = 0
# Can write registers
# registers = builder.to_registers()
# client.write_registers(address, registers, unit=1)

# Or can write encoded binary string
client.write_registers(address, payload, skip_encode=True, unit=1)
# ----------------------------------------------------------------------- #
# If you need to decode a collection of registers in a weird layout, the
# payload decoder can help you as well.
#
# Here we demonstrate decoding a random register layout, unpacked it looks
# like the following:
#
# - a 8 byte string 'abcdefgh'
# - a 32 bit float 22.34
# - a 16 bit unsigned int 0x1234
# - another 16 bit unsigned int which we will ignore
# - an 8 bit int 0x12
# - an 8 bit bitstring [0,1,0,1,1,0,1,0]
# ----------------------------------------------------------------------- #
address = 0x0
count = len(payload)
result = client.read_holding_registers(address, count, unit=1)
print("-" * 60)
print("Registers")
print("-" * 60)
print(result.registers)
print("\n")
decoder = BinaryPayloadDecoder.fromRegisters(result.registers,
byteorder=bo,
wordorder=wo)

assert decoder._byteorder == builder._byteorder, \
"Make sure byteorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder"

assert decoder._wordorder == builder._wordorder, \
"Make sure wordorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder"


decoded = OrderedDict([
('string', decoder.decode_string(len(strng))),
('bits', decoder.decode_bits()),
('8int', decoder.decode_8bit_int()),
('8uint', decoder.decode_8bit_uint()),
('16int', decoder.decode_16bit_int()),
('16uint', decoder.decode_16bit_uint()),
('32int', decoder.decode_32bit_int()),
('32uint', decoder.decode_32bit_uint()),
('16float', decoder.decode_16bit_float()),
('16float2', decoder.decode_16bit_float()),
('32float', decoder.decode_32bit_float()),
('32float2', decoder.decode_32bit_float()),
('64int', decoder.decode_64bit_int()),
('64uint', decoder.decode_64bit_uint()),
('ignore', decoder.skip_bytes(8)),
('64float', decoder.decode_64bit_float()),
('64float2', decoder.decode_64bit_float()),
])

print("-" * 60)
print("Decoded Data")
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: 2 additions & 0 deletions examples/common/modbus_payload_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ def run_payload_server():
builder.add_16bit_uint(0x1234)
builder.add_32bit_int(-0x1234)
builder.add_32bit_uint(0x12345678)
builder.add_16bit_float(12.34)
builder.add_16bit_float(-12.34)
builder.add_32bit_float(22.34)
builder.add_32bit_float(-22.34)
builder.add_64bit_int(-0xDEADBEEF)
Expand Down
6 changes: 3 additions & 3 deletions examples/common/synchronous_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ def run_server():
# run the server you want
# ----------------------------------------------------------------------- #
# Tcp:
StartTcpServer(context, identity=identity, address=("localhost", 5020))

StartTcpServer(context, identity=identity, address=("", 5020))
#
# TCP with different framer
# StartTcpServer(context, identity=identity,
# framer=ModbusRtuFramer, address=("0.0.0.0", 5020))
Expand All @@ -131,7 +131,7 @@ def run_server():

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

# Binary
# StartSerialServer(context,
Expand Down
8 changes: 5 additions & 3 deletions pymodbus/client/asynchronous/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

DGRAM_TYPE = socket.SocketKind.SOCK_DGRAM


class BaseModbusAsyncClientProtocol(AsyncModbusClientMixin):
"""
Asyncio specific implementation of asynchronous modbus client protocol.
Expand Down Expand Up @@ -119,7 +120,7 @@ def connected(self):
def write_transport(self, packet):
return self.transport.write(packet)

def execute(self, request, **kwargs):
def _execute(self, request, **kwargs):
"""
Starts the producer to send the next request to
consumer.write(Frame(request))
Expand Down Expand Up @@ -727,7 +728,7 @@ class AsyncioModbusSerialClient(object):
framer = None

def __init__(self, port, protocol_class=None, framer=None, loop=None,
baudrate=9600, bytesize=8, parity='N', stopbits=1):
baudrate=9600, bytesize=8, parity='N', stopbits=1, **serial_kwargs):
"""
Initializes Asyncio Modbus Serial Client
:param port: Port to connect
Expand All @@ -747,6 +748,7 @@ def __init__(self, port, protocol_class=None, framer=None, loop=None,
self.parity = parity
self.stopbits = stopbits
self.framer = framer
self._extra_serial_kwargs = serial_kwargs
self._connected_event = asyncio.Event()

def stop(self):
Expand Down Expand Up @@ -780,7 +782,7 @@ def connect(self):

yield from create_serial_connection(
self.loop, self._create_protocol, self.port, baudrate=self.baudrate,
bytesize=self.bytesize, stopbits=self.stopbits, parity=self.parity
bytesize=self.bytesize, stopbits=self.stopbits, parity=self.parity, **self._extra_serial_kwargs
)
yield from self._connected_event.wait()
_logger.info('Connected to %s', self.port)
Expand Down
8 changes: 6 additions & 2 deletions pymodbus/client/asynchronous/factory/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from __future__ import absolute_import

import logging

import time
from pymodbus.client.asynchronous import schedulers
from pymodbus.client.asynchronous.thread import EventLoopThread

Expand Down Expand Up @@ -103,7 +103,11 @@ def async_io_factory(port=None, framer=None, **kwargs):

client = AsyncioModbusSerialClient(port, proto_cls, framer, loop, **kwargs)
coro = client.connect()
loop.run_until_complete(coro)
if loop.is_running():
future = asyncio.run_coroutine_threadsafe(coro, loop=loop)
future.result()
else:
loop.run_until_complete(coro)
return loop, client


Expand Down
Loading

0 comments on commit e402d9f

Please sign in to comment.