modbus-tk для Modbus RTU, чтение / запись нескольких регистров (код fn 23), возвращает код исключения 1
Я использую modbus-tk
для последовательной связи с устройством через Modbus RTU по сети RS-485.
Я пытаюсь понять, как использовать функцию 23, READ_WRITE_MULTIPLE_REGISTERS
. Я впервые использую функцию 23. Вот моя текущая реализация:
response = modbus_master.execute(
slave=SLAVE_NUM,
function_code=cst.READ_WRITE_MULTIPLE_REGISTERS,
starting_address=2,
quantity_of_x=1,
output_value=[1],
)
При выполнении этой команды я получаю следующую ошибку: Modbus Error: Exception code = 1
Я нашел этот код исключения в Википедии и увидел:
Код функции, полученный в запросе, не распознается или не разрешен ведомым устройством
Как вы думаете, это означает, что мое устройство действительно не поддерживает этот код функции? Или у меня проблемы с синтаксисом / я неправильно использую эту функцию?
Я разместил свой полный сценарий ниже.
Пример полного кода
Ввод
#!/usr/bin/env python3
import time
from collections import namedtuple
from logging import Logger
from serial import Serial
from modbus_tk.modbus_rtu import RtuMaster
import modbus_tk.defines as cst # cst = constants
from modbus_tk.utils import create_logger
PORT = "COM3"
SLAVE_NUM = 1
MODBUS_MASTER_TIMEOUT_SEC = 5.0
ModbusHoldingReg = namedtuple(
"ModbusHoldingRegister", ["name", "address", "last_read_value", "to_write_value"]
)
shutdown_delay = ModbusHoldingReg("shutdown delay", 2, 0, None) # sec
logger = create_logger(name="console") # type: Logger
serial_ = Serial(PORT)
modbus_master = RtuMaster(serial_)
modbus_master.set_timeout(MODBUS_MASTER_TIMEOUT_SEC)
modbus_master.set_verbose(True)
# Sleep some time per [1]
# [1]: https://github.com/ljean/modbus-tk/issues/73#issuecomment-284800980
time.sleep(2.0)
# Read/write from/to multiple registers
response = modbus_master.execute(
slave=SLAVE_NUM,
function_code=cst.READ_WRITE_MULTIPLE_REGISTERS,
starting_address=shutdown_delay.address,
quantity_of_x=1,
output_value=[1],
) # type: tuple
print(response)
Выход
2020-01-31 10:43:24,885 INFO modbus_rtu.__init__ MainThread RtuMaster COM3 is opened
2020-01-31 10:43:26,890 DEBUG modbus.execute MainThread -> 1-23-0-2-0-1-0-23-0-1-2-0-1-55-131
2020-01-31 10:43:31,933 DEBUG modbus.execute MainThread <- 1-151-1-143-240
---------------------------------------------------------------------------
ModbusError Traceback (most recent call last)
<ipython-input-1-f42d200d6c09> in <module>
37 starting_address=shutdown_delay.address,
38 quantity_of_x=1,
---> 39 output_value=[1],
40 ) # type: tuple
41 print(response)
c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
37 ret = fcn(*args, **kwargs)
38 except Exception as excpt:
---> 39 raise excpt
40 finally:
41 if threadsafe:
c:\path\to\venv\lib\site-packages\modbus_tk\utils.py in new(*args, **kwargs)
35 lock.acquire()
36 try:
---> 37 ret = fcn(*args, **kwargs)
38 except Exception as excpt:
39 raise excpt
c:\path\to\venv\lib\site-packages\modbus_tk\modbus.py in execute(self, slave, function_code, starting_address, quantity_of_x, output_value, data_format, expected_length)
312 # the slave has returned an error
313 exception_code = byte_2
--> 314 raise ModbusError(exception_code)
315 else:
316 if is_read_function:
ModbusError: Modbus Error: Exception code = 1
Особенности устройства
- Устройство: SST Sensing's OXY-LC-485
- Modbus RTU,
9600/8-N-1
- Руководство пользователя (раздел 7.1.2.1 содержит набор входных регистров)
- Устройство подключено к машине Windows, на которой я запускаю этот скрипт Python
Пакеты
Я использую Python 3.6 в Windows 10.
pyserial==3.4
modbus-tk==1.1.0
4 ответа
Далее к ответу @maxy; в спецификации Modbus указано, что код исключения 1 (НЕЗАКОННАЯ ФУНКЦИЯ) означает:
Код функции, полученный в запросе, не является допустимым действием для сервера (или ведомого устройства). Это может быть связано с тем, что код функции применим только к более новым устройствам и не был реализован в выбранном устройстве. Это также может указывать на то, что сервер (или подчиненное устройство) находится в неправильном состоянии для обработки запроса этого типа, например, потому что он не настроен и его просят вернуть значения регистров.
Итак, в этом случае я бы сказал, что устройство не поддерживает эту команду.
Однако, учитывая, что другой пользователь сообщил о проблеме с этой командой, я подумал, что стоит проверить кодировку:
1- Slave ID
23- Function Code
0, 2- Read Starting Address
0, 1- Quantity to Read
0, 23- Write Starting Address
0, 1 - Quantity to write
2, Write Byte Count
0,1, - Write Registers value
55,131 - CRC (have not checked)
Мне это кажется правильным, за одним исключением; неясно, откуда берется "Начальный адрес записи" (и подозрительно, что он такой же, как код функции). Смотрим на источник:
pdu = struct.pack(
">BHHHHB",
function_code, starting_address, quantity_of_x, defines.READ_WRITE_MULTIPLE_REGISTERS,
len(output_value), byte_count
)
Мне это кажется неправильным (defines.READ_WRITE_MULTIPLE_REGISTERS
всегда будет 23). Код был изменен на этот в https://github.com/ljean/modbus-tk/commit/dcb0a2f115d7a9d63930c9b4466c4501039880a3; ранее это было:
pdu = struct.pack(
">BHHHHB",
function_code, starting_address, quantity_of_x, starting_addressW_FC23,
len(output_value), byte_count
)
Для меня это имеет больше смысла (вам нужен способ передать адрес, чтобы начать запись, а текущий интерфейс, похоже, этого не обеспечивает). Я добавил примечание об этом к проблеме с github.
Итак, в заключение ваша проблема, вероятно, связана с устройством, но даже если устройство поддерживает команду, я не думаю, что она будет работать из-за ошибки в modbus-tk.
Основываясь на строгости ответа @maxy, а затем на ответе @Brits, я решил продолжить расследование. Цель заключалась в том, чтобы определить, была ли основная причинаmodbus-tk
ошибка, или если мое устройство не поддерживает код функции 23.
В выпуске #121 modbus-tk OP упоминает, чтоpymodbus
работал с кодом функции 23, чтение / запись нескольких регистров.
Итак, я установил pymodbus==2.3.0
, а затем крутанул. Вот код, который я использовал:
Ввод
#!/usr/bin/env python3
import sys
import logging
from collections import namedtuple
from pymodbus.pdu import ModbusResponse, ExceptionResponse
from pymodbus.client.sync import ModbusSerialClient
from pymodbus.register_read_message import ReadWriteMultipleRegistersResponse
log = logging.getLogger()
log.addHandler(logging.StreamHandler(sys.stdout))
log.setLevel(logging.DEBUG)
ModbusHoldingReg = namedtuple(
"ModbusHoldingRegister", ["name", "address", "last_read_value", "to_write_value"]
)
sensor_mode = ModbusHoldingReg("sensor on, off, and standby enum", 0, None, None)
PORT = "COM3"
SLAVE_NUM = 1
BAUD_RATE = 9600
with ModbusSerialClient(
method="rtu", port=PORT, baudrate=BAUD_RATE, strict=False
) as modbus_client:
regs_to_write = [0, 1, 3]
response = modbus_client.readwrite_registers(
read_address=sensor_mode.address,
read_count=len(regs_to_write),
write_address=sensor_mode.address,
write_registers=regs_to_write,
unit=SLAVE_NUM,
) # type: ModbusResponse
if response.isError():
response: ExceptionResponse
print(
f"Exception! Original function code = {response.original_code}, "
f"exception_code = {response.exception_code}."
)
else:
response: ReadWriteMultipleRegistersResponse
print(f"Success! response.registers = {response.registers}.")
Выход
Current transaction state - IDLE
Running transaction 1
SEND: 0x1 0x17 0x0 0x0 0x0 0x3 0x0 0x0 0x0 0x3 0x6 0x0 0x0 0x0 0x1 0x0 0x3 0x5d 0xce
New Transaction state 'SENDING'
Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
RECV: 0x1 0x97 0x1 0x8f 0xf0
Getting Frame - 0x97 0x1
Factory Response[151]
Frame advanced, resetting header!!
Adding transaction 1
Getting transaction 1
Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Original function code = 23, exception code = 1.
Вывод
Видно, что устройство ответило кодом исключения 1, Illegal Function
. Поэтому я считаю, что это устройство не поддерживает код функции 23.
Я вернусь назад, если когда-нибудь найду устройство, поддерживающее код fn 23.
Ваш вывод отладки содержит следующую трассировку:
-> 1-23-0-2-0-1-0-23-0-1-2-0-1-55-131
<- 1-151-1-143-240
Обратите внимание на следующее:
- Второй байт - 23, значит, был отправлен правильный код функции.
- Вы действительно получили "недопустимый функциональный код" на проводе, который, должно быть, был специально сгенерирован устройством. Вы не получите ошибку CRC, "недопустимый адрес" или "недопустимое значение".
- Возможно (но маловероятно), что устройство поддерживает код 23, но только для некоторых адресов.
Единственное, что могло быть неправильно с вашей стороны, это то, что библиотека испортила кодировку фактического запроса. Я не проверял другие байты, но, как заметил британец, может быть ошибка в modbus-tk с кодировкой. Возможно, что человек, реализующий подчиненное устройство, решил ответить "недопустимым кодом функции" на некорректный запрос.
Мне также кажется правдоподобным, что они просто не удосужились реализовать этот код функции. Например, simplemodbus его даже не перечисляет.
У меня такая же проблема, но я знаю, что мой ведомый соответствует функциональному коду 23, это wago 750-362. Я могу читать данные, но кажется, что функция пишет по неправильному адресу. У меня нет ошибки кода функции.
Это команда, которую я отправляю:
inputExt = master.execute(1, cst.READ_WRITE_MULTIPLE_REGISTERS, 0, 5, output_value=[32767,32767,32767,32767,0x00ff])
Вот что я вижу при захвате wirehark:
Modbus/TCP
Transaction Identifier: 35394
Protocol Identifier: 0
Length: 21
Unit Identifier: 1
Modbus
.001 0111 = Function Code: Read Write Register (23)
Read Reference Number: 0
Read Word Count: 5
Write Reference Number: 23
Write Word Count: 5
Byte Count: 10
Data: 7fff7fff7fff7fff00ff
Почему контрольный номер записи, который должен быть адресом, по которому мы пишем, и таким же, как мы читаем, равен 23, а не 0? Ссылка на чтение в порядке.