pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.22k stars 907 forks source link

ModbusTCP: identify slave context by peer ip and unit id #1913

Closed ickedude closed 7 months ago

ickedude commented 8 months ago

The slave context is now identified by a tuple of the peer IP address and unit identifier of the request. It allows to have multiple master sending an unit identifier of 0xFF and also to implement a bridge with routing based on both information.

minimal working example:

#!/usr/bin/env python3

import asyncio

from pymodbus.datastore.store import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext
from pymodbus.datastore import ModbusServerContext
from pymodbus.server import StartAsyncTcpServer
from pymodbus.framer.socket_framer import ModbusSocketFramer

async def run_modbus_server():
    ds1 = ModbusSequentialDataBlock(0, [0]*100)
    ds2 = ModbusSequentialDataBlock(0, [0]*64)
    ds3 = ModbusSequentialDataBlock(0, [0]*64)
    ds4 = ModbusSequentialDataBlock(0, [0]*144)
    slaves = {
        ('192.168.10.22', 0xFF): ModbusSlaveContext(di=ds1, co=ds1, hr=ds1, ir=ds1, zero_mode=True),
        ('192.168.10.20', 0x01): ModbusSlaveContext(di=ds2, co=ds2, hr=ds2, ir=ds2),
        ('192.168.10.20', 0x02): ModbusSlaveContext(di=ds3, co=ds3, hr=ds3, ir=ds3),
        ('192.168.10.21', 0xFF): ModbusSlaveContext(di=ds4, co=ds4, hr=ds4, ir=ds4),
    }
    server_context = ModbusServerContext(slaves=slaves, single=False)
    server = await StartAsyncTcpServer(
        context=server_context,
        address=('192.168.10.10', 5020),
        framer=ModbusSocketFramer,
    )
    return server

async def main() -> None:
    server = asyncio.create_task(run_modbus_server())
    await server

if __name__ == "__main__":
    asyncio.run(main(), debug=True)
ickedude commented 8 months ago

I guess I cannot resolve the failing check, because CI / windows-latest - 3.12 reports No Python at '"C:\hostedtoolcache\windows\Python\3.12.0\x64\python.exe'

janiversen commented 8 months ago

In general you need to document this feature, so that users know how to use it.

An example (or extending an existing example) would also be nice, since most users look more at the examples than at the documentation.

Have you considered, what happens to a server running tcp, with framer=socket and the slaves defined as slave id´s not uid...we need to secure that those still work unchanged.

ickedude commented 8 months ago

In general you need to document this feature, so that users know how to use it.

You are totally right. I created the pull request to start a conversation.

Have you considered, what happens to a server running tcp, with framer=socket and the slaves defined as slave id´s not uid...we need to secure that those still work unchanged.

I don't get this part. Do you mean the RemoteSlaveContext and related forwarder examples? If you mean that, then you are right. Did not know about RemoteSlaveContext till today.

janiversen commented 8 months ago

Having read your updated description, I think there is a fundamental flaw/misunterstanding this this Pull Request!

slaves are NOT clients! slave id == unit id (its just a confusion of names unit=socket, slave=serial, but they mean the same).

slave id / unit id == 0xff is just another slave nothing special. slave id / unit id == 0x00 means broadcast.

one server emulates n slaves/units, and when multiple client read the same register in the same slave they should pr definition get the same content.

Example:

Server contains slaves/units 1,2 and 3, slave 3 register 10 have a value of 17.

ClientA connect to server, and can address slave 1,2 and when reading slave 3 register 10 17 is returned

ClientB connect to server, and can address slave 1,2 and when reading slave 3 register 10 17 is returned

Your configuration would allow clientA and clientB to read different values for the same register and that would be wrong.

janiversen commented 8 months ago

Sorry for not catching this when you started, but I thought you were talking about ModbusDeviceIdentification.

Alex0176 commented 8 months ago

Thats exacly what i searching for - is it possible to modify the datastore for eatch instance/client ip with diffrent content in runtime?

Alex0176 commented 8 months ago

Having read your updated description, I think there is a fundamental flaw/misunterstanding this this Pull Request!

slaves are NOT clients! slave id == unit id (its just a confusion of names unit=socket, slave=serial, but they mean the same).

slave id / unit id == 0xff is just another slave nothing special. slave id / unit id == 0x00 means broadcast.

one server emulates n slaves/units, and when multiple client read the same register in the same slave they should pr definition get the same content.

Example:

Server contains slaves/units 1,2 and 3, slave 3 register 10 have a value of 17.

ClientA connect to server, and can address slave 1,2 and when reading slave 3 register 10 17 is returned

ClientB connect to server, and can address slave 1,2 and when reading slave 3 register 10 17 is returned

Your configuration would allow clientA and clientB to read different values for the same register and that would be wrong.

In my case i use pymodbus to simulate a smartmeter for more solar inverters and this inverters need diffrent serial numbers on the modbus. So i have to modify the serial number for every ,,fake" smartmeter. Otherwise i get troubles with the visualisation from the vendor. i step up for every inverter and if the identifierer is 0,1,2,3 i add to the original one, this value.

In the future i like to shift the load a little bit between the inverters, depend on the battery level or other situations.

janiversen commented 7 months ago

That is easy to do, give each device its own slave id and define a storage for each (NOT single) slave, then you can modify it as you like, that way reading register X can have different values for each slave.

ModbusDeviceIdentification is a singleton, and cannot be different for each slave...but it seems to me you are only looking to have different registers.

janiversen commented 7 months ago

And just to be clear your PR does not change ModbusDeviceInformation at all.

janiversen commented 7 months ago

Closing, as it seems to be stale.

If/when you start working on it again, feel free to reopen the Pr.