pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.26k stars 922 forks source link

Same context for created object #1937

Closed titov32 closed 7 months ago

titov32 commented 8 months ago

It's probably a question rather than a problem. The same context is used when creating objects, specifically self.identity = ModbusDeviceIdentification(). How to correctly use pymodbus to have different contexts?

Versions

Pymodbus Specific

Description

I create different class modbus server, change self.identity.ProductCode and other parametrs, but when I run them all get one context. I expected different object classes, but get one class.

Code and Logs

from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
from pymodbus.server import StartAsyncUdpServer
import asyncio
from logger import info_log
import pymodbus

pymodbus.pymodbus_apply_logging_config("DEBUG")

class MirageBase:
    def __init__(self):
        super().__init__()
        self.store: ModbusSequentialDataBlock = ModbusSequentialDataBlock(0, [0] * 129)
        self.slave_context = ModbusSlaveContext(hr=self.store)
        self.context = ModbusServerContext(slaves=self.slave_context, single=True)
        self.identity = ModbusDeviceIdentification()
        self.identity.VendorName = 'Tornado\x00'

    async def change_register(self, address, value):
        while True:
            self.store.setValues(address, value)
            await asyncio.sleep(1)

    def read_register(self, address):
        r = self.store.getValues(address)[0]
        return r.registers

    async def run(self):
        info_log.info(f'Start Emulator IOD on IP address {self.ip_address}, model: {self.identity.ProductCode}')
        await StartAsyncUdpServer(self.context, identity=self.identity, address=(self.ip_address, 502))
        info_log.info(f'Stop Emulator IOD on IP address {self.ip_address}, model: {self.identity.ProductCode}')

class MirageNai(MirageBase):
    def __init__(self, ip_address):
        super().__init__()
        self.store: ModbusSequentialDataBlock = ModbusSequentialDataBlock(0, [0] * 129)
        self.ip_address = ip_address
        self.slave_context = ModbusSlaveContext(hr=self.store)
        self.context = ModbusServerContext(slaves=self.slave_context, single=True)

        # ниже информация которое будет выдавать устройство Nai
        self.identity = ModbusDeviceIdentification()
        self.identity.VendorName = 'Tornado\x00'
        self.identity.ProductCode = 'NAI'

class MirageNDI(MirageBase):
    """
    Mirage NDI, registers for use: 17-28 and 1001-1024
    """

    def __init__(self, ip_address):
        super().__init__()
        self.store: ModbusSequentialDataBlock = ModbusSequentialDataBlock(0, [0] * 1128)
        self.ip_address = ip_address
        self.slave_context = ModbusSlaveContext(hr=self.store)
        self.context = ModbusServerContext(slaves=self.slave_context, single=True)
        self.identity = ModbusDeviceIdentification()
        self.identity.VendorName = 'Tornado\x00'
        self.identity.ProductCode = 'NDI'

def create_coroutines(device_type) -> list:
    coroutines = []
    info_log.info(rf'Device type for create coroutine: {device_type}')
    match device_type:
        case 'NAI':
            mirage = MirageNai('10.2.11.2')
        case 'NDI':
            mirage = MirageNDI('10.2.11.9')
        case _:
            raise ValueError('Wrong Type Mirage')
    coroutines.append(mirage.run())
    return coroutines

async def main():
    list_coroutines = []
    devices = ['NDI', 'NAI']
    for device in devices:
        list_coroutines.extend(create_coroutines(device))
    await asyncio.gather(*list_coroutines)

if __name__ == '__main__':
    asyncio.run(main())

# please use the following to format logs when posting them here
import pymodbus

pymodbus.pymodbus_apply_logging_config("DEBUG")

2024-01-16 14:45:59,730 : info:70 : INFO : Device type for create coroutine: NDI 2024-01-16 14:45:59,732 : info:70 : INFO : Device type for create coroutine: NAI 2024-01-16 14:45:59,735 : info:30 : INFO : Start Emulator IOD on IP address 10.2.11.9, model: NAI 2024-01-16 14:45:59,735 DEBUG logging:103 Awaiting connections server_listener 2024-01-16 14:45:59,736 : info:30 : INFO : Start Emulator IOD on IP address 10.2.11.2, model: NAI 2024-01-16 14:45:59,736 DEBUG logging:103 Awaiting connections server_listener 2024-01-16 14:45:59,736 DEBUG logging:103 Connected to server 2024-01-16 14:45:59,736 DEBUG logging:103 Connected to server 2024-01-16 14:45:59,736 INFO logging:97 Server listening. 2024-01-16 14:45:59,736 INFO logging:97 Server listening.

janiversen commented 8 months ago

The server can pr definition only have 1 context! This is logical since 1 server == 1 device.

janiversen commented 8 months ago

Of course having different server objects would be 1 object = 1 device.

Currently the modbusIdentification is a singleton in the code and thus identical across all server objects.

Pull requests are welcome.

titov32 commented 8 months ago

To run on a single device and obtain different contexts, is it necessary to use different instances of the program (docker and others), or is it possible to handle everything within one program?

janiversen commented 8 months ago

You need to start multiple programs....A singleton, means there are only 1 object in one program space.

titov32 commented 8 months ago

Can you point out the place in the code where the singleton implementation is?

janiversen commented 8 months ago

"git grep ModbusDeviceIdentification" is your friend.

github-actions[bot] commented 7 months ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] commented 7 months ago

This issue was closed because it has been stalled for 5 days with no activity.