pymodbus-dev / pymodbus

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

Client Connect Error: Target Machine Refused It #2043

Closed dkhawajafaithtechinc closed 7 months ago

dkhawajafaithtechinc commented 7 months ago

Versions

Pymodbus Specific

Description

I have a server started up with the following code:

async def run_server(device):
    port = int(device['port'])
    device_name = device['name']
    try:
        print(f"Starting server for device {device_name}...")
        store = ModbusSlaveContext(
            di=ModbusSequentialDataBlock.create(),
            co=ModbusSequentialDataBlock.create(),
            hr=ModbusSequentialDataBlock.create(),
            ir=ModbusSequentialDataBlock.create())
        context = ModbusServerContext(slaves=store, single=True)
        ModbusTcpServer(context=context, address=("127.0.0.1", port))

        print(f"Server for device {device_name} is online")

        while device['runServer']:
            for i in #values coming from proprietary code:
                address = i['address']
                values = #proprietary code

                if values is not None:
                    context[0x03].setValues(3, address, values)

            # Print the values in the HR (holding registers) block
            print("Values in HR block:")
            # Get values for addresses 53000 to 53100
            res = [value for value in store.store["h"].values[53000:53101]]
            print(res)
            sleep(5)
        return True
    except Exception as e:
        print("Error starting server:", e)

The server runs properly and populates holding registers with appropriate values asynchronously. I am having trouble reading using a client. I wrote quick test code, but the error I get is:

Connection to (127.0.0.1, 601) failed: [WinError 10061] No connection could be made because the target machine actively refused it

Am I missing a parameter somewhere? I tried using broadcast_enable = True, but I am not sure.

Code and Logs

# Importing modbus modules
from pymodbus.client import ModbusTcpClient
ip = '127.0.0.1'
port = 601
c = ModbusTcpClient(host=ip, port=port, broadcast_enable = True)
def main():
    try:
        if not c.connect():
            return {"Error": f"Unable to connect to device at {ip}:{port}"}

        registers = c.read_holding_registers(53031, 1, 1)
        return registers.registers

    except ValueError as ve:
        return {"Error": f"Error with host or port params: {ve}"}

if __name__ == "__main__":
    main()
janiversen commented 7 months ago

Did you allow connections to that port in windows ? windows typically blocks ports below 2058.

And a stupid question the client runs on the same PC ?

janiversen commented 7 months ago

If you add our logging call (see examples or documentation), then you can see what the server does and especially if it starts listening.

janiversen commented 7 months ago

Just a hint, if you see the output in your while loop, then the server do not start.

janiversen commented 7 months ago

c.read_holding_registers(53031, 1, 1) do not follow the API, please see the documentation, but that a problem you will see later once connected.

dkhawajafaithtechinc commented 7 months ago

Port is allowed and yes client and server are running on the same pc. Turns out server was not listening. I am trying to get the server to serve using server.serve_forever. When I use await server.serve_forever, I get that the server is listening, but I don't want to await this because I need the rest of my code to run so I am trying to run it in a separate async task but when I do it seems that the server never starts listening until I call await server_task which still gives the same blocking issue. I know the following code is wrong because I was trying a few different things with asyncio.create_task but not 100% sure how to go about this

async def run_server(device):
    port = int(device['port'])
    device_name = device['name']
    try:
        print(f"Starting server for device {device_name}...")
        store = ModbusSlaveContext(
            di=ModbusSequentialDataBlock.create(),
            co=ModbusSequentialDataBlock.create(),
            hr=ModbusSequentialDataBlock.create(),
            ir=ModbusSequentialDataBlock.create())
        context = ModbusServerContext(slaves=store, single=True)
        server = ModbusTcpServer(context=context, address=("127.0.0.1", port))
        # Run the Modbus server in a separate task
        server_task = asyncio.create_task(server.serve_forever())
        await asyncio.wait_for(server.serving, timeout=10)
        print(f"Server for device {device_name} is online")

        while device['simulate']:

            for i in #values:
                address = i["address"]

                values = #some register values

                if values is not None:
                    context[0x03].setValues(3, address, values)

            # Print the values in the HR (holding registers) block
            print("Values in HR block:")
            # Get values for addresses 53000 to 53100
            res = [value for value in store.store["h"].values[53000:53101]]
            print(res)
            sleep(5)

        print(f"Shutting down server for device {device_name}...")
        server.shutdown() 
        # Wait for the server task to complete - DOESN'T COMPLETE
        await server_task

        print(f"Server for device {device_name} is offline")
        return True
    except Exception as e:
        print("Error starting server:", e)
dkhawajafaithtechinc commented 7 months ago

c.read_holding_registers(53031, 1, 1) do not follow the API, please see the documentation, but that a problem you will see later once connected.

Okay I finally got it to work by replacing sleep(5) with asyncio.sleep(5) and removing the await asyncio.wait_for...

For what you said about this, I am able to read using this code but you said it does not follow the documentation. How so?

janiversen commented 7 months ago

Read the documentation, that explains how to use the server without having to wait, but of course it does not tell you how to program async, which seems to be the problem.

read_holding_registers do not use 3 positional parameters, please read the documentation.

dkhawajafaithtechinc commented 7 months ago

Read the documentation, that explains how to use the server without having to wait, but of course it does not tell you how to program async, which seems to be the problem.

read_holding_registers do not use 3 positional parameters, please read the documentation.

The last parameter is for broadcasting which was needed in my case to be able to communicate

janiversen commented 7 months ago

OK....but this is not what the documentation says (nor what the API uses), and actually if you want broadcasting the value is not 1.