Skip to content

Commit ed09435

Browse files
authored
Allow multiple servers. (#1164)
1 parent 2210a59 commit ed09435

File tree

6 files changed

+118
-100
lines changed

6 files changed

+118
-100
lines changed

examples/client_payload.py

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,30 @@
77
88
Works out of the box together with payload_server.py
99
"""
10+
import asyncio
1011
import logging
1112
from collections import OrderedDict
1213

13-
from pymodbus.client import ModbusTcpClient as ModbusClient
14+
from pymodbus import pymodbus_apply_logging_config
15+
from pymodbus.client import AsyncModbusTcpClient
1416
from pymodbus.constants import Endian
1517
from pymodbus.payload import BinaryPayloadBuilder, BinaryPayloadDecoder
1618

1719

18-
# --------------------------------------------------------------------------- #
19-
# configure the client logging
20-
# --------------------------------------------------------------------------- #
21-
22-
FORMAT = (
23-
"%(asctime)-15s %(threadName)-15s"
24-
" %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s"
25-
)
26-
logging.basicConfig(format=FORMAT)
27-
log = logging.getLogger()
28-
log.setLevel(logging.INFO)
29-
20+
_logger = logging.getLogger()
3021
ORDER_DICT = {"<": "LITTLE", ">": "BIG"}
3122

3223

33-
def run_binary_payload_client():
24+
async def run_binary_payload_client(port):
3425
"""Run binary payload."""
26+
pymodbus_apply_logging_config()
27+
_logger.setLevel(logging.DEBUG)
28+
3529
# ----------------------------------------------------------------------- #
3630
# We are going to use a simple sync client to send our requests
3731
# ----------------------------------------------------------------------- #
38-
client = ModbusClient("127.0.0.1", port=5020)
39-
client.connect()
32+
client = AsyncModbusTcpClient("127.0.0.1", port=port)
33+
await client.connect()
4034

4135
# ----------------------------------------------------------------------- #
4236
# If you need to build a complex message to send, you can use the payload
@@ -97,23 +91,26 @@ def run_binary_payload_client():
9791
print("\n")
9892
payload = builder.build()
9993
address = 0
94+
slave = 1
10095
# We can write registers
101-
client.write_registers(address, registers, unit=1)
96+
rr = await client.write_registers(address, registers, slave=slave)
97+
assert not rr.isError()
10298
# Or we can write an encoded binary string
103-
client.write_registers(address, payload, skip_encode=True, unit=1)
99+
rr = await client.write_registers(address, payload, skip_encode=True, slave=1)
100+
assert not rr.isError()
104101

105102
# ----------------------------------------------------------------------- #
106103
# If you need to decode a collection of registers in a weird layout, the
107104
# payload decoder can help you as well.
108105
# ----------------------------------------------------------------------- #
109106
print("Reading Registers:")
110-
address = 0x0
111107
count = len(payload)
112-
result = client.read_holding_registers(address, count, slave=1)
113-
print(result.registers)
108+
rr = await client.read_holding_registers(address, count, slave=slave)
109+
assert not rr.isError()
110+
print(rr.registers)
114111
print("\n")
115112
decoder = BinaryPayloadDecoder.fromRegisters(
116-
result.registers, byteorder=byte_endian, wordorder=word_endian
113+
rr.registers, byteorder=byte_endian, wordorder=word_endian
117114
)
118115
# Make sure word/byte order is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder
119116
assert (
@@ -146,17 +143,14 @@ def run_binary_payload_client():
146143
)
147144
print("Decoded Data")
148145
for name, value in iter(decoded.items()):
149-
print(
150-
"%s\t" % name, # pylint: disable=consider-using-f-string
151-
hex(value) if isinstance(value, int) else value,
152-
)
146+
print(f"{name}\t{hex(value) if isinstance(value, int) else value}")
153147
print("\n")
154148

155149
# ----------------------------------------------------------------------- #
156150
# close the client
157151
# ----------------------------------------------------------------------- #
158-
client.close()
152+
await client.close()
159153

160154

161155
if __name__ == "__main__":
162-
run_binary_payload_client()
156+
asyncio.run(run_binary_payload_client(5020))

examples/modbus_forwarder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def setup_forwarder(args):
3939
for i in args.slaves:
4040
store[i.to_bytes(1, "big")] = RemoteSlaveContext(args.client, unit=i)
4141
else:
42-
store = RemoteSlaveContext(args.client)
42+
store = RemoteSlaveContext(args.client, unit=1)
4343
args.context = ModbusServerContext(slaves=store, single=True)
4444
return args
4545

examples/server_async.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,7 @@ def setup_server(args):
106106
single = False
107107
else:
108108
context = ModbusSlaveContext(
109-
di=datablock,
110-
co=datablock,
111-
hr=datablock,
112-
ir=datablock,
109+
di=datablock, co=datablock, hr=datablock, ir=datablock, unit=1
113110
)
114111
single = True
115112

examples/server_payload.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import asyncio
88
import logging
99

10+
from pymodbus import pymodbus_apply_logging_config
1011
from pymodbus.constants import Endian
1112
from pymodbus.datastore import (
1213
ModbusSequentialDataBlock,
@@ -23,13 +24,14 @@
2324
from pymodbus.version import version
2425

2526

26-
# set logging level for library.
2727
_logger = logging.getLogger()
28-
_logger.setLevel(logging.DEBUG)
2928

3029

31-
async def run_payload_server():
30+
async def run_payload_server(port):
3231
"""Run payload server."""
32+
pymodbus_apply_logging_config()
33+
_logger.setLevel(logging.DEBUG)
34+
3335
# ----------------------------------------------------------------------- #
3436
# build your payload
3537
# ----------------------------------------------------------------------- #
@@ -78,10 +80,10 @@ async def run_payload_server():
7880
await StartAsyncTcpServer(
7981
context,
8082
identity=identity,
81-
address=("127.0.0.1", 5020),
83+
address=("127.0.0.1", port),
8284
allow_reuse_address=True,
8385
)
8486

8587

8688
if __name__ == "__main__":
87-
asyncio.run(run_payload_server())
89+
asyncio.run(run_payload_server(5020))

pymodbus/server/async_io.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -932,16 +932,15 @@ def __init__(self, server, custom_functions, register):
932932
self.task = None
933933

934934
@classmethod
935-
def get_server(cls, index: int):
935+
def get_server(cls):
936936
"""Get server at index."""
937-
return cls._servers[index]
937+
return cls._servers[-1]
938938

939939
def _remove(self):
940940
"""Remove server from active list."""
941-
for i in range(len(self._servers)): # pylint: disable=consider-using-enumerate
942-
if self._servers[i] == self:
943-
del self._servers[i]
944-
break
941+
server = self._servers[-1]
942+
self._servers.pop()
943+
del server
945944

946945
async def run(self):
947946
"""Help starting/stopping server."""
@@ -952,7 +951,9 @@ async def run(self):
952951
_logger.error(txt)
953952
await self.job_stop.wait()
954953
await self.server.shutdown()
954+
await asyncio.sleep(0.1)
955955
self.task.cancel()
956+
await asyncio.sleep(0.1)
956957
try:
957958
await asyncio.wait_for(self.task, 10)
958959
except asyncio.CancelledError:
@@ -1152,15 +1153,15 @@ def StartUdpServer(**kwargs): # pylint: disable=invalid-name
11521153
return asyncio.run(StartAsyncUdpServer(**kwargs))
11531154

11541155

1155-
async def ServerAsyncStop(index: int = -1): # pylint: disable=invalid-name
1156+
async def ServerAsyncStop(): # pylint: disable=invalid-name
11561157
"""Terminate server."""
1157-
my_job = _serverList.get_server(index)
1158+
my_job = _serverList.get_server()
11581159
my_job.request_stop()
11591160
await my_job.async_await_stop()
11601161

11611162

1162-
def ServerStop(index: int = -1): # pylint: disable=invalid-name
1163+
def ServerStop(): # pylint: disable=invalid-name
11631164
"""Terminate server."""
1164-
my_job = _serverList.get_server(index)
1165+
my_job = _serverList.get_server()
11651166
my_job.request_stop()
11661167
my_job.await_stop()

0 commit comments

Comments
 (0)