在ModbusTcp读取中第二次尝试获得结果的原因是什么?

时间:2018-11-20 09:33:34

标签: python modbus pymodbus

有时候,当我第一次从Modbus设备读取数据时,我会立即收到错误响应,但是当我再次尝试时,它会作为预期结果响应。

这是第一次的错误消息:

pymodbus.exceptions.ModbusIOException(pymodbus.exceptions.InvalidMessageReceivedException('Incomplete message received, expected at least 8 bytes (0 received)'), 3)

这是iPython控制台中的简化代码段:

In [1]: from pymodbus.client.sync import ModbusTcpClient
In [2]: cli = ModbusTcpClient('192.168.1.152', port=502)
In [3]: cli.connect()
Out[3]: True
In [4]: req = cli.read_holding_registers(0x1e, 2, unit=21)  # First try.
In [5]: req.isError()
Out[5]: True
In [6]: req
Out[6]: pymodbus.exceptions.ModbusIOException(pymodbus.exceptions.InvalidMessageReceivedException('Incomplete message received, expected at least 8 bytes (0 received)'), 3)
In [7]: req = cli.read_holding_registers(0x1e, 2, unit=21)  # Second try.
In [8]: req.isError()
Out[8]: False
In [9]: req.registers  # As expected.
Out[9]: [16091, 15697]

是什么原因?


[更新]:

我激活Modbus从站/服务器日志记录:

import logging
from pymodbus.client.sync import ModbusTcpClient as ModbusClient

logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

client = ModbusClient('192.168.1.152', port=502)
client.connect() 

print('First Try: ')
res = client.read_holding_registers(0x1e, 2, unit=21)
print('isError = {}'.format(res.isError()))

print('Second Try: ')
res = client.read_holding_registers(0x1e, 2, unit=21)
print('isError = {}'.format(res.isError()))

client.close() 

出局:

First Try: 
DEBUG:pymodbus.transaction:Current transaction state - IDLE
DEBUG:pymodbus.transaction:Running transaction 1
DEBUG:pymodbus.transaction:SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x15 0x3 0x0 0x1e 0x0 0x2
DEBUG:pymodbus.client.sync:New Transaction state 'SENDING'
DEBUG:pymodbus.transaction:Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG:pymodbus.transaction:Transaction failed. (Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received)) 
DEBUG:pymodbus.framer.socket_framer:Processing: 
DEBUG:pymodbus.transaction:Getting transaction 1
DEBUG:pymodbus.transaction:Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
isError = True
Second Try: 
DEBUG:pymodbus.transaction:Current transaction state - TRANSACTION_COMPLETE
DEBUG:pymodbus.transaction:Running transaction 2
DEBUG:pymodbus.transaction:SEND: 0x0 0x2 0x0 0x0 0x0 0x6 0x15 0x3 0x0 0x1e 0x0 0x2
DEBUG:pymodbus.client.sync:New Transaction state 'SENDING'
DEBUG:pymodbus.transaction:Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG:pymodbus.transaction:Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG:pymodbus.transaction:RECV: 0x0 0x2 0x0 0x0 0x0 0x7 0x15 0x3 0x4 0x3f 0x99 0x8 0xe8
DEBUG:pymodbus.framer.socket_framer:Processing: 0x0 0x2 0x0 0x0 0x0 0x7 0x15 0x3 0x4 0x3f 0x99 0x8 0xe8
DEBUG:pymodbus.factory:Factory Response[ReadHoldingRegistersResponse: 3]
DEBUG:pymodbus.transaction:Adding transaction 2
DEBUG:pymodbus.transaction:Getting transaction 2
DEBUG:pymodbus.transaction:Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
isError = False

如您所见,在第一次尝试中我遇到了错误,但是在第二次尝试中我得到了不错的结果。

2 个答案:

答案 0 :(得分:0)

我在Keba热泵控制器上遇到了同样的问题。正如其他人所提到的,我的问题已通过将超时增加到10秒来解决,在您的示例中,是通过

client = ModbusClient('192.168.1.152',port = 502,timeout = 10)

10秒对我来说有一定的余地,我在wireshark中观察到总是大约5秒。从第二个请求开始,响应总是很容易在几分之一秒之内到达。

我的热泵​​供应商给出的原因是控制器内存不足,而modbus服务器仅在第一个传入的连接请求时启动。

@pymodbus开发人员:我建议将Default.timeout增加到10秒。

答案 1 :(得分:0)

为了建立一个客户端-客户端连接,需要填充 kwarg source_address,但是没有办法解除端口的绑定,socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 需要在 bind() 之前完成,这意味着执行后,操作系统需要捕获端口以使其再次可绑定。

from pymodbus.client.sync import ModbusTcpClient
import logging

logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def pymodbus_tcp_client(address, port):
    client = ModbusTcpClient(address, port=port, source_address=(address, port))
    client.set_debug(True)
    client.connect()
    if client.is_socket_open():
        client.socket.setsockopt(65535, 4, 1)   # Adds no effect (!)
        res_1 = client.read_holding_registers(0x1e, 5, unit=21)
        res_2 = client.write_register(0x1e, 10, unit=21)
        res_3 = client.read_holding_registers(0x1e, 5, unit=21)
        print(res_1.registers, res_2, res_3.registers)

    client.close()

if __name__ == '__main__':
    pymodbus_tcp_client('127.0.0.1', 5020)