Modbus-TK для Modbus RTU, чтение / запись нескольких регистров (код FN 23), возвращает код исключения 1 - PullRequest
2 голосов
/ 01 февраля 2020

Я использую 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-L C -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 ]

3 голосов
/ 02 февраля 2020

Далее к ответу от @maxy; modbus spe c указывает, что код исключения 1 (ILLEGAL FUNCTION) означает:

Код функции, полученный в запросе, не является допустимым действием для сервера (или подчиненного устройства). ). Это может быть связано с тем, что код функции применим только к новым устройствам и не был реализован в выбранном устройстве. Это также может указывать на то, что сервер (или подчиненный) находится в неправильном состоянии для обработки запроса этого типа, например, потому что он ненастроен и запрашивается для возврата значений регистра.

Итак, в этом случае я бы сказал, что устройство не поддерживает эту команду.

Однако, учитывая, что другой пользователь сообщил о проблеме с этой командой, я подумал, что стоит проверить кодировку:

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). Код был изменен на это в commit dcb0a2f115d7a9d63930c9b4466c4501039880a3 ; ранее это было:

pdu = struct.pack(
    ">BHHHHB",
    function_code, starting_address, quantity_of_x, starting_addressW_FC23,
    len(output_value), byte_count
)

Это имеет больше смысла для меня (вам нужен способ передать адрес, чтобы начать писать, и текущий интерфейс, похоже, не обеспечивает этого). Я добавил примечание к этому вопросу github .

Итак, в заключение ваша проблема, вероятно, связана с устройством, но даже если устройство поддерживает команду, я не думаю, что это будет работа из-за ошибки в modbus-tk.

2 голосов
/ 04 февраля 2020

На основании строгости ответа @ maxy, а затем ответа @ Brits я решил продолжить расследование. Цель состояла в том, чтобы определить, была ли причина root ошибкой modbus-tk или мое устройство не поддерживает код функции 23.

In modbus-tk Issue # 121 , ОП упоминает, что 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.

Я вернусь назад, если когда-нибудь найду устройство, которое поддерживает код 23 23.

2 голосов
/ 02 февраля 2020

Ваш отладочный вывод содержит следующую трассировку:

-> 1-23-0-2-0-1-0-23-0-1-2-0-1-55-131
<- 1-151-1-143-240

Примите во внимание следующее:

  • Второй байт равен 23, поэтому был отправлен правильный код функции.
  • На самом деле вы получили «недопустимый код функции» на проводе, который должен быть специально сгенерирован устройством. Вы не получите ошибку CR C, «недопустимый адрес» или «недопустимое значение».
  • Возможно (но я полагаю, несколько маловероятно), что устройство поддерживает код 23, но только для некоторых адресов.

Единственное, что могло остаться не так на вашей стороне, - это то, что библиотека испортила кодировку фактического запроса. Я не проверял другие байты, но, как прокомментировал Бритс , в modbus-tk могла быть ошибка с кодировкой. Возможно, что лицо, реализующее ведомое устройство, решило ответить «недопустимым кодом функции» на неправильно сформированный запрос.

Мне также кажется правдоподобным, что они просто не удосужились реализовать этот код функции. Например, simplymodbus даже не перечисляет его.

1 голос
/ 05 февраля 2020

У меня та же проблема, но я знаю, что мой подчиненный соответствует функциональному коду 23, это wa go 750-362. Я могу прочитать данные, но, похоже, функция пишет не по адресу. У меня нет ошибки кода функции.

Это команда, которую я отправляю:

inputExt = master.execute(1, cst.READ_WRITE_MULTIPLE_REGISTERS, 0, 5, output_value=[32767,32767,32767,32767,0x00ff])

Это то, что я вижу с помощью захвата Wireshark:

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? Ссылка на чтение в порядке.

...