Содержание
Драйвер &ModbusProxy - это прокси (посредник), через который подключаются драйверы устройств на шине MODBUS. Он решает задачу виртуализации ввода-вывода и сериализации (выстраивания в очередь) запросов от множества устройств, работающих в разных программных потоках. Это позволяет упростить взаимодействие драйверов MODBUS устройств и сделать их программирование проще.
Клиент &ModbusProxy:
реализует протокол приемо-передачи MODBUS (клиент,мастер), в соответствии со спецификациями
"MODBUS application protocol specification V1.1b",
"MODBUS messaging on TCP/IP implementation guide V1.0b" и
"MODBUS over serial line specification and implementation guide V1.02".
поддерживает функции MODBUS номер 1, 2, 3, 4, 5, 6, 15, 16.
поддерживает протоколы MODBUS-TCP/IP, MODBUS-RTU, MODBUS-ASCII.
поддерживает (в текущей версии) до 16 логических портов.
Таким образом, клиент "закрывает" сразу большой спектр задач.
Клиент &ModbusProxy является универсальным посредником, связующим звеном между подчиненными драйверами и устройствами MODBUS. Клиент &ModbusProxy ничего не делает сам по себе, он лишь обеспечивает прием и передачу данных через порты по протоколу MODBUS, выстраивая опросы от драйверов в очередь и предотвращая конфликты при наличии множества устройств на каждом порте. В то же время подчиненные драйверы напрямую не работают с физическими портами (TCP,COM), они работают с логическими портами (номерами 1..16). Всю работу, связанную с физическими портами, берет на себя клиент &ModbusProxy.
Подчиненные драйверы периодически посылают в консоль &ModbusProxy команду [@Modbus.Poll](#poll_logic) которая помещается в очередь для последующего выполнения. Когда приходит очередь, клиент &ModbusProxy инициирует опрос физического порта, связанного с указанным в запросе логическим портом и по результатам опроса посылает драйверу результат в виде консольной команды (одной из @Modbus.Reply, @Modbus.Refuse, @Modbus.TimeOut).
&ModbusProxy не требует регистрации драйверов, которые к нему обращаются, поскольку в каждом запросе указывается имя (или ссылка) драйвера, которому надо передать ответ для последующей обработки. Таким образом, драйверы могут пользоваться услугами клиента &ModbusProxy в довольно свободном режиме. Например, драйвер может обрабатывать ввод-вывод нескольких модулей на разных портах, а разные драйверы могут опрашивать один модуль. Клиент &ModbusProxy обеспечивает бесконфликтную сериализацию (выстраивание опросов в очередь) и канальный уровень приемо-передачи (порты, заголовки, контрольные суммы, отработка таймаутов). Таким образом, подчиненные драйверы могут сосредоточиться на обработке данных.
При написании подчиненных драйверов (как и самого клиента &ModbusProxy) используется библиотека NetLibrary.
Имеется работающий пример demo_mdbs.
Конфигурация клиента &ModbusProxy имеет примерно такой вид:
[DeviceList]
&ModbusProxy = device software program
[&ModbusProxy]
Comment = MODBUS PROXY MASTER.
InquiryPeriod = 0
DevicePolling = 1, tpTimeCritical
ProgramSource = ~~\resource\daqsite\modbusproxy\modbusproxy.pas
DebugFlags = 3
OpenConsole = 2
StdInFifo = 512
StdOutFifo = 512
StartingOrder = -1024
StoppingOrder = +1024
StartupScript = [&ModbusProxy.StartupScript]
FinallyScript = [&ModbusProxy.FinallyScript]
[]
либо просто делается ссылка на стандартную Конфигурацию
[ConfigFileList]
ConfigFile = ~~\resource\daqsite\default\modbusproxy.cfg
[]
при этом
в секцию [&ModbusProxy.StartupScript] надо поместить описание портов MODBUS.Таблица логических портов MODBUS задается в секции [&ModbusProxy.StartupScript] через консольные команды, описанные ниже. Поэтому эта секция обязательно должна быть описана. Хотя, строго говоря, все описание может генерироваться и динамически, через посылку сообщений. При этом размер буфера StdInFifo должен быть достаточным для размещения таблиц из секции StartupScript.
Команда @Reset очищает все таблицы портов и приводит клиент в исходное состояние. С этой команды рекомендуется начинать описание таблиц портов клиента.
Команда @ZeroPortCounters очищает все теги со счетчиками ошибок. Эту команду можно дать при старте, после инициализации всех таблиц портов.
Команда @Port port prot decl - таблица логических портов.
Логический порт - это просто номер (индекс в таблице физических портов) для последующего подключения к нему устройств. Каждый логический порт имеет номер port, протокол prot и определение decl. В данной версии номера логических портов port занимают диапазон (1..16). Протокол prot может принимать значения (IP, RTU, ASCII). Определение decl может содержать писание порта TCP или COM, как описано в справке по функции pipe_init() (при этом TCP подключение должно быть клиентским).
Например:
@port 1 IP tcp port 502 client localhost
- логический порт №1 работает по протоколу IP, использует TCP порт №502 (это стандартный порт MODBUS)
- клиент соединяется с сервером localhost.
@port 2 RTU com port 4 baudrate 9600 parity even databits 8 stopbits 1
- логический порт №2 работает по протоколу RTU, использует COM порт №4, скорость 9600 бит/сек,
- контроль четности EVEN, 8 бит данных, 1 стоповый бит.Допускается любая комбинация типов портов и протоколов, например "RTU over TCP/IP".
Командой @PortErrorCounters port Rx Tx Ex можно задать теги счетчиков ошибок данного порта.
Командой @PortErrorRates port Rx Tx Ex можно задать теги скорости счета ошибок данного порта.
Здесь
port - номер порта.
Rx - имя тега для счетчика ошибок приемника, когда приходят запросы с неверным адресом устройства, неверной контрольной суммой и т.д.
Tx - имя тега для счетчика ошибок передатчика, когда не удается отправить ответ на запрос.
Ex - имя тега для счетчика ошибок - исключений MODBUS, когда в запросе указан неверный адрес статусного или регистрового входа-выхода.
Аналогично задаются теги для подсчета траффика:
Командой @PortPollCounters port Rx Tx можно задать теги счетчиков запросов данного порта.
Командой @PortPollRates port Rx Tx можно задать теги скорости поступления запросов данного порта.
Командой @PortByteCounters port Rx Tx можно задать теги счетчиков потока байтов данного порта.
Командой @PortByteRates port Rx Tx можно задать теги скорости счета потока байтов данного порта.
Теги счетчиков могут иметь тип Integer или Real (лучше Real). При необходимости общего счетчика допустимо многократно ссылаться на один и тот же тег, в этом случае счета из разных источников будут складываться.
Использование тегов счетчиков является необязательным. Если счетчики не нужны, их можно не описывать.
В случае, если теги ошибок не указаны, будут инкрементироваться стандартные счетчики ошибок, общие для всех портов. Эти стандартные ошибки влияют на статус DAQ-системы (улыбающийся человечек пожелтеет и покраснеет). В случае, когда теги ошибок указаны, они будут инкрементироваться при ошибках на данном порте, но при этом стандартные счетчики задействованы не будут (поэтому статус DAQ-системы при ошибках не изменится). Это позволяет делать индивидуальную обработку ошибок, по выбору прикладного программиста.
Команда @View port служит для отображения (в консоли) таблицы данных для данного порта. Если порт не указан, то в консоли отображается таблица всех портов. Эта команда применяется для контроля задания таблиц при отладке.
Команды @ModbusPoll, @ModScan32, @ModbusHelp служат для отладочных и справочных целей.
Краткое описание команд в секции StartupScript:
@Reset
@View
@View port
@Port port prot decl
@Node node port neta
@PortErrorRates port Rx Tx Ex
@PortErrorCounters port Rx Tx Ex
@PortPollRates port Rx Tx
@PortPollCounters port Rx Tx
@PortByteRates port Rx Tx
@PortByteCounters port Rx Tx
@CheckOpt port opt
@TimeGap port gap
где
port - логический порт, 1..16
prot - протокол IP,RTU,ASCII
decl - описание порта: (tcp port 502 client localhost),(com port 1 baudrate 9600 parity even databits 8 stopbits 1)
Rx - имя тега (integer,real) для накопления счетчиков приема/передачи/ошибок приемника
Tx - имя тега (integer,real) для накопления счетчиков приема/передачи/ошибок передатчика
Ex - имя тега (integer,real) для накопления счетчиков приема/передачи/ошибок - исключений
opt - опции проверки протокола, строка LPTUF, сокращение от Length,Protocol,Transaction,Unit,Function
влияет на строгость проверки данных при анализе сообщений, например, надо ли считать ошибкой
неверное значение поля Protocol (некотрые PLC неверно его задают)
символ * задает значение опции по умолчанию
gap - временной зазор (пауза) между опросами, мс
пауза необходима для разделения сообщений в Modbus RTU,
для других протоколов (IP,ASCII) может отсутствовать (=0)
Например:
[&ModbusProxy.StartupScript]
;--- Задание таблицы портов (порт,протокол,описание)
;--- Задание счетчиков приема/передачи/ошибок
;--- Логический порт 1
@Port 1 ip tcp port 502 client localhost
@PortByteCounters 1 BYTECOUNT.RX BYTECOUNT.TX
@PortPollCounters 1 POLLCOUNT.RX POLLCOUNT.TX
@PortErrorCounters 1 BUGSCOUNT.RX BUGSCOUNT.TX BUGSCOUNT.EX
@PortByteRates 1 BYTERATE.RX BYTERATE.TX
@PortPollRates 1 POLLRATE.RX POLLRATE.TX
@PortErrorRates 1 BUGSRATE.RX BUGSRATE.TX BUGSRATE.EX
@CheckOpt 1 LPTUF
@TimeGap 1 0
;--- Логический порт 2
@Port 2 rtu com port 4 baudrate 9600 parity even databits 8 stopbits 1
@PortByteCounters 2 BYTECOUNT.RX BYTECOUNT.TX
@PortPollCounters 2 POLLCOUNT.RX POLLCOUNT.TX
@PortErrorCounters 2 BUGSCOUNT.RX BUGSCOUNT.TX BUGSCOUNT.EX
@PortByteRates 2 BYTERATE.RX BYTERATE.TX
@PortPollRates 2 POLLRATE.RX POLLRATE.TX
@PortErrorRates 2 BUGSRATE.RX BUGSRATE.TX BUGSRATE.EX
@CheckOpt 2 *
@TimeGap 2 10
;--- Очистка счетчиков ошибок
@ZeroPortCounters
;--- Просмотр состояния
@View
[]Работа клиента &ModbusProxy происходит в пассивном режиме ожидания запросов со стороны подчиненных драйверов. Предполагается, что драйверы периодически посылают сообщение @Modbus.Poll ... и ждут ответа. При этом драйверы формируют и интерпретируют только данные протокольного уровня PDU (Protocol Data Unit), не заботясь об их передаче и приеме по протоколам канального уровня (IP,RTU,ASCII).
Клиент &ModbusProxy самостоятельно переводит данные PDU к нужному для приемо-передачи формату ADU (Application Data Unit), добавляя требуемые заголовки и контрольные суммы.
Для отказоустойчивости драйверы должны периодически (скажем, раз в минуту) проверять наличие ответа на посланный запрос и генерировать новый запрос, если ответ все еще не пришел (эта ситуация возможна при перезапуске клиента &ModbusProxy).
Команда @Modbus.Poll ref cid tot port uid fid $$dat служит для инициирования операции опроса данных для данного порта.
@Modbus.Poll ref cid tot port uid fid $$dat
ref - reference, числовая ссылка или имя драйвера, инициировавшего опрос. Ответное сообщение будут послано ему.
cid - command id, пользовательская команда - любое число, идентифицирующее команду, которая возвращается в ответе
tot - максимальное время ожидания ответа, ms, (1..MaxInt). Время ожидания должно быть положительным числом.
port - номер логического порта, (1..16). Порт должен быть открыт командой @Port.
uid - идентификатор (адрес) устройства, (1..247).
fid - функция MODBUS, (1,2,3,4,5,6,15,16).
dat - данные запроса (request) в формате HEX_ENCODE, зависящие от функции fid.
например:
@Modbus.Poll &demo.driver 33 200 1 5 3 $$00090006
Инициировать опрос модуля с адресом 5, подключенного к порту 1,
ждать не более 200 ms, ответ переслать драйверу &demo.driver.
При опросе вызывается MODBUS функция 3 (чтение регистров), читать
6 регистров начиная с адреса 9 (адресация начинается с нуля).
В запросе передается номер команды 33 для использования драйвером.В результате выполнения команды @Modbus.Poll в ответ устройству &sender посылается одно из следующих сообщений: @Modbus.Refuse (опрос отклонен), @Modbus.Reply (опрос выполнен, получен ответ), @Modbus.TimeOut (время ожидания истекло).
@Modbus.Refuse ref cid tim port uid fid msg
@Modbus.Reply ref cid tim port uid fid $$ans
@Modbus.TimeOut ref cid tim port uid fid $$dat
ref - reference, имя или ссылка устройства, пославшего ответ; значение должно быть ссылкой &ModbusProxy
cid - command id, идентификатор команды; значение должно совпадать с посланным
tim - измеренное время выполнения цикла запрос-ответ, ms; значение должно быть неотрицательным
port - логический порт, значение должно быть неотрицательным и совпадать с посланным
uid - адрес устройства, значение должно быть (1..247) и совпадать с посланным
fid - номер функции; значение должно совпадать с посланным, но возможно, с установленным флагом ошибки (128)
ans - ответ устройства в формате HEX_ENCODE, возвращаемый при Reply
req - исходный запрос в формате HEX_ENCODE, возвращаемый при TimeOut
msg - строка с описанием причины, по которой запрос был отклонен - например, "Bad Func"= неверный номер функции
например:
@Modbus.Reply 1049090 33 10 1 5 3 $$0C4148890D00004159BFB90000
@Modbus.TimeOut 1049090 33 210 1 5 3 $$00090006
@Modbus.Refuse &ModbusProxy 33 0 1 5 3 Bad Port 0
здесь 1049090 - ссылка устройства &ModbusProxyДанные req для передачи и ответ ans передаются в формате HEX_ENCODE и интерпретируются в соответствии с описанием протокола MODBUS.
Для использования MODBUS через COM порт пользователь должен иметь разрешение на доступ к COM портам.
Для этого пользователя надо включить в группу dialout:
groups # получить список групп для пользователя
id -Gn # получить список групп для пользователя
sudo usermod -a -G dialout $USER # включить пользователя в группу dialoutВ случае использования MODBUS over TCP обычно используется порт TCP 502.
Проблема состоит в том, что по умолчанию порты 0-1023 недоступны для обычных (не root) пользователей.
В этом случае предлагается сделать следующее:
sudo iptables -A INPUT -p tcp --dport 502 -j ACCEPT # разрешить входящий tcp:502
sudo iptables -A OUTPUT -p tcp --dport 502 -j ACCEPT # разрешить исходящий tcp:502
sudo iptables -A FORWARD -p tcp --dport 502 -j ACCEPT # разрешить проходной tcp:502sudo setcap cap_net_bind_service=ep $(readlink -f /opt/crwdaq/crwdaq)sudo iptables -L # посмотреть таблицу правил IPTABLES
sudo /sbin/iptables-save # сохранить таблицу правил IPTABLES для автозагрузки
ss -tuln # посмотреть список программ, слушающих (LISTEN) TCP портыПосле проведения этих настроек программа /opt/crwdaq/crwdaq должна получить постоянные права на работу с портом TCP 502 для MODBUS.
Желаем успешного использования &ModbusProxy.
CRW-DAQ Copyright (c) 2001-2024 Alexey Kuryakin daqgroup@mail.ru.