pymodbus-dev / pymodbus

A full modbus protocol written in python
Other
2.16k stars 889 forks source link

Server not returning data expected in data block #2185

Closed jcameron-sso closed 1 month ago

jcameron-sso commented 1 month ago

Versions

Pymodbus Specific

Description

Trying to read registers from a server, data was returned, but data is zero, expected data to be as supplied. tcpdump showed server as cause.

Code and Logs

#!/usr/bin/python3

import pymodbus
pymodbus.pymodbus_apply_logging_config("DEBUG")
from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

data_registers = [173, 8, 380, 168, 133, 192]

# Create a Modbus data block with your data
block = ModbusSequentialDataBlock(0, data_registers)

# Create a slave context and add your data block
context = ModbusSlaveContext(slaves={1: block})

# Create a server context
server_context = ModbusServerContext(slaves=context, single=True)

# Start the Modbus TCP server
StartTcpServer(context=server_context, address=("0.0.0.0", 32502))

"""
logs

2024-05-03 10:31:08,435 DEBUG logging:103 Awaiting connections server_listener
2024-05-03 10:31:08,435 INFO  logging:97 Server listening.
2024-05-03 10:31:10,892 DEBUG logging:103 Connected to server
2024-05-03 10:31:10,892 DEBUG logging:103 recv: 0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x3 0x0 0x0 0x0 0x6 old_data:  addr=None
2024-05-03 10:31:10,892 DEBUG logging:103 Handling data: 0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x3 0x0 0x0 0x0 0x6
2024-05-03 10:31:10,893 DEBUG logging:103 Processing: 0x0 0x1 0x0 0x0 0x0 0x6 0x0 0x3 0x0 0x0 0x0 0x6
2024-05-03 10:31:10,893 DEBUG logging:103 Factory Request[ReadHoldingRegistersRequest': 3]
2024-05-03 10:31:10,893 DEBUG logging:103 validate: fc-[3] address-1: count-6
2024-05-03 10:31:10,893 DEBUG logging:103 getValues: fc-[3] address-1: count-6
2024-05-03 10:31:10,893 DEBUG logging:103 send: 0x0 0x1 0x0 0x0 0x0 0xf 0x0 0x3 0xc 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
2024-05-03 10:31:10,893 DEBUG logging:103 Frame check, no more data!
2024-05-03 10:31:10,894 DEBUG logging:103 -> transport: received eof
2024-05-03 10:31:10,894 DEBUG logging:103 Connection lost server due to None
2024-05-03 10:31:10,894 DEBUG logging:103 Handler for stream [server] has been canceled

tcpdump wireshark follow tcp stream

rx 00 01 00 00 00 06 00 03 00 00 00 06
tx 00 01 00 00 00 0f 00 03 0c 00 00 00 00 00 00 00 00 00 00 00 00

test client

#!/usr/bin/python3

from pymodbus.client import ModbusTcpClient

server_ip = "127.0.0.1"
server_port = 32502

client = ModbusTcpClient(server_ip, port=server_port)

try:
    client.connect()

    start_address = 0
    num_registers = 6  # Read 10 registers starting from address 0
    response = client.read_holding_registers(0, 6, unit=1)

    if response.isError():
        print(f"Error reading data: {response}")
    else:
        # Extract the values from the response
        data_values = response.registers
        print(f"Read data: {data_values}")
        print(f"Raw response: {response}")
        print(f"Registers:C{response.registers}")
finally:
    # Close the client connection
    client.close()

test client output

Read data: [0, 0, 0, 0, 0, 0]
Raw response: ReadHoldingRegistersResponse (6)
Registers:C[0, 0, 0, 0, 0, 0]

"""
janiversen commented 1 month ago

Your setup is not correct, ModbusSlaveContext does not follow the Api, please see examples/server_async.py

janiversen commented 1 month ago

Closing since this is not a bug, but a configuration error. Help is in discussions.

jcameron-sso commented 1 month ago

Thanks. Using examples/server_async.py, removing complexity and features we don't need, our code above is changed to;

block = lambda : ModbusSequentialDataBlock(0, data_registers)
context = ModbusSlaveContext(di=block(), co=block(), hr=block(), ir=block())

So it isn't that Pymodbus.ModbusSlaveContext doesn't follow the Api, but that our use of it did not. My apologies.

Having made that change, the registers are no longer zero, but the registers are offset by one. We provide six registers, but a client can only read five. Client receives the second register value instead of the first register value.

client (REPL)

>>> from pymodbus.client import ModbusTcpClient
>>> client = ModbusTcpClient(...)
>>> client.connect()
True
>>> r = client.read_holding_registers(0, 6)
>>> r.isError()
True
>>> r = client.read_holding_registers(0, 5)
>>> r.isError()
False
>>> r.registers
[8, 380, 168, 133, 192]  # unexpected offset by one

server output

[173, 8, 380, 168, 133, 192]
Exception response Exception Response(131, 3, IllegalAddress)
janiversen commented 1 month ago

That's because there is a difference on 1 between the memory layout and the addressing...which is due to an old standard that address 0 was not allowed.

There is a "zero_mode" parameter to change that.

jcameron-sso commented 1 month ago

Thanks, that fixed it. For your interest, zero_mode is not discoverable in https://pymodbus.readthedocs.io/en/latest/search.html?q=zero_mode&check_keywords=yes&area=default

janiversen commented 1 month ago

Pull requests are welcome.

jcameron-sso commented 1 month ago

Good idea, thanks. https://github.com/pymodbus-dev/pymodbus/pull/2187