Skip to content

Commit 23f1f38

Browse files
authored
contrib example: TCP drainage simulator with two devices (#1936)
Co-authored-by: Steffen Beyer <[email protected]>
1 parent a7ca62a commit 23f1f38

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

examples/contrib/drainage_sim.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
3+
# Simulates two Modbus TCP slave servers:
4+
#
5+
# Port 5020: Digital IO (DIO) with 8 discrete inputs and 8 coils. The first two coils each control
6+
# a simulated pump. Inputs are not used.
7+
#
8+
# Port 5021: Water level meter (WLM) returning the current water level in the input register. It
9+
# increases chronologically and decreases rapidly when one or two pumps are active.
10+
11+
import asyncio
12+
import logging
13+
from datetime import datetime
14+
15+
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
16+
from pymodbus.server import StartAsyncTcpServer
17+
18+
INITIAL_WATER_LEVEL = 300
19+
WATER_INFLOW = 1
20+
PUMP_OUTFLOW = 8
21+
22+
logging.basicConfig(level = logging.INFO)
23+
24+
dio_di = ModbusSequentialDataBlock(1, [False] * 8)
25+
dio_co = ModbusSequentialDataBlock(1, [False] * 8)
26+
dio_context = ModbusSlaveContext(di = dio_di, co = dio_co)
27+
wlm_ir = ModbusSequentialDataBlock(1, [INITIAL_WATER_LEVEL])
28+
wlm_context = ModbusSlaveContext(ir = wlm_ir)
29+
30+
async def update():
31+
while True:
32+
await asyncio.sleep(1)
33+
34+
# Update water level based on DIO output values (simulating pumps)
35+
water_level = wlm_ir.getValues(1, 1)[0]
36+
dio_outputs = dio_co.getValues(1, 2)
37+
38+
water_level += WATER_INFLOW
39+
water_level -= (int(dio_outputs[0]) + int(dio_outputs[1])) * PUMP_OUTFLOW
40+
water_level = max(0, min(INITIAL_WATER_LEVEL * 10, water_level))
41+
wlm_ir.setValues(1, [water_level])
42+
43+
async def log():
44+
while True:
45+
await asyncio.sleep(10)
46+
47+
dio_outputs = dio_co.getValues(1, 8)
48+
wlm_level = wlm_ir.getValues(1, 1)[0]
49+
50+
logging.info(f"{datetime.now()}: WLM water level: {wlm_level}, DIO outputs: {dio_outputs}")
51+
52+
async def run():
53+
ctx = ModbusServerContext(slaves = dio_context)
54+
dio_server = asyncio.create_task(StartAsyncTcpServer(context = ctx, address = ("0.0.0.0", 5020)))
55+
logging.info("Initialising slave server DIO on port 5020")
56+
57+
ctx = ModbusServerContext(slaves = wlm_context)
58+
wlm_server = asyncio.create_task(StartAsyncTcpServer(context = ctx, address = ("0.0.0.0", 5021)))
59+
logging.info("Initialising slave server WLM on port 5021")
60+
61+
update_task = asyncio.create_task(update())
62+
logging_task = asyncio.create_task(log())
63+
64+
logging.info("Init complete")
65+
await asyncio.gather(dio_server, wlm_server, update_task, logging_task)
66+
67+
if __name__ == "__main__":
68+
asyncio.run(run(), debug=True)

0 commit comments

Comments
 (0)