Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e33153f
it reads! (something)
altendky Jan 3, 2021
9320e25
read a few things in tc.py
altendky Jan 3, 2021
e7a365b
add a few tests
altendky Jan 3, 2021
29346ff
commented out attempt to break the framer does
altendky Jan 3, 2021
809972d
slight simplification
altendky Jan 11, 2021
743bda1
hacky but running trio server
altendky Jan 11, 2021
4123eb4
misc
altendky Jan 12, 2021
e3be360
missing imports
altendky Jan 13, 2021
b0e72eb
use async_generator for asynccontextmanager
altendky Jan 13, 2021
0229932
Add async_generator to extras_require:trio
altendky Jan 13, 2021
a844e30
use https for github link
altendky Jan 13, 2021
a4c5e71
another test
altendky Jan 15, 2021
4187d5f
pytest-trio~=0.7.0;python_version>="3.6"
altendky Jan 15, 2021
1a5ee53
misc
altendky Jan 16, 2021
83bf250
misc
altendky Jan 18, 2021
12af789
Merge branch 'dev' into trio
altendky Mar 9, 2021
12c3313
Merge branch 'dev' into trio
altendky Apr 22, 2021
d514d08
update test for logging change
altendky Apr 23, 2021
7bd1cbb
skip trio tests in py2
altendky Apr 23, 2021
325a53e
unit test trio.execute()
altendky Apr 23, 2021
a2e368f
simplify trio broadcast handling code
altendky Apr 23, 2021
023caff
test trio.incoming
altendky Apr 24, 2021
b1fc236
shift examples to the examples directory
altendky Apr 24, 2021
707035e
first round of trio documentation additions
altendky Apr 24, 2021
61dc891
more docstrings and some refactors
altendky Apr 24, 2021
6f1af2f
simplify trio client test file
altendky Apr 24, 2021
ffd12d1
more tests and use mock
altendky Apr 27, 2021
ef8ffbf
stop using MagicMock and AsyncMock
altendky Apr 27, 2021
49f7ca9
trio.BaseModbusAsyncClientProtocol tests
altendky Apr 28, 2021
5464580
more tests...
altendky Apr 28, 2021
343346c
fix the tests
altendky Apr 28, 2021
1207589
even more tests
altendky Apr 28, 2021
8774d1b
one more assert
altendky Apr 28, 2021
c750233
fixup py36-37
altendky Apr 28, 2021
c47a533
fixup patching and mocking of trio.open_tcp_stream()
altendky Apr 28, 2021
363903e
use outcome so we can handle errors as well
altendky Apr 28, 2021
f08b86c
handle protocol connection lost
altendky Apr 28, 2021
c6db326
explain the +1s
altendky Apr 28, 2021
bcef8b7
use a lock for trio client sending
altendky Apr 28, 2021
0528b1c
Merge branch 'dev' into trio
altendky May 26, 2021
ab584ea
Merge branch 'dev' into trio
altendky Aug 16, 2021
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
4 changes: 4 additions & 0 deletions doc/source/example/async_trio_client.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
==================================================
Async Trio Client Example
==================================================
.. literalinclude:: ../../../examples/common/async_trio_client.py
4 changes: 4 additions & 0 deletions doc/source/example/async_trio_server.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
==================================================
Async Trio Server Example
==================================================
.. literalinclude:: ../../../examples/common/trio_server.py
2 changes: 2 additions & 0 deletions doc/source/example/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Examples
async_asyncio_serial_client
async_tornado_client
async_tornado_client_serial
async_trio_client
async_trio_server
async_twisted_client
async_twisted_client_serial
asynchronous_processor
Expand Down
1 change: 1 addition & 0 deletions doc/source/library/pymodbus.client.asynchronous.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Subpackages
pymodbus.client.asynchronous.factory
pymodbus.client.asynchronous.schedulers
pymodbus.client.asynchronous.tornado
pymodbus.client.asynchronous.trio
pymodbus.client.asynchronous.twisted

Submodules
Expand Down
8 changes: 8 additions & 0 deletions doc/source/library/pymodbus.client.asynchronous.trio.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pymodbus\.client\.asynchronous\.trio package
===============================================

.. automodule:: pymodbus.client.asynchronous.trio
:members:
:undoc-members:
:show-inheritance:

7 changes: 7 additions & 0 deletions doc/source/library/pymodbus.server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,11 @@ pymodbus\.server\.sync module
:undoc-members:
:show-inheritance:

pymodbus\.server\.trio module
-----------------------------

.. automodule:: pymodbus.server.trio
:members:
:undoc-members:
:show-inheritance:

42 changes: 42 additions & 0 deletions examples/common/async_trio_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python
"""
Pymodbus Asynchronous Client Examples
--------------------------------------------------------------------------

The following is an example of how to use the asynchronous modbus
client implementation from pymodbus with Trio.

The example is only valid on Python3.6 and above
"""
import contextlib
import logging
import sys

from pymodbus.client.asynchronous.tcp import AsyncModbusTCPClient as ModbusClient
from pymodbus.client.asynchronous import schedulers

import trio


async def main():
root_logger = logging.getLogger()
handler = logging.StreamHandler(stream=sys.stdout)
root_logger.addHandler(hdlr=handler)
root_logger.setLevel(logging.DEBUG)

client = ModbusClient(scheduler=schedulers.TRIO, host="127.0.0.1", port=5020)

with contextlib.suppress(KeyboardInterrupt):
async with client.manage_connection() as protocol:
while True:
response = await protocol.read_coils(address=1, count=1, unit=0x01)
print(' response:', response.bits)
response = await protocol.read_holding_registers(address=1, count=1, unit=0x01)
print(' response:', response.registers)
response = await protocol.read_holding_registers(address=10, count=1, unit=0x01)
print(' response:', response.registers)

await trio.sleep(1)


trio.run(main)
57 changes: 57 additions & 0 deletions examples/common/trio_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python
"""
Pymodbus Trio Server Example
--------------------------------------------------------------------------

"""
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)

import functools

from pymodbus.server.trio import tcp_server
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

import trio


async def run_server():
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)

identity = ModbusDeviceIdentification()
identity.VendorName = "Pymodbus"
identity.ProductCode = "PM"
identity.VendorUrl = "https://github.com/riptideio/pymodbus/"
identity.ProductName = "Pymodbus Server"
identity.ModelName = "Pymodbus Server"
identity.MajorMinorRevision = "2.3.0"

await trio.serve_tcp(
functools.partial(
tcp_server,
context=context,
identity=identity,
),
port=5020,
host="0.0.0.0",
)


if __name__ == "__main__":
trio.run(run_server)
7 changes: 5 additions & 2 deletions pymodbus/client/asynchronous/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Async Modbus Client implementation based on Twisted, tornado and asyncio
------------------------------------------------------------------------
Async Modbus Client implementation based on Twisted, tornado, asyncio, and Trio
-------------------------------------------------------------------------------

Example run::

Expand All @@ -21,6 +21,9 @@
# For asyncio based asynchronous client use
event_loop, client = Client(schedulers.ASYNC_IO, port=5020)

# For asyncio based asynchronous client use
client = Client(schedulers.TRIO, port=5020)

# Here event_loop is a thread which would control the backend and future is
# a Future/deffered object which would be used to
# add call backs to run asynchronously.
Expand Down
21 changes: 21 additions & 0 deletions pymodbus/client/asynchronous/factory/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,25 @@ def async_io_factory(host="127.0.0.1", port=Defaults.Port, framer=None,
return loop, client


def trio_factory(host="127.0.0.1", port=Defaults.Port, framer=None,
source_address=None, timeout=None, **kwargs):
"""
Factory to create Trio based asynchronous tcp clients
:param host: Host IP address
:param port: Port
:param framer: Modbus Framer
:param source_address: Bind address
:param timeout: Timeout in seconds
:param kwargs:
:return: tcp client
"""
from pymodbus.client.asynchronous.trio import init_tcp_client
proto_cls = kwargs.get("proto_cls", None)
client = init_tcp_client(proto_cls=proto_cls, host=host, port=port)

return client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inconsistent with other factories which return two values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do return two values, but I don't see the consistency in the meaning of those two values. Can you help me understand what sort of thing the first value should be and what the second should be? Then I can try to map that into the Trio stuff.

Twisted: protocol / deferred
Tornado: protocol / future
asyncio: event loop / client
Trio: ? / ?

I can see the similarity between Twisted and Tornado. I don't see how asyncio fits. Apparently when I coded this a couple months ago I didn't see what other useful thing I could return in the Trio case. We'll see if I can find something now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.



def get_factory(scheduler):
"""
Gets protocol factory based on the backend scheduler being used
Expand All @@ -117,6 +136,8 @@ def get_factory(scheduler):
return io_loop_factory
elif scheduler == schedulers.ASYNC_IO:
return async_io_factory
elif scheduler == schedulers.TRIO:
return trio_factory
else:
LOGGER.warning("Allowed Schedulers: {}, {}, {}".format(
schedulers.REACTOR, schedulers.IO_LOOP, schedulers.ASYNC_IO
Expand Down
1 change: 1 addition & 0 deletions pymodbus/client/asynchronous/schedulers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
REACTOR = "reactor"
IO_LOOP = "io_loop"
ASYNC_IO = "async_io"
TRIO = "trio"
17 changes: 11 additions & 6 deletions pymodbus/client/asynchronous/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pymodbus.client.asynchronous.factory.tcp import get_factory
from pymodbus.constants import Defaults
from pymodbus.compat import IS_PYTHON3, PYTHON_VERSION
from pymodbus.client.asynchronous.schedulers import ASYNC_IO
from pymodbus.client.asynchronous.schedulers import ASYNC_IO, TRIO

logger = logging.getLogger(__name__)

Expand All @@ -25,6 +25,7 @@ def __new__(cls, scheduler, host="127.0.0.1", port=Defaults.Port,
- reactor (Twisted)
- io_loop (Tornado)
- async_io (asyncio)
- trio (Trio)
:param scheduler: Backend to use
:param host: Host IP address
:param port: Port
Expand All @@ -34,11 +35,15 @@ def __new__(cls, scheduler, host="127.0.0.1", port=Defaults.Port,
:param kwargs: Other extra args specific to Backend being used
:return:
"""
if (not (IS_PYTHON3 and PYTHON_VERSION >= (3, 4))
and scheduler == ASYNC_IO):
logger.critical("ASYNCIO is supported only on python3")
import sys
sys.exit(1)
if not IS_PYTHON3:
if scheduler == ASYNC_IO and PYTHON_VERSION < (3, 4):
logger.critical("ASYNCIO is supported only on Python >= 3.4")
import sys
sys.exit(1)
elif scheduler == TRIO and PYTHON_VERSION < (3, 6):
logger.critical("TRIO is supported only on Python >= 3.6")
import sys
sys.exit(1)
factory_class = get_factory(scheduler)
yieldable = factory_class(host=host, port=port, framer=framer,
source_address=source_address,
Expand Down
Loading