понедельник, 3 июня 2024 г.

Owen Codesys: опрос сигналов по ModBus и передача их по UDP

Попытался в заголовке изложить полную суть проблемы, чтобы было понятно что же требуется. Ну и чтобы решить общую задачу, разбиваем ее на несколько подзадач: сначала принимаем сигналы, потом их отправляем.Для того чтобы в Codesys настроить прием сигналов по протоколу Модбас, нужно настроить ПЛК Овен на режим Модбас Мастер:

Все это делалось по инструкции с сайта Овен, там очень хорошо построен сайт - есть все драйвера, софт, прошивки, вспомогательные библиотеки, примеры проектов, видео инструкции, обычные инструкции - в общем очень и очень хорошо поставлена работа, за что Овену большой плюс. Вот конкретная ссылка на видео, на основании, которой делалась эта конфигурация: https://www.youtube.com/watch?v=77fdfnfdJME Если коротко описать словами, то нужно во вкладке ресурсы нужно открыть "Конфигурация ПЛК" и там через контекстное меню добавить элемент ModBus Master. В нем нужно настроить порт (RS485) и другие параметры. Потом уже к мастеру добавляем опрашиваемое устройство: Universal Modbus Device, которое тоже параметрируем так как надо: главное выставить его адрес и скорость обмена. И вот уже потом добавляется Подэлемент - Теги которые опрашиваются у устройства, в зависимости от их типа данных, можно выбрать разные опции. У каждого тега настраивается его адрес. И вот тут есть небольшой нюанс, который и побудил написать эту заметку. Как видно на скрине в качестве опрашиваемого типа используется String Input module и это не просто так.

Дело в том, что с самого начала при добавлении тегов по той видео инструкции, я обратил внимание что каждый новый тег визуально замедляет обмен с устройством- в онлайн режиме данные при опросе одного тега меняются очень и очень быстро, а уже при 4 визуально видно что замедляется, при 20 это видно очень и очень хорошо. При этом обмен с OPC сервером был довольно быстрый, но зато там я обратил внимание на опцию - "опрашивать каждый тег по-отдельности". Когда включил эту опцию, то скорость визуально стала в несколько раз меньше. Это навело меня на мысль, что ПЛК опрашивает теги не в пакетном режиме, а каждый по-отдельности, и нужно было как-то эту проблему решить. На форуме нашлось решение такое, что  посоветовали выбирать Строковую переменную, а в ней можно выбрать количество опрашиваемых символов (байт). Таким образом вся строка (несколько подряд идущих тегов) опрашивается одним пакетом, и теперь только требуется правильно разделить принятые данные. 

Мне требовалось опросить 8 input регистров (код операции 0х04) типа real, которые идут подряд и начинаются с адреса 33000. Для того чтобы опросить их одним пакетом, я добавил опрос строки с адреса 33000 длиной 32 байт (8 шт * 4 байта в Real). Теперь данные опрашивались с такой же скоростью, как и скорость опроса одного тега (по сути один тег и есть), но в ответ приходила строка с кучей непонятных символов. Оказалось что в памяти ПЛК можно выделить область памяти куда эти данные можно поместить в виде массива (байт, или WORD, или Real как в моем случае), и делается это в разделе регистрации глобальных переменных, конструкцией AT %IB9.1.0.0 (как ссылка на область памяти зарезервированной для устройства, то есть как я понимаю массив байт с устройства копируется в новый массив, но зато с ним проще работать):

RTU1_R AT %IB9.1.0.0: ARRAY [0..7] OF REAL;

То есть как видно, создается массив из 8 элементов типа REAL. Но теперь выявилась другая проблема: эти данные в контроллере стали отображаться неверно - поменяны местами первое и второе слово, и чтобы вернуть им нормальный вид, пришлось снова искать решение. Готовых библиотек в составе пакета Codesys для такого не было, нужно было добавлять их, но зато оказалось что написать функцию стандартными средствами (через указатели), которая будет делать SWAP (менять слова в составе Real переменной местами) не так-то и сложно (подсмотрел на форуме):

FUNCTION SWAP_R : REAL
VAR_INPUT
    Input:REAL;
END_VAR
VAR
    pInput:POINTER TO ARRAY [0..3] OF BYTE;
    pResult:POINTER TO ARRAY [0..3] OF BYTE;
END_VAR

pInput:=ADR(Input);
pResult:=ADR(SWAP_R);
pResult^[0]:=pInput^[2];
pResult^[1]:=pInput^[3];
pResult^[2]:=pInput^[0];
pResult^[3]:=pInput^[1];

 

Как видно сначала должны идти 2 и 3 байты, а потом 0 и 1. Знак ^  означает что нужно брать контент расположенный по адресу указателя. Теперь данные имели понятный вид и их можно передавать по UDP.

Чтобы передать по UDP на сайте Овен также нашел пример проекта: оттуда тупо копипастой скопировал код в блок UDP_FC, который вызывается из основной программы. Чтобы код заработал, нужно было импортировать библиотеку syslibsockets.lib которая тоже нашлась на сайте Овен

Как видно из названий FC блоков на скрине, UDP понадобился для того чтобы передавать данные на сервер IBA. Чтобы упаковать все полученные данные с устройств ModBus (в данном примере пока только 2 устройства с 2 наборами тегов: Real и DWORD) в телеграмму для передачи по UDP, бал добавлен функциональный блок с таким содержимым:

sendArray[0]:= QW1_mobbus;
(*MODBUS #1*)
sendArray[1]:= SWAP_R(RTU1_R[0]);
...
sendArray[8]:= SWAP_R(RTU1_R[7]);
sendArray[9]:= RTU1_D[0];
sendArray[10]:= RTU1_D[1];
...
sendArray[18]:= RTU1_D[9];
(*MODBUS #2*)
sendArray[19]:= SWAP_R(RTU2_R[0]);
...
sendArray[26]:= SWAP_R(RTU2_R[7]);
sendArray[27]:= RTU2_D[0];
...
sendArray[36]:= RTU2_D[9];

Как видим Real значения передаются через функцию SWAP, а DWORD в том виде, какой есть (там порядок байт прямой).

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

Комментариев нет:

Отправить комментарий