Use the URsim as modbus server and read signal in a fastapi app

Hello,

I’m trying to create a test setup relying on the URsim docker image :
https://hub.docker.com/r/universalrobots/ursim_e-series

I have created a python modbus client from the pymodbus library that looks like this (ip is from the URsim container when initialized) :

import asyncio
import pymodbus.client as ModbusClient
from pymodbus import ExceptionResponse, ModbusException, pymodbus_apply_logging_config
from loguru import logger

class ModbusClient:
    def __init__(self, host='127.0.0.1', port=502, register_address=1, poll_interval=1):
        self.host = host
        self.port = port
        self.register_address = register_address
        self.poll_interval = poll_interval
        self.client = None
        self.last_value = None

        logger.add("modbus_client.log", rotation="1 MB")

    async def start_client(self):
        pymodbus_apply_logging_config("DEBUG")

        self.client = ModbusClient.AsyncModbusTcpClient(
            self.host,
            port=self.port,
        )

        await self.client.connect()
        assert self.client.connected

        await self.read_registers()

    async def read_registers(self):
        while True:
            try:
                rr = await self.client.read_coils(self.register_address, 1)
            except ModbusException as exc:
                logger.error(f"Received ModbusException({exc}) from library")
                self.client.close()
                return
            if rr.isError():
                logger.error(f"Received Modbus library error({rr})")
                self.client.close()
                return
            if isinstance(rr, ExceptionResponse):
                logger.error(f"Received Modbus library exception ({rr})")
                self.client.close()
                return

            current_value = rr.bits[0]
            if current_value != self.last_value:
                logger.info(f"Register value changed to: {current_value}")
                self.last_value = current_value

            await asyncio.sleep(self.poll_interval)

    async def run(self):
        await self.start_client()

if __name__ == "__main__":
    modbus_client = ModbusClient(host='172.18.0.2', port=502, register_address=1, poll_interval=1)  # Use the IP address of the URsim container
    asyncio.run(modbus_client.run())

Now I’d like to try it out, I have created a dedicated route that displays an html page with the name of the last modbus signal that was send.

However when accessing the URsim interface I don’t really understand what I’m supposed to be doing to send test modbus signals without creating a whole program.

I expect to send modbus signal from the robot and have my app polling the client every 0,5 second and display the name of the last signal send.

I read that the robot can act as a modbus server by itself.

The environment is in linux.

Thanks for your help! I stay available to share more details