Skip to content

Commit cd563f8

Browse files
authored
Simulator add calls functionality. (#1390)
* Simulator add calls functionality.
1 parent 52211ff commit cd563f8

File tree

7 files changed

+386
-214
lines changed

7 files changed

+386
-214
lines changed

examples/client_test.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python3
2+
"""Pymodbus Client modbus call examples.
3+
4+
Very small example to do experiments on.
5+
6+
The corresponding server must be started before e.g. as:
7+
8+
./server_async.py
9+
"""
10+
import asyncio
11+
import logging
12+
13+
import pymodbus.diag_message as req_diag
14+
import pymodbus.mei_message as req_mei
15+
from examples.client_async import run_async_client, setup_async_client
16+
from examples.helper import get_commandline
17+
from pymodbus.pdu import ExceptionResponse
18+
19+
20+
_logger = logging.getLogger()
21+
22+
23+
SLAVE = 0x01
24+
25+
26+
def _check_call(rr):
27+
"""Check modbus call worked generically."""
28+
assert not rr.isError() # test that call was OK
29+
assert not isinstance(rr, ExceptionResponse) # Device rejected request
30+
return rr
31+
32+
33+
# ------------------------------------------------------
34+
# Call modbus device (all possible calls are presented).
35+
# ------------------------------------------------------
36+
async def _handle_coils(client):
37+
"""Read/Write coils."""
38+
_logger.info("### Reading Coil different number of bits (return 8 bits multiples)")
39+
rr = _check_call(await client.read_coils(32, 1, slave=SLAVE)) # addr 2
40+
assert len(rr.bits) == 8
41+
42+
43+
async def _handle_discrete_input(client):
44+
"""Read discrete inputs."""
45+
_logger.info("### Reading discrete input, Read address:0-7")
46+
rr = _check_call(await client.read_discrete_inputs(32, 8, slave=SLAVE)) # addr 2
47+
assert len(rr.bits) == 8
48+
49+
50+
async def _handle_holding_registers(client):
51+
"""Read/write holding registers."""
52+
_logger.info("### write holding register and read holding registers")
53+
_check_call(await client.write_register(3, 17, slave=SLAVE))
54+
rr = None
55+
rr = _check_call(await client.read_holding_registers(3, 1, slave=SLAVE))
56+
assert rr.registers[0] == 17
57+
rr = _check_call(await client.read_holding_registers(4, 2, slave=SLAVE))
58+
assert rr.registers[0] == 9
59+
assert rr.registers[1] == 27177
60+
61+
62+
async def _execute_information_requests(client):
63+
"""Execute extended information requests."""
64+
_logger.info("### Running information requests.")
65+
rr = _check_call(
66+
await client.execute(req_mei.ReadDeviceInformationRequest(unit=SLAVE))
67+
)
68+
assert rr.information[0] == b"pymodbus"
69+
70+
71+
async def _execute_diagnostic_requests(client):
72+
"""Execute extended diagnostic requests."""
73+
_logger.info("### Running diagnostic requests.")
74+
rr = _check_call(await client.execute(req_diag.ReturnQueryDataRequest(unit=SLAVE)))
75+
assert not rr.message[0]
76+
77+
_check_call(
78+
await client.execute(req_diag.RestartCommunicationsOptionRequest(unit=SLAVE))
79+
)
80+
81+
82+
# ------------------------
83+
# Run the calls in groups.
84+
# ------------------------
85+
86+
87+
async def run_async_calls(client):
88+
"""Demonstrate basic read/write calls."""
89+
await _handle_coils(client)
90+
await _handle_discrete_input(client)
91+
await _handle_holding_registers(client)
92+
await _execute_information_requests(client)
93+
await _execute_diagnostic_requests(client)
94+
95+
96+
if __name__ == "__main__":
97+
cmd_args = get_commandline(
98+
server=False,
99+
description="Run modbus calls in asynchronous client.",
100+
)
101+
testclient = setup_async_client(cmd_args)
102+
asyncio.run(run_async_client(testclient, modbus_calls=run_async_calls))

pymodbus/factory.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class ServerDecoder:
113113
To add more implemented functions, simply add them to the list
114114
"""
115115

116-
function_table = [
116+
__function_table = [
117117
ReadHoldingRegistersRequest,
118118
ReadDiscreteInputsRequest,
119119
ReadInputRegistersRequest,
@@ -155,10 +155,15 @@ class ServerDecoder:
155155
ReadDeviceInformationRequest,
156156
]
157157

158+
@classmethod
159+
def getFCdict(cls):
160+
"""Build function code - class list."""
161+
return {f.function_code: f for f in cls.__function_table}
162+
158163
def __init__(self):
159164
"""Initialize the client lookup tables."""
160-
functions = {f.function_code for f in self.function_table}
161-
self.__lookup = {f.function_code: f for f in self.function_table}
165+
functions = {f.function_code for f in self.__function_table}
166+
self.__lookup = self.getFCdict()
162167
self.__sub_lookup = {f: {} for f in functions}
163168
for f in self.__sub_function_table:
164169
self.__sub_lookup[f.function_code][f.sub_function_code] = f

pymodbus/server/simulator/TODO.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
- Add action_random do not change with every read (--randomize from repl)
33
- Add change_rate to getValues (--change_rate from repl)
44

5+
- Code for register setValue (Uint32....)
56
- Code for calls
7+
- Format tracer/responder (hex, function name etc.)
8+
- Code for call tracer
9+
- Code for response tracer
10+
- Code for response simulator
611
- Code for logs
712
- Code for server
813

@@ -11,14 +16,9 @@
1116
- Code for JSON Calls
1217
- Code for JSON server
1318

14-
- Columns for all
15-
- Hide element in radio button
16-
1719
- Document simulator general
18-
- Document datastore-simulator dict
19-
- Document simulator server dict
2020
- Document JSON.
2121

2222
- Update datastore simulator example
2323

24-
- Add test of server simulator
24+
- Add test of server simulator (100% coverage)

0 commit comments

Comments
 (0)