Skip to content

Commit 17bac6b

Browse files
authored
server tracer example. (#1773)
1 parent 580ed3e commit 17bac6b

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

doc/source/examples.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ Callback Server example
6464
^^^^^^^^^^^^^^^^^^^^^^^
6565
.. literalinclude:: ../../examples/server_callback.py
6666

67+
Server tracer example
68+
^^^^^^^^^^^^^^^^^^^^^
69+
.. literalinclude:: ../../examples/server_hook.py
70+
6771
Custom Message client
6872
^^^^^^^^^^^^^^^^^^^^^
6973
.. literalinclude:: ../../examples/client_custom_msg.py
@@ -107,6 +111,10 @@ Examples contributions
107111
These examples are supplied by users of pymodbus.
108112
The pymodbus team thanks for sharing the examples.
109113

114+
Solar
115+
^^^^^
116+
.. literalinclude:: ../../examples/contrib/solar.py
117+
110118
Redis datastore
111119
^^^^^^^^^^^^^^^
112120
.. literalinclude:: ../../examples/contrib/redis_datastore.py

examples/server_hook.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
"""Pymodbus Server With request/response manipulator.
3+
4+
This is an example of using the builtin request/response tracer to
5+
manipulate the messages to/from the modbus server
6+
"""
7+
import asyncio
8+
import logging
9+
10+
from pymodbus import pymodbus_apply_logging_config
11+
from pymodbus.datastore import (
12+
ModbusSequentialDataBlock,
13+
ModbusServerContext,
14+
ModbusSlaveContext,
15+
)
16+
from pymodbus.server import ModbusTcpServer
17+
from pymodbus.transaction import ModbusSocketFramer
18+
19+
20+
class Manipulator:
21+
"""A Class to run the server.
22+
23+
Using a class allows the easy use of global variables, but
24+
are not strictly needed
25+
"""
26+
27+
message_count: int = 1
28+
server: ModbusTcpServer = None
29+
30+
def server_request_tracer(self, request, *_addr):
31+
"""Trace requests.
32+
33+
All server requests passes this filter before being handled.
34+
"""
35+
print(f"---> REQUEST: {request}")
36+
37+
def server_response_manipulator(self, response):
38+
"""Manipulate responses.
39+
40+
All server responses passes this filter before being sent.
41+
The filter returns:
42+
43+
- response, either original or modified
44+
- skip_encoding, signals whether or not to encode the response
45+
"""
46+
if not self.message_count:
47+
print(f"---> RESPONSE: {response}")
48+
self.message_count = 3
49+
else:
50+
print("---> RESPONSE: NONE")
51+
response.should_respond = False
52+
self.message_count -= 1
53+
return response, False
54+
55+
async def setup(self):
56+
"""Prepare server."""
57+
pymodbus_apply_logging_config(logging.DEBUG)
58+
datablock = ModbusSequentialDataBlock(0x00, [17] * 100)
59+
context = ModbusServerContext(
60+
slaves=ModbusSlaveContext(
61+
di=datablock, co=datablock, hr=datablock, ir=datablock
62+
),
63+
single=True,
64+
)
65+
self.server = ModbusTcpServer(
66+
context,
67+
ModbusSocketFramer,
68+
None,
69+
("127.0.0.1", 5020),
70+
request_tracer=self.server_request_tracer,
71+
response_manipulator=self.server_response_manipulator,
72+
)
73+
74+
async def run(self):
75+
"""Attach Run server"""
76+
await self.server.serve_forever()
77+
78+
79+
async def main():
80+
"""Run example."""
81+
server = Manipulator()
82+
await server.setup()
83+
await server.run()
84+
85+
86+
if __name__ == "__main__":
87+
asyncio.run(main(), debug=True) # pragma: no cover

0 commit comments

Comments
 (0)