Продолжаем расширять мой базовый курс работы с системой WinCC OA. На этот раз речь пойдет о драйвере Modbus TCP. Очень хорошо эта задача уже описана у Вячеслава Лапшина на его сайте «Быстрые проекты». Тем не менее, ряд деталей у него в описании был опущен, так что создам свою версию гайда. Это уже вторая версия документа, в первой были допущены досадные оплошности.
В качестве сервера (подчиненного устройства) протокола Modbus у меня выступает свободнопрограммируемый ПЛК.
Я традиционно работаю в англоязычной среде, не переключаясь на русский.
В случае неясностей рекомендую обратиться к «номерным» частям базового курса.
В первую очередь создаем пустой проект с «классическими» настройками безопасности и выбранным архивированием NextGen. Все шаги создания проекта не описываю, это уже очевидные вещи. Рутовый пароль для учебного проекта не задаю.


Запускаем созданный проект и переходим в консоль. Добавляем драйвер Modbus (именно драйвер модбаса, а не сервер модбаса!). При добавлении опцией «-num 10» задаем ему номер 10. Номер драйвера может быть любым, единственное условие - все драйверы в системе, вне зависимости от типа, должны иметь уникальный номер.


Убеждаемся, что драйвер запустился, а он запустился, так как его статус в консоли = 2, а цвет статуса - зеленый.

В рассматриваемом случае после запуска драйвера в системном журнале сразу появятся ошибки:
WARNING, 54, Unexpected state, Cannot get DPID for _Driver10.AL.AClasses:_original.._value
WARNING, 54, Unexpected state, Cannot get DPID for _Driver10.AL.AckDps:_original.._value
WARNING, 54, Unexpected state, Cannot get DPID for _Driver10.AL.AckData:_original.._value
В первую очередь обращаем внимание на ошибку вида «не найден идентификатор точки данных» и имя точки _Driver10. Связано это с тем, что все настройки обмена данными хранятся во внутренних (системных) точках данных, коих в прикладном проекте не счесть. В любой системе количество предопределенных точек данных ограничено, как правило, тремя точками. А мы только что потребовали обращаться к несуществующей точке для десятого драйвера. Посмотрим в модуле Para:

Так и есть, заданы точки данных для драйверов 1..3, но никак не 10. Добавляем две точки _Driver10 и _Driver10_2. Подчеркивание в имени точки данных является своего рода признаком «невидимости» точки данных, как обычная точке в начале имени файл юникс-систем. Создается именно две точки - одна для активной системы, другая для резервированной, даже, если мы работаем без резервирования серверов, это признак хорошего тона. Получаем в итоге

Получаем новые ошибки в журнале:
DP does not exist, _Stat_Configs_driver_10._address:_original.._value
DP does not exist, _Stat_Configs_driver_10._cmd_conv:_original.._value
DP does not exist, _Stat_Configs_driver_10._msg_conv:_original.._value
DP does not exist, _Stat_Configs_driver_10._smooth:_original.._value
Добавляем еще одну точку данных, уже другого типа и получаем.

Более ошибок в журнале не появляется. Это относится к любому драйверу. Как правило созданы внутренние точки данных только для первых трех номеров драйвера, остальные необходимо создавать самостоятельно. Некоторые типы драйверов требуют и других новых точек данных.
Прошу обратить внимание, что добавление секции mod_XX в конфиг-файл проекта является теперь опциональным, а не обязательным.
Далее переходим в System Management и ищем настройки драйвера модбаса.



Нажимаем кнопку Create и добавляем в список Mod_Plc_10. Ошибочно можно предположить, что 10 - это номер драйвера. Нет, 10 - это просто 10. Это внутреннее «имя» соединения, не номер драйвера и не device ID устройства Modbus. В данном случае номер драйвера и "имя" соединения совпадают, но это необязательно.

Добавляем ip-адрес нашего сервера modbus TCP, ставим галочку Active. Вот так теперь выглядят настройки.

Напоминаю, что Unit Address в полноценной реализации протокола Modbus TCP используется только для гейтов TCP - RTU. Нормальные сервера Modbus TCP этот параметр игнорируют. Но встречаются и обратные случаи, и сервер, не являясь гейтом, все равно контролирует свой Unit ID. Поскольку в реализации протокола модбас каждый сходит с ума по-своему, заранее предугадать поведение подчиненного устройства затруднительно. В нашем же случае сервер Modbus TCP реализован правильно и Unit ID игнорирует. Если вы все делаете правильно, но соединения так и не происходит, то задавайте значение Unit address. Узнать его можно в подчиненном устройстве Modbus TCP.
Нажимаем кнопку Apply. Если вы ожидаете, что после нажатия состояние вместо Not Connected станет Connecting или Connected, то зря надеетесь. Состояние соедине��ия изменится только после добавления первого конфига _address на какую-либо точку данных, для чего традиционно открываем модуль para. Я применяю предопределенный DPT ExampleDP_Int и создаю первую DP с названием MyInt.

Добавляем конфиг _address и начинаем его настройку



Работать будем с регистрами хранения. Самый первый регистр в моем ПЛК - это шестнадцатиразрядный знаковый Int, который инкрементируется на 1 каждую секунду в программе ПЛК. Соответственно, тип преобразования указываем = Int16. Не забываем добавить группу опроса.

В группе опроса не забывает активировать группу, а то обмен не заработает.
Напоследок делаем адрес активным (ставим галочку).

Переходим на конфиг _original.

Читаемое раз в секунду значение соответствует значению в ПЛК. Этот опыт удался. Кстати, после добавления первого сигнала в окне драйвера появляется надпись Connected.

Следующие два регистра в моем случае - это 32разрядный Int. Заведем еще одну точки данных с именем MyDInt в этом же типе и настроим ее.

Тип преобразования - уже Int32, это два регистра. Задаем смещение - 1 (нулевой регистр - это MyInt, первый и второй - MyDInt). Группа опроса остается той же. Переходим на конфиг original и наблюдаем недостоверные данные.

Значение сигнала тут то ли несколько миллионов, то ли несколько миллиардов, что радикально отличается от значения в ПЛК = 5440 (для этого DInt так же запрограммирован ежесекундный инкремент). Идем смотреть в настройки драйвера. Один регистр Modbus - это 16 бит, два байта. Два регистра - это 32 бита, 4 байта или двойное слово. А порядок байт в слове у нас заранее может быть и неизвестен. Смотрим на закладку продвинутых настроек и находим порядок регистров. Что выставлено по умолчанию - я не знаю. Но на всякий случай попробуем порядок Big endian, как у интелловских процов.


Не забываем нажимать Apply. После этого изменения значение MyDInt стало соответствовать значению в ПЛК. Разумеется, пока я пишу эту заметку, идет время, и сейчас значение сигнала уже не 5440,а немного больше.

На всякий случай смотрим MyInt, не сломалось ли там что-нибудь.

Не сломалось, что вполне ожидаемо. Следующим переметром давайте заведем переменную типа Real (так же, два регистра). На стороне контроллера это значение уже не задается принудительно, так что пропишем и чтение, и запись. Создаем и настраиваем точку данных типа ExampleDP_Float

Заданное на стороне ПЛК значение считалось

Добавим к этой DP конфиг _smooth в режиме сравнения старое/новое. В противном случае будет проблематично вбить новое значение в модуле Para, ибо оно будет перезатираться каждую секунду.

Меняем значение в модуле Para и лезем проверять контроллер. Все работает.


И напоследок решил проверить - можно ли задать DPE типа bool и читать его, как бит регистра хранения. Как оказалось, можно.

Пятый регистр- это слово MyWord в ПЛК. Указываю номер бита = 1 (нумерация в этом случае с единицы). Пробую читать/записывать значение, все получается.


Шестой регистр в ПЛК у меня представлен одним битом. Обращаю внимание, что конкретно в этом случае я должен выставить номер бита не 1, а 9, так как идет переворачивание байт в слове, и это никак не победить. Это связано в первую очередь с выравниванием информации в блоках данных Симатиков по слову в случае стандартного доступа.

