Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asyncio server implementation #400

Merged
merged 30 commits into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a2d79e1
#357 Support registration of custom requests
dhoomakethu Dec 3, 2018
f07dcee
#368 Fixes write to broadcast address
mdmuhlbaier Jan 10, 2019
2e169b6
Bump version to 2.2.0
dhoomakethu Jan 14, 2019
c410f5b
Merge branch '#357-Custom-Function' into pymodbus-2.2.0
dhoomakethu Jan 14, 2019
826240b
Fix #371 pymodbus repl on python3
dhoomakethu Jan 14, 2019
6e72e44
1. Fix tornado async serial client `TypeError` while processing incom…
dhoomakethu Jan 15, 2019
18fe036
[fix v3] poprawa sprawdzania timeout
MarekLew Jan 6, 2019
964a565
Release candidate for pymodbus 2.2.0
dhoomakethu Jan 16, 2019
6960d9c
Fix #377 when invalid port is supplied and minor updates in logging
dhoomakethu Jan 26, 2019
249ad8f
Merge remote-tracking branch 'upstream/dev' into dev
muhlbaier Jan 31, 2019
60aca50
#368 adds broadcast support for sync client and server
muhlbaier Jan 31, 2019
e07e01e
#368 Fixes minor bug in broadcast support code
muhlbaier Jan 31, 2019
5030514
Fixed erronous CRC handling
JStrbg Jan 16, 2019
1a24c1d
Merge branch 'pull/372' into pymodbus-2.2.0
dhoomakethu Feb 11, 2019
e5c2615
Update Changelog
dhoomakethu Feb 11, 2019
f66f464
Fix test coverage
dhoomakethu Feb 11, 2019
7650421
Fix #387 Transactions failing on 2.2.0rc2.
dhoomakethu Feb 16, 2019
6233706
Task Cancellation and CRC Errors
pazzarpj Dec 12, 2018
89d3909
Cherry pick commit from PR #367 , Update changelog , bump version to …
dhoomakethu Mar 6, 2019
a36ccbf
native asyncio implementation of ModbusTcpServer and ModbusUdpServer
cleandesign-contrib Mar 24, 2019
a952ff2
preliminary asyncio server examples
cleandesign-contrib Mar 24, 2019
50c5117
move serial module dependency into class instantiation
cleandesign-contrib May 2, 2019
dcd5074
unittests for asyncio based server implementation
cleandesign-contrib May 13, 2019
5e314f9
induce exception in execute method by mock patching the request objec…
cleandesign-contrib May 13, 2019
d5b8e1c
Merge remote-tracking branch 'upstream/dev' into pymodbus-2.2.0
cleandesign-contrib May 13, 2019
b89364f
move serial module dependency into class instantiation
cleandesign-contrib May 16, 2019
c5c2d2d
added asynctest depency to requirements-tests.txt
cleandesign-contrib May 19, 2019
985d0cf
add unittest skip condition for unsupported targets, remove failing a…
cleandesign-contrib May 19, 2019
9dd23be
remove logger setLevel call since doing so may override library consu…
cleandesign-contrib May 20, 2019
6b79723
remove async def/await keywords from unittest so that the ast can be …
cleandesign-contrib May 21, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
158 changes: 158 additions & 0 deletions examples/common/asyncio_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/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
# --------------------------------------------------------------------------- #
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.2.0'

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

# 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.)
# 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 pymodbus/client/sync.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import socket
import select
import serial
import time
import sys
from functools import partial
Expand Down Expand Up @@ -425,6 +424,7 @@ def __init__(self, method='ascii', **kwargs):
:param strict: Use Inter char timeout for baudrates <= 19200 (adhere
to modbus standards)
"""
import serial
self.method = method
self.socket = None
BaseModbusClient.__init__(self, self.__implementation(method, self),
Expand Down