pymodbus-dev / pymodbus

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

Weird: Server listening but cannot use port #1893

Closed mbilalshaikh closed 9 months ago

mbilalshaikh commented 9 months ago

Just running the server_async example.

Versions

Pymodbus Specific

Description

Just trying to start a async server (Serial[RTU])

python main.py -c serial -f rtu -p 502 WARNING logging:108 Failed to start server [Errno 2] could not open port 502: [Errno 2] No such file or directory: '502' 2023-11-28 INFO logging:96 Server listening.

janiversen commented 9 months ago

Without the code it's hard to help, however it seems you have call with a port that do not exist (for serial it's typically something like

port="/dev/tty01"

janiversen commented 9 months ago

Did you solve your problem ?

mbilalshaikh commented 9 months ago

Here is the source code. ` import asyncio import logging import pymodbus

import helper

from pymodbus import version as pymodbus_version from pymodbus.datastore import ( ModbusSequentialDataBlock, ModbusServerContext, ModbusSlaveContext, ModbusSparseDataBlock, ) from pymodbus.device import ModbusDeviceIdentification from pymodbus.server import ( StartAsyncSerialServer, StartAsyncTcpServer, StartAsyncTlsServer, StartAsyncUdpServer, )

_logger = logging.getLogger(file) _logger.setLevel(logging.INFO)

pymodbus.pymodbus_apply_logging_config("DEBUG")

def setup_server(description=None, context=None, cmdline=None): """Run server setup.""" args = helper.get_commandline(server=True, description=description, cmdline=cmdline) if context: args.context = context if not args.context: _logger.info("### Create datastore")

The datastores only respond to the addresses that are initialized

    # If you initialize a DataBlock to addresses of 0x00 to 0xFF, a request to
    # 0x100 will respond with an invalid address exception.
    # This is because many devices exhibit this kind of behavior (but not all)
    if args.store == "sequential":
        # Continuing, use a sequential block without gaps.
        datablock = ModbusSequentialDataBlock(0x00, [17] * 100)
    elif args.store == "sparse":
        # Continuing, or use a sparse DataBlock which can have gaps
        datablock = ModbusSparseDataBlock({0x00: 0, 0x05: 1})
    elif args.store == "factory":
        # Alternately, use the factory methods to initialize the DataBlocks
        # or simply do not pass them to have them initialized to 0x00 on the
        # full address range::
        datablock = ModbusSequentialDataBlock.create()

    if args.slaves:
        # The server then makes use of a server context that allows the server
        # to respond with different slave contexts for different slave ids.
        # By default it will return the same context for every slave id supplied
        # (broadcast mode).
        # However, this can be overloaded by setting the single flag to False and
        # then supplying a dictionary of slave id to context mapping::
        #
        # The slave context can also be initialized in zero_mode which means
        # that a request to address(0-7) will map to the address (0-7).
        # The default is False which is based on section 4.4 of the
        # specification, so address(0-7) will map to (1-8)::
        context = {
            0x01: ModbusSlaveContext(
                di=datablock,
                co=datablock,
                hr=datablock,
                ir=datablock,
            ),
            0x02: ModbusSlaveContext(
                di=datablock,
                co=datablock,
                hr=datablock,
                ir=datablock,
            ),
            0x03: ModbusSlaveContext(
                di=datablock,
                co=datablock,
                hr=datablock,
                ir=datablock,
                zero_mode=True,
            ),
        }
        single = False
    else:
        context = ModbusSlaveContext(
            di=datablock, co=datablock, hr=datablock, ir=datablock
        )
        single = True

    # Build data storage
    args.context = ModbusServerContext(slaves=context, single=single)

# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
args.identity = ModbusDeviceIdentification(
    info_name={
        "VendorName": "Pymodbus",
        "ProductCode": "PM",
        "VendorUrl": "https://github.com/pymodbus-dev/pymodbus/",
        "ProductName": "Pymodbus Server",
        "ModelName": "Pymodbus Server",
        "MajorMinorRevision": pymodbus_version,
    }
)
return args

async def run_async_server(args): """Run server.""" txt = f"### start ASYNC server, listening on {args.port} - {args.comm}" _logger.info(txt) if args.comm == "tcp": address = (args.host if args.host else "", args.port if args.port else None) server = await StartAsyncTcpServer( context=args.context, # Data storage identity=args.identity, # server identify

TBD host=

        # TBD port=
        address=address,  # listen address
        # custom_functions=[],  # allow custom handling
        framer=args.framer,  # The framer strategy to use
        # ignore_missing_slaves=True,  # ignore request to a missing slave
        # broadcast_enable=False,  # treat slave_id 0 as broadcast address,
        # timeout=1,  # waiting time for request to complete
        # TBD strict=True,  # use strict timing, t1.5 for Modbus RTU
    )
elif args.comm == "udp":
    address = (
        args.host if args.host else "127.0.0.1",
        args.port if args.port else None,
    )
    server = await StartAsyncUdpServer(
        context=args.context,  # Data storage
        identity=args.identity,  # server identify
        address=address,  # listen address
        # custom_functions=[],  # allow custom handling
        framer=args.framer,  # The framer strategy to use
        # ignore_missing_slaves=True,  # ignore request to a missing slave
        # broadcast_enable=False,  # treat slave_id 0 as broadcast address,
        # timeout=1,  # waiting time for request to complete
        # TBD strict=True,  # use strict timing, t1.5 for Modbus RTU
    )
elif args.comm == "serial":
    # socat -d -d PTY,link=/tmp/ptyp0,raw,echo=0,ispeed=9600
    #             PTY,link=/tmp/ttyp0,raw,echo=0,ospeed=9600
    server = await StartAsyncSerialServer(
        context=args.context,  # Data storage
        identity=args.identity,  # server identify
        # timeout=1,  # waiting time for request to complete
        port=args.port,  # serial port
        # custom_functions=[],  # allow custom handling
        framer=args.framer,  # The framer strategy to use
        # stopbits=1,  # The number of stop bits to use
        # bytesize=8,  # The bytesize of the serial messages
        # parity="N",  # Which kind of parity to use
        baudrate=args.baudrate,  # The baud rate to use for the serial device
        # handle_local_echo=False,  # Handle local echo of the USB-to-RS485 adaptor
        # ignore_missing_slaves=True,  # ignore request to a missing slave
        # broadcast_enable=False,  # treat slave_id 0 as broadcast address,
        # strict=True,  # use strict timing, t1.5 for Modbus RTU
    )
elif args.comm == "tls":
    address = (args.host if args.host else "", args.port if args.port else None)
    server = await StartAsyncTlsServer(
        context=args.context,  # Data storage
        host="localhost",  # define tcp address where to connect to.
        # port=port,  # on which port
        identity=args.identity,  # server identify
        # custom_functions=[],  # allow custom handling
        address=address,  # listen address
        framer=args.framer,  # The framer strategy to use
        certfile=helper.get_certificate(
            "crt"
        ),  # The cert file path for TLS (used if sslctx is None)
        # sslctx=sslctx,  # The SSLContext to use for TLS (default None and auto create)
        keyfile=helper.get_certificate(
            "key"
        ),  # The key file path for TLS (used if sslctx is None)
        # password="none",  # The password for for decrypting the private key file
        # ignore_missing_slaves=True,  # ignore request to a missing slave
        # broadcast_enable=False,  # treat slave_id 0 as broadcast address,
        # timeout=1,  # waiting time for request to complete
        # TBD strict=True,  # use strict timing, t1.5 for Modbus RTU
    )
return server

async def async_helper(): """Combine setup and run.""" _logger.info("Starting...") run_args = setup_server(description="Run asynchronous server.") await run_async_server(run_args)

def main():

print("Hello World!")

asyncio.run(async_helper(), debug=True)  # pragma: no cover

if name == "main": main() `

Not solved yet. Even after using

python main.py -c serial -f rtu -p /dev/tty01

janiversen commented 9 months ago

well it means your serial port is not /dev/tty01. You need to use the port where the rs-485 adapter is connected.

mbilalshaikh commented 9 months ago

I have figured out the serial ports and running with

python main.py -c serial -f rtu -p /dev/ttyS0

Having below warning.

2023-12-01 08:31:23,837 WARNING logging:108 Failed to start server Could not configure port: (5, 'Input/output error') 2023-12-01 08:31:23,837 INFO logging:96 Server listening.

mbilalshaikh commented 9 months ago

On windows machine, I am running

python main.py -c serial -f rtu -p COM3

Output:

2023-12-01 03:58:23,873 INFO logging:96 Server listening. 2023-12-01 03:58:23,873 ERROR logging:114 Server callback_connected exception: 'str' object is not callable; Traceback (most recent call last): File "C:\Users\MillSlicer\AppData\Local\Programs\Python\Python38\lib\site-packages\pymodbus\server\async_io.py", line 73, in callback_connected self.framer = self.server.framer( TypeError: 'str' object is not callable`

Need to understand and fix this Error

janiversen commented 9 months ago

The first one is because your user is not allowed to use the port.

The second one is because your code do not match the version of the library you use, the framer argument is wrong.

janiversen commented 9 months ago

btw if you use a usb rs485 converter it is surely not the correct port you use.

The error message cn be security but it can also mean that nothing is connected to the port.

mbilalshaikh commented 9 months ago

**Changed the machine and there are three RTU devices on port COM1, COM2 and COM3.

Using pymodbus==3.5.4 running with new parameters got the new response. If the server is listening here then which port the client be connected?**

python main.py -c serial -f rtu -p COM3

2023-12-01 08:34:11,917 INFO logging:96 Server listening. 2023-12-01 08:34:11,917 ERROR logging:114 Server callback_connected exception: 'str' object is not callable; Traceback (most recent call last): File "C:\Users\MillSlicer\AppData\Local\Programs\Python\Python38\lib\site-packages\pymodbus\server\async_io.py", line 73, in callback_connected self.framer = self.server.framer( TypeError: 'str' object is not callable

Executing <Handle ModbusProtocol.connection_made() created at C:\Users\MillSlicer\AppData\Local\Programs\Python\Python38\lib\site-packages\pymodbus\transport\transport_serial.py:35> took 0.203 seconds

janiversen commented 9 months ago

Do you have 3 seríal-rs485 converters ? each connected to a different com port.

That is really not how you cable modbus devices.

The error with the frame parameter is because you use code which are made for a different version of pymodbus.

mbilalshaikh commented 9 months ago

Updated code to

client = Mo1dbusSerialClient(method="rtu", port=f"COM{i}", baudrate=115200, timeout=1, stopbits=2, parity='N', bytesize=8) client.connect()
while client.connect() != False:
assert client.connected
print('connected')
rr = client.read_holding_registers(address= 0x0010, count=2, unit=1)
print(rr.registers)

sleep 2s before next polling

    time.sleep(2)

Error connected Traceback (most recent call last): File "search_slicer.py", line 55, in result = search_slicer() File "search_slicer.py", line 25, in search_slicer print(rr.registers) AttributeError: 'ModbusIOException' object has no attribute 'registers'

using pymodbus version 3.5.4

janiversen commented 9 months ago

You do not test the return value as suggested.....an exception do not have .registers, that is only in valid messages

mbilalshaikh commented 9 months ago

updated to

while client.connect() != False: assert client.connected print('connected') try: rr = client.read_holding_registers(address= 900, count=60, unit=1) print(rr) except: print('an exception occured') finally: print('test finished')

Error: Modbus Error: [Input/Output] No Response received from the remote slave/Unable to decode response