One SimCore over multi server instance #2927
-
|
Hello, I am currently implementing a modbus server using this library at its vers 3.13. I am thus using the new SimDevice/SimData architecture. Then I find this : pymodbus/pymodbus/server/base.py Lines 47 to 53 in 8a97aa0 And now I am doing this trick : import random
import asyncio
from pymodbus import FramerType
from pymodbus.server import ModbusSerialServer, ModbusTcpServer
from pymodbus.simulator import DataType, SimData, SimDevice
from pymodbus.simulator.simcore import SimCore
class MyServers:
def __init__(self):
self.deviceId = 1
self.device = SimCore(SimDevice(id=self.deviceId, simdata=[SimData(
10, 1, 0, DataType.REGISTERS
)]))
dummy = SimDevice(id=1, simdata=[SimData(1, 1, 0, DataType.REGISTERS)])
self.modbusTcpServer = ModbusTcpServer(
context=dummy,
framer=FramerType.SOCKET,
address=("0.0.0.0", 1502)
)
self.modbusTcpServer.context = self.device
self.modbusSerialServer = ModbusSerialServer(
context=dummy,
port="/dev/ttyUSB0",
framer=FramerType.RTU,
stopbits=2,
bytesize=8,
parity='N',
baudrate=9600,
)
self.modbusSerialServer.context = self.device
async def set_registers(self, address, value):
await self.device.async_setValues(self.deviceId, 3, address, [value])
async def Update(self):
newValue = random.randint(1, 50)
print(newValue)
await self.set_registers(10, newValue)
async def do_async():
servers = MyServers()
await servers.modbusSerialServer.serve_forever(background = True)
await servers.modbusTcpServer.serve_forever(background = True)
while True:
await servers.Update()
await asyncio.sleep(1)
asyncio.run(do_async())So here is my question: Am I missing a mechanism to do it more nicely? Thank you, |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 4 replies
-
|
I am not sure what your problem is ? you point at a copule of lines where SimDevice is activated in the server, after that point SimDevice is no longer used. Please tell what is your problem ? SimCore is an internal class that you should not use. |
Beta Was this translation helpful? Give feedback.
-
|
You cannot share a device between múltiple servers! Each server have it's own devices. Your workaround will most likely fail when running long term, due to inconsistent register images. if 2 servers run with the same registers and server A updates a registerm server B are likely to be "confused" because the update happened without it knowing it. But the solution for your problem is easy, use the same SimDevice as context=, but have 2 server.set calls one to each server. |
Beta Was this translation helpful? Give feedback.
-
|
I just read your answer, so i will share my update first... Because i was facing the fact that my TCP server wasn't answering to all id (more or less mandatory for a modbus tcp server), i came up to change a bit the code and ended with this one : import random
import asyncio
from pymodbus import FramerType
from pymodbus.server import ModbusSerialServer, ModbusTcpServer
from pymodbus.simulator import DataType, SimData, SimDevice
from pymodbus.simulator.simcore import SimCore
from pymodbus.simulator.simruntime import SimRuntime
class MyServers:
def __init__(self):
self.device = SimRuntime(SimDevice(id=1, simdata=[SimData(
10, 1, 0, DataType.REGISTERS
)]))
dummy = SimDevice(id=1, simdata=[SimData(1, 1, 0, DataType.REGISTERS)])
self.modbusTcpServer = ModbusTcpServer(
context=dummy,
framer=FramerType.SOCKET,
address=("0.0.0.0", 1502)
)
wrappedDevice = SimCore([])
wrappedDevice.devices[0] = self.device
self.modbusTcpServer.context = wrappedDevice
self.modbusSerialServer = ModbusSerialServer(
context=dummy,
port="/dev/ttyUSB0",
framer=FramerType.RTU,
stopbits=2,
bytesize=8,
parity='N',
baudrate=9600,
)
wrappedDevice = SimCore([])
wrappedDevice.devices[20] = self.device
self.modbusSerialServer.context = wrappedDevice
async def Update(self):
newValue = random.randint(1, 50)
print(newValue)
await self.set_registers(10, newValue)
async def set_registers(self, address, value):
await self.device.async_setValues(3, address, [value])
async def do_async():
servers = MyServers()
await servers.modbusSerialServer.serve_forever(background = True)
await servers.modbusTcpServer.serve_forever(background = True)
while True:
await servers.Update()
await asyncio.sleep(1)
asyncio.run(do_async())That way my serial server only answer to id 20, and my TCP server does answer to any id. Currently my tests are a success.
-> That being said, you warn me about "(...)due to inconsistent register images.(...)" what does it means? Do you have something in mind? Thank you for your share. |
Beta Was this translation helpful? Give feedback.
-
|
In my experience, which is a lifetime (30+ years of work in the industrial automation sector) with modbus: All the professional tcp servers I know do NOT respond to undefined ids ! This is independent if the RTU or SOCKET frame is used. A lot of servers have a configuration option to enable/disable broadcast, and in this case responds with all known device ids. The TCP/IP address only defines the server, NOT the devices within the server, you need both to uniquely address a device. Gateways (which is one of the most popular uses of a TCP server, are nothing special be merely a normal server with clients as backend. --> Here is mix up between RTU and TCP... Not correct id=0 is broadcast independent of the framer type used. If a client sends a request with id=0 all devices must respond against independent of how the request is transmitted. Please remember that a TCP server can use SOCKET or RTU frames, internally the modbus request with its id are typically handled identically. And actually on serial lines , all devices must respond to id=0....this feature is often used to detect connected device. RS485 is half duplex, but most RS485 chips checks if there are traffic on the line, and wait for a silent period (3.5chars) before transmitting. pymodbus server allows you to define id=0, so you define the response, and currently do not send a response from all devices, this is actually a know bug, which we are working to fix. Regarding you timing concern, that is easy solved.
Jan |
Beta Was this translation helpful? Give feedback.
You cannot share a device between múltiple servers! Each server have it's own devices.
Your workaround will most likely fail when running long term, due to inconsistent register images.
if 2 servers run with the same registers and server A updates a registerm server B are likely to be "confused" because the update happened without it knowing it.
But the solution for your problem is easy, use the same SimDevice as context=, but have 2 server.set calls one to each server.