Skip to content

Commit 92da0db

Browse files
authored
update package_test_tool (add 4 test scenarios) (#2107)
1 parent 579791c commit 92da0db

File tree

1 file changed

+63
-27
lines changed

1 file changed

+63
-27
lines changed

examples/package_test_tool.py

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131
3232
Send raw data packets to the server (remark data is frame+request)
3333
34-
*** handle_client_data(transport, data) ***
34+
*** simulate_server(transport, request) ***
3535
36-
Called when data is received from the client/server (remark data is frame+request)
36+
Called when data is received from the client (remark data is frame+request)
3737
3838
The function generates frame+response and sends it.
3939
40-
And one function which can be modified to test the server functionality:
40+
*** simulate_client(transport, response) ***
41+
42+
Called when data is received from the server (remark data is frame+request)
43+
4144
"""
4245
from __future__ import annotations
4346

@@ -46,7 +49,7 @@
4649

4750
import pymodbus.client as modbusClient
4851
import pymodbus.server as modbusServer
49-
from pymodbus import Framer, pymodbus_apply_logging_config
52+
from pymodbus import Framer, ModbusException, pymodbus_apply_logging_config
5053
from pymodbus.datastore import (
5154
ModbusSequentialDataBlock,
5255
ModbusServerContext,
@@ -69,6 +72,7 @@ def __init__(
6972
"""Initialize a stub instance."""
7073
self.stub_handle_data = handler
7174
super().__init__(params, is_server)
75+
self.is_tcp = params.comm_type == CommType.TCP
7276

7377
async def start_run(self):
7478
"""Call need functions to start server/client."""
@@ -78,7 +82,7 @@ async def start_run(self):
7882

7983
def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
8084
"""Handle received data."""
81-
self.stub_handle_data(self, data)
85+
self.stub_handle_data(self, self.is_tcp, data)
8286
return len(data)
8387

8488
def callback_connected(self) -> None:
@@ -95,27 +99,33 @@ def callback_new_connection(self) -> ModbusProtocol:
9599
return new_stub
96100

97101

102+
test_port = 5004 # pylint: disable=invalid-name
103+
98104
class ClientTester: # pylint: disable=too-few-public-methods
99105
"""Main program."""
100106

101107
def __init__(self, comm: CommType):
102108
"""Initialize runtime tester."""
109+
global test_port # pylint: disable=global-statement
103110
self.comm = comm
111+
host = NULLMODEM_HOST
104112

105113
if comm == CommType.TCP:
106114
self.client = modbusClient.AsyncModbusTcpClient(
107-
NULLMODEM_HOST,
108-
port=5004,
115+
host,
116+
port=test_port,
109117
)
110118
elif comm == CommType.SERIAL:
119+
host = f"{NULLMODEM_HOST}:{test_port}"
111120
self.client = modbusClient.AsyncModbusSerialClient(
112-
f"{NULLMODEM_HOST}:5004",
121+
host,
113122
)
114123
else:
115124
raise RuntimeError("ERROR: CommType not implemented")
116125
server_params = self.client.comm_params.copy()
117-
server_params.source_address = (f"{NULLMODEM_HOST}:5004", 5004)
118-
self.stub = TransportStub(server_params, True, handle_client_data)
126+
server_params.source_address = (host, test_port)
127+
self.stub = TransportStub(server_params, True, simulate_server)
128+
test_port += 1
119129

120130

121131
async def run(self):
@@ -135,6 +145,7 @@ class ServerTester: # pylint: disable=too-few-public-methods
135145

136146
def __init__(self, comm: CommType):
137147
"""Initialize runtime tester."""
148+
global test_port # pylint: disable=global-statement
138149
self.comm = comm
139150
self.store = ModbusSlaveContext(
140151
di=ModbusSequentialDataBlock(0, [17] * 100),
@@ -151,22 +162,23 @@ def __init__(self, comm: CommType):
151162
self.context,
152163
framer=Framer.SOCKET,
153164
identity=self.identity,
154-
address=(NULLMODEM_HOST, 5004),
165+
address=(NULLMODEM_HOST, test_port),
155166
)
156167
elif comm == CommType.SERIAL:
157168
self.server = modbusServer.ModbusSerialServer(
158169
self.context,
159170
framer=Framer.SOCKET,
160171
identity=self.identity,
161-
port=f"{NULLMODEM_HOST}:5004",
172+
port=f"{NULLMODEM_HOST}:{test_port}",
162173
)
163174
else:
164175
raise RuntimeError("ERROR: CommType not implemented")
165176
client_params = self.server.comm_params.copy()
166177
client_params.host = client_params.source_address[0]
167178
client_params.port = client_params.source_address[1]
168179
client_params.timeout_connect = 1.0
169-
self.stub = TransportStub(client_params, False, handle_server_data)
180+
self.stub = TransportStub(client_params, False, simulate_client)
181+
test_port += 1
170182

171183

172184
async def run(self):
@@ -175,7 +187,7 @@ async def run(self):
175187
Log.debug("--> Start testing.")
176188
await self.server.listen()
177189
await self.stub.start_run()
178-
await server_calls(self.stub)
190+
await server_calls(self.stub, (self.comm == CommType.TCP))
179191
Log.debug("--> Shutting down.")
180192
await self.server.shutdown()
181193

@@ -194,21 +206,43 @@ async def main(comm: CommType, use_server: bool):
194206
async def client_calls(client):
195207
"""Test client API."""
196208
Log.debug("--> Client calls starting.")
197-
_resp = await client.read_holding_registers(address=124, count=4, slave=0)
198-
209+
try:
210+
resp = await client.read_holding_registers(address=124, count=4, slave=0)
211+
except ModbusException as exc:
212+
txt = f"ERROR: exception in pymodbus {exc}"
213+
Log.error(txt)
214+
return
215+
if resp.isError():
216+
txt = "ERROR: pymodbus returned an error!"
217+
Log.error(txt)
218+
await asyncio.sleep(1)
219+
client.close()
220+
print("---> CLIENT all done")
199221

200-
async def server_calls(transport: ModbusProtocol):
201-
"""Test client API."""
222+
async def server_calls(transport: ModbusProtocol, is_tcp: bool):
223+
"""Test server functionality."""
202224
Log.debug("--> Server calls starting.")
203-
_resp = transport.send(b'\x00\x02\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' +
204-
b'\x07\x00\x03\x00\x00\x06\x01\x03\x00\x00\x00\x01')
225+
226+
if is_tcp:
227+
request = b'\x00\x02\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01'
228+
else:
229+
# 2 responses:
230+
# response = b'\x00\x02\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01' +
231+
# b'\x07\x00\x03\x00\x00\x06\x01\x03\x00\x00\x00\x01')
232+
# 1 response:
233+
request = b'\x00\x02\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01'
234+
transport.send(request)
205235
await asyncio.sleep(1)
206-
print("---> all done")
236+
transport.close()
237+
print("---> SERVER all done")
207238

208-
def handle_client_data(transport: ModbusProtocol, data: bytes):
239+
def simulate_server(transport: ModbusProtocol, is_tcp: bool, request: bytes):
209240
"""Respond to request at transport level."""
210-
Log.debug("--> stub called with request {}.", data, ":hex")
211-
response = b'\x01\x03\x08\x00\x05\x00\x05\x00\x00\x00\x00\x0c\xd7'
241+
Log.debug("--> Server simulator called with request {}.", request, ":hex")
242+
if is_tcp:
243+
response = b'\x00\x01\x00\x00\x00\x06\x00\x03\x00\x7c\x00\x04'
244+
else:
245+
response = b'\x01\x03\x08\x00\x05\x00\x05\x00\x00\x00\x00\x0c\xd7'
212246

213247
# Multiple send is allowed, to test fragmentation
214248
# for data in response:
@@ -217,12 +251,14 @@ def handle_client_data(transport: ModbusProtocol, data: bytes):
217251
transport.send(response)
218252

219253

220-
def handle_server_data(_transport: ModbusProtocol, data: bytes):
254+
def simulate_client(_transport: ModbusProtocol, _is_tcp: bool, response: bytes):
221255
"""Respond to request at transport level."""
222-
Log.debug("--> stub called with response {}.", data, ":hex")
256+
Log.debug("--> Client simulator called with response {}.", response, ":hex")
223257

224258

225259
if __name__ == "__main__":
226260
# True for Server test, False for Client test
227-
# asyncio.run(main(CommType.SERIAL, False), debug=True)
261+
asyncio.run(main(CommType.SERIAL, False), debug=True)
262+
asyncio.run(main(CommType.SERIAL, True), debug=True)
263+
asyncio.run(main(CommType.TCP, False), debug=True)
228264
asyncio.run(main(CommType.TCP, True), debug=True)

0 commit comments

Comments
 (0)