В чем причина получения результата со второй попытки чтения ModbusTcp?

Иногда я сразу же получаю сообщение об ошибке при чтении с устройства 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]

В чем причина?


[ОБНОВИТЬ]:

Я активировал ведение журнала ModbusClient:

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

Как видите, с первой попытки я получил ошибку, но со второй попытки я получил правильный результат.


person Benyamin Jafari    schedule 20.11.2018    source источник
comment
Возможно, ваш подчиненный сервер медленно обрабатывает входящие запросы. Вы также можете установить тайм-аут чтения на своем клиенте и посмотреть, поможет ли это. Некоторые журналы было бы полезно проанализировать.   -  person Sanju    schedule 21.11.2018
comment
@Sanju Тайм-аут по умолчанию установлен на 3 секунды, но при первой попытке он не ждет тайм-аута и сразу же выдает результат с ошибкой. Был показан журнал ошибок в приведенном выше коде.   -  person Benyamin Jafari    schedule 21.11.2018
comment
Я имею в виду, что журналы транзакций регистрируют не только сообщение об ошибке, но и происходит ли это со всеми ведомыми устройствами или только с одной моделью ведомых устройств, которые у вас есть?   -  person Sanju    schedule 22.11.2018
comment
@ Санджу, хорошо, я проверю.   -  person Benyamin Jafari    schedule 22.11.2018
comment
@Sanju Я обновил свой ответ, указав ведение журнала подчиненного устройства Modbus.   -  person Benyamin Jafari    schedule 26.11.2018
comment
Нет ответа на первую транзакцию с заданным временем ожидания чтения и, следовательно, ошибка. Это может быть просто проблема с конкретным рабом, который у вас есть.   -  person Sanju    schedule 26.11.2018
comment
@Sanju Есть ли способ передать значение повторных попыток, когда я хочу создать объект .ModbusClient() по его аргументу или чему-то еще?   -  person Benyamin Jafari    schedule 19.01.2021
comment
Вы можете использовать retries kwarg, чтобы установить количество повторных попыток. См. github.com/riptideio/pymodbus/blob/   -  person Sanju    schedule 21.01.2021
comment
@Sanju Спасибо за ваш ответ. Раньше я использовал from pymodbus.constants import Defaults для установки повторных попыток через Defaults.Retries = 2, Defaults.RetryOnEmpty = True и не знал, что для этой цели существует kwarg.   -  person Benyamin Jafari    schedule 21.01.2021
comment
@Sanju Но у меня есть еще один вопрос, это retires относится к методу подключения или только к методам чтения? На самом деле у меня есть устройство Modbus, с подключением которого у меня иногда возникают проблемы, и я реализовал переподключение в своем коде, если оно пропало. Но странно то, что у меня нет проблем с подключением к этому устройству в Windows, а проблема только в ОС Linux, которую я решил несколькими переподключениями ...   -  person Benyamin Jafari    schedule 21.01.2021
comment
это для чтения, соединения должны обрабатываться вручную   -  person Sanju    schedule 21.01.2021


Ответы (2)


У меня была такая же проблема с контроллером теплового насоса Keba. Как уже упоминалось другим, моя проблема была решена путем увеличения времени ожидания до 10 секунд, в вашем примере

клиент = ModbusClient('192.168.1.152', порт=502, время ожидания=10)

10 секунд имели для меня некоторый запас, я наблюдал в wireshark всегда около 5 секунд. Начиная со второго запроса, ответы всегда приходят легко в течение долей секунды.

Причина, указанная поставщиком моего теплового насоса, заключалась в том, что контроллеру не хватает памяти, а сервер Modbus запускается только при первом входящем запросе на соединение.

Разработчики @pymodbus: я предлагаю увеличить Default.timeout до 10 секунд.

person RDorsch    schedule 28.11.2018
comment
Тайм-аут по умолчанию установлен на 3 секунды, но при первой попытке он не ждет тайм-аута и сразу же выдает результат с ошибкой, даже если timout=10 дает точно такой же результат. Но со второй попытки у меня нет никаких проблем. - person Benyamin Jafari; 29.11.2018
comment
Это явно отличается от того, что я вижу здесь. С тайм-аутом в 3 секунды мой первый запрос всегда терпит неудачу, через 10 секунд они проходят. Это относится как к pymodbus 2.1.0, так и к 1.5.2. Вы используете устаревшую версию pymodbus? - person RDorsch; 30.11.2018
comment
Я также тестирую его с pymodbus 1.5.2 и 2.1.0. - person Benyamin Jafari; 30.11.2018
comment
Странно, у меня заканчиваются идеи. Wireshark помог мне отладить мою проблему. Это может вам помочь, в частности, вы сможете увидеть, когда придет ответ от сервера.... - person RDorsch; 01.12.2018

Чтобы сделать клиент-клиентское соединение, заполняющее kwarg source_address, был необходим 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)

person Alex    schedule 19.01.2021