Русский
Русский
English
Статистика
Реклама

Simatic

Мобильная Установка Доказательства Актуальности Контроля Изменений. Часть 1. Хороший человек идет на войну

28.12.2020 06:15:07 | Автор: admin

Итак, надоело! Даже, не так, ДОСТАЛО! А точнее, за.... Впрочем, не буду прибегать к ненормативной лексике. Квартира, купленная 2.5 года назад - это не квартира, а какое-то вечное испытание. Да такое, что Форт Баярд отдыхает. Кроме того, в конце игры Баярда можно нехило поднять золотишка, а чем тут все закончится - я еще не знаю. Недостаточная (ниже норм, утвержденных Правительством РФ, а эти нормы, скажем так, разрабатывались моржами) температура воздуха в помещении, отсутствие горячей воды по утрам, а ежедневное принятие душа превращается в соревнование "кто быстрей", ибо вода неожиданно может стать кипятком. Или, наоборот, льдом. Бездействие управляющей компании, сбор инициативной группы, объявления по подъездам, коллективная жалоба... это и многие другие веселые подробности останутся за рамками повествования.

Итак, если какой-то параметр, будь то температура или давление, не устраивает, то этот параметр надо для начала измерить. Желательно, в автоматическом режиме, непрерывно и с сохранением всех значений. И тут на помощь приходят средства промышленной автоматизации - датчик давления, датчик температуры, и программируемый логический контроллер с модулем аналоговых входов и поддержкой протокола HART. Почему именно они? Во-первых, я АСУшник, а не электронщик. Во-вторых, я исполняю обязанности руководителя технической группы Управления "Цифровое производство" уральского филиала компании ООО "Сименс". А, стало быть, все эти игрушки у меня есть. Этого оказалось вполне достаточно, чтобы продумать, собрать на коленке и запрограммировать систему, которой я дал замысловатое название Мобильная Установка Доказательства Актуальности Контроля Измерений (весь смысл замысловатости заключается в получившейся аббревиатуре... настолько я огорчен сложившейся ситуацией).

В качестве датчика, измеряющего давление, выступает Siemens Sitrans P DS3 с диапазоном измерения от 0 до 16 бар, выходом 4..20 мА и, что немаловажно, поддержкой цифрового протокола HART. HART означает Highway Addressable Remote Transducer. По сути, это цифровой протокол обмена данными между системой управления и датчиком. Обмен данными двухсторонний. Для кодирования нулей и единичек цифрового обмена используется частотная модуляция, которая накладывается прямо поверх аналогового сигнала 4..20 мА. Поддержка HART сильно играет мне на руку, поскольку позволяет избежать дополнительного аналого-цифрового преобразования на стороне ПЛК и, скажем откровенно, "забить" на "землю" (боюсь, что после этих слов мне коллеги руки не подадут). На фотографии этот красавец уже подключен к домашнему водопроводу.

Датчик температуры - тоже Siemens, тоже с HART'ом. Вторичка - Sitrans TH300, первичка - обычный Pt100 в толстой гильзе. Именно из-за гильзы он не подошел для замеров температуры воздуха - просто огромная инерция измерений.

В качестве CPU применяется S7-1510 (он же ET200SP CPU), заказной номер 6ES7 510-1DJ01-0AB0, версия прошивки 2.8. К CPU справа подключен четырехканальный модуль аналоговых входов с поддержкой HART, заказной номер 6ES7 134-6TD00-0CA1. Блок питания слева от контроллера, весьма большой и очень избыточный по мощности в моем случае.

Для визуализации измеряемых параметров и, что немаловажно, для их архивации взял базовую операторскую панель второго поколения, KTP400 Basic, 6AV2 123-2DB03-0AX0, прошивка обновлена до 16.0. Причина для такого выбора простая - мобильных панелей у меня под рукой нет, а "немобильные", стандартные версии предназначены для врезки в дверь шкафа управления, но никак не для того, чтобы держать их в руках. Маленькую четырехдюймовую панель на весу удержать можно без проблем, а вот семидюймовую Comfort или Unified одной рукой не удержишь. Разбивать подотчетное оборудование и попадать на 1200 евро её листовой стоимости не хочется никак, поэтому лучше маленькую и легкую, чем большую, тяжелую, но функциональную.

Для обвязки применяется обычный провод сечением 0.5 мм2 и НШВИ 0.5х8. Техническая эстетика не соблюдается никак. Корпуса нет. Заземляющих проводников нет. Все обвязано, абы как. Прикладное ПО далеко не универсально, уставок от оператора нет, даже константы не применяются, все тупо в лоб. В общем, это ни в коем случае не готовое решение, которое можно сертифицировать и вносить в реестр СИ. Я напишу про это тут один раз, дабы избежать войн в комментариях, да, со всеми этими замечаниями я согласен.

Датчики подключаем к каналам 0 и 1 аналогового модуля в соответствии с документацией.

У нас используется вариант (1) - двухпроводное подключение датчика. Под "двухпроводностью" подразумевается, что к датчику в общем случае идет всего два провода, "+" и "-" непосредственно от аналогового модуля. Отдельного питания на датчик не требуется, он запитывается от модуля AI. Электроника модуля получает энергию по внутренней шине контроллера, а "силовая" часть подается на отдельные клеммы. В итоге, первый датчик (датчик давления) садим на контакты 9 и 13 (нулевой канал), датчик температуры - 10 и 14 (первый канал).

Для программирования и конфигурирования этой системы применяем среду разработки TIA Portal V16 (+update 3), в составе Step 7 Professional (контроллер) и WinCC Basic (операторская панель). После создания проекта в первую очередь создаем и конфигурируем ПЛК.

Включаем System and Clock Memory, пригодится.

Задаем IP адрес CPU - 192.168.43.205. Конфигурируем аналоговый модуль. Второй и третий каналы отключаем, на нулевом и первом включаем HART и активируем всю диагностику (диагностика совсем необязательна в моем простом случае).

Обзор настройки каналов модуляОбзор настройки каналов модуляНастройка нулевого (и первого тоже) каналаНастройка нулевого (и первого тоже) канала

Поскольку в данном случае задействовано всего 2 канала, а от функционала HART требуется всего лишь передача значения измеряемой величины, то можно поступить просто и забирать первичную переменную HART обоих датчиков через пространство ввода/вывода аналогового модуля. Тем самым избавляемся от необходимости осуществлять системный вызов в программе PLC.

Из этой таблицы видно, что нам надо забрать две переменные с адресов ID8 и ID13. Первичные переменные HART будут поступать нам, как готовые вещественные величины. Пропишем эти адреса в Tag Table. Так же я еще прописал все четыре аналоговых канала модуля, но в программе они никак не используются... просто привычка объявлять переменные по каналам, которые есть в проекте.

Посмотрим, что я начудил в программе контроллера. Вообще, в ПЛК можно обойтись без какой-либо программы вообще. Информация с датчиков и так автоматически поступает в переменные с именами CH0_HART (давление) и CH1_HART (температура). Однако, установка изначально разрабатывалась, как универсальная - она замеряет и Т воздуха, и Т воды, и давление воды, да еще и в нескольких местах (воздух в разных комнатах, например). Если обойтись одним трендом, то можно уже через день запутаться, что, где и когда измерял. Поэтому программа контроллеру тут носит больше "организационный" характер.

Все необходимые переменные сведены в один блок данных.

Вся программа написана в "главном" огранизационном блоке. Расписывать ее нет необходимости, можно ознакомиться с комментариями в коде.

"Data".CurrentPress := "Ch0_HART" / 10.0; //давление перевести из бар в МПа"Data".CurrentTemp := "Ch1_HART"; //текущая температураIF "Data".MeasP THEN //если включен (с панели) замер давления        //допускается всего 4 места измерения давления - в ванной (ХВС, ГВС) и на кухне    IF "Data".WaterSelector <= 0 OR "Data".WaterSelector > 4 THEN         "Data".WaterSelector := 1;    END_IF;        //создать для панели сообщение "идет замер давления в/на <место замера и выбранная труба">    CASE "Data".WaterSelector OF        1: //ХВС в ванной            "Data".WaterMessage := W#2#0000000000000001;        2: //ГВС в ванной            "Data".WaterMessage := W#2#0000000000000010;        3: //ХВС на кузне            "Data".WaterMessage := W#2#0000000000000100;        4: //ГВС на кухне            "Data".WaterMessage := W#2#0000000000001000;    END_CASE;        //тнекщее давление в МПа скопировать в отдельную переменную для тренда    "Data".CurrentPress_M := "Data".CurrentPress;        //если замер только что включили, сбросить минимальное и максимальное зафиксированное значение    IF (NOT "Data".MeasP_prv) THEN        "Data".MinPress_M := "Data".CurrentPress_M;        "Data".MaxPress_M := "Data".CurrentPress_M;    END_IF;        //зафиксировать при необходимости минимальное значение    IF "Data".CurrentPress_M < "Data".MinPress_M THEN        "Data".MinPress_M := "Data".CurrentPress_M;    END_IF;        //зафиксировать максимальное значение давления во время этого замера    IF "Data".CurrentPress_M > "Data".MaxPress_M THEN        "Data".MaxPress_M := "Data".CurrentPress_M;    END_IF;        //выставить аварийные и предупредительные сообщения    "Data".P003Alarm_M := "Data".CurrentPress_M < 0.03;    "Data".P01Alarm_M := "Data".CurrentPress_M < 0.1;    "Data".P02Alarm_M := "Data".CurrentPress_M < 0.2;ELSE //если замер давления не ведется    "Data".CurrentPress_M := 0.0; //обнулить переменную для тренад давления    "Data".WaterMessage := W#2#0000000000000000; //сбросить "место проведения давления"    "Data".P003Alarm_M := FALSE; //сбросить флаги алармов и сообщений    "Data".P01Alarm_M := FALSE;;    "Data".P02Alarm_M := FALSE;;END_IF;IF "Data".MeasTair THEN //аналогично для температуры воздуха        IF "Data".PlaceSelector <= 0 OR "Data".PlaceSelector > 7 THEN        "Data".PlaceSelector := 7;    END_IF;        CASE "Data".PlaceSelector OF        1: //спальня            "Data".PlaceMessage := W#2#0000000000000001;        2: //детская            "Data".PlaceMessage := W#2#0000000000000010;        3: //зал            "Data".PlaceMessage := W#2#0000000000000100;        4: //кухня            "Data".PlaceMessage := W#2#0000000000001000;        5: //ванная            "Data".PlaceMessage := W#2#0000000000010000;        6: //туалет            "Data".PlaceMessage := W#2#0000000000100000;        7: //прочее            "Data".PlaceMessage := W#2#0000000001000000;    END_CASE;        "Data".CurrentTemp_Mair := "Data".CurrentTemp;        IF (NOT "Data".MeasTair_prv) THEN        "Data".MinTemp_Mair := "Data".CurrentTemp_Mair;        "Data".MaxTemp_Mair := "Data".CurrentTemp_Mair;        "Data".MeasTwater := false;    END_IF;        IF "Data".CurrentTemp_Mair < "Data".MinTemp_Mair THEN        "Data".MinTemp_Mair := "Data".CurrentTemp_Mair;    END_IF;        IF "Data".CurrentTemp_Mair > "Data".MaxTemp_Mair THEN        "Data".MaxTemp_Mair := "Data".CurrentTemp_Mair;    END_IF;        "Data".T18Alarm_M := "Data".CurrentTemp < 18;    "Data".T20Alarm_M := "Data".CurrentTemp < 20;    "Data".T22Alarm_M := "Data".CurrentTemp < 22;ELSE    "Data".CurrentTemp_Mair := 0.0;    "Data".PlaceMessage := W#2#0000000000000000;    "Data".T18Alarm_M := FALSE;    "Data".T20Alarm_M := FALSE;    "Data".T22Alarm_M := FALSE;END_IF;//аналогично для температуры водыIF "Data".MeasTwater THEN    "Data".CurrentTemp_Mwater := "Data".CurrentTemp;        IF (NOT "Data".MeasTwater_prv) THEN        "Data".MinTemp_Mwater := "Data".CurrentTemp_Mwater;        "Data".MaxTemp_Mwater := "Data".CurrentTemp_Mwater;        "Data".MeasTair := false;    END_IF;        IF "Data".CurrentTemp_Mwater < "Data".MinTemp_Mwater THEN        "Data".MinTemp_Mwater := "Data".CurrentTemp_Mwater;    END_IF;        IF "Data".CurrentTemp_Mwater > "Data".MaxTemp_Mwater THEN        "Data".MaxTemp_Mwater := "Data".CurrentTemp_Mwater;    END_IF;        "Data".T40Alarm_M := "Data".CurrentTemp < 40;    "Data".T55Alarm_M := "Data".CurrentTemp < 55;    "Data".T57Alarm_M := "Data".CurrentTemp < 57;    "Data".T60Alarm_M := "Data".CurrentTemp < 60;ELSE    "Data".CurrentTemp_Mwater := 0.0;    "Data".T40Alarm_M := FALSE;    "Data".T55Alarm_M := FALSE;    "Data".T57Alarm_M := FALSE;    "Data".T60Alarm_M := FALSE;END_IF;//при первом запуске контроллера сбросить зафисированные мин и макс "общих" (не замерных) величин"tonFirstScan".TOF(IN:=NOT "FirstScan",                   PT:=T#1s);IF "tonFirstScan".Q THEN    IF "Data".CurrentPress < "Data".MinPress THEN        "Data".MinPress := "Data".CurrentPress;    END_IF;        IF "Data".CurrentPress > "Data".MaxPress THEN        "Data".MaxPress := "Data".CurrentPress;    END_IF;        IF "Data".CurrentTemp < "Data".MinTemp THEN        "Data".MinTemp := "Data".CurrentTemp;    END_IF;        IF "Data".CurrentTemp > "Data".MaxTemp THEN        "Data".MaxTemp := "Data".CurrentTemp;    END_IF;END_IF;//сбросить мин и макс по запросу с панелиIF "Data".ResetPressStat THEN    "Data".MinPress := "Data".CurrentPress;    "Data".MaxPress := "Data".CurrentPress;    "Data".ResetPressStat := FALSE;END_IF;IF "Data".ResetTempStat THEN    "Data".MinTemp := "Data".CurrentTemp;    "Data".MaxTemp := "Data".CurrentTemp;    "Data".ResetTempStat := FALSE;END_IF;//выставить обобщенный флаг "идет замер""Data".Meas := "Data".MeasP OR "Data".MeasTair OR "Data".MeasTwater;//для определения фронтов"Data".MeasP_prv := "Data".MeasP;"Data".MeasTair_prv := "Data".MeasTair;"Data".MeasTwater_prv := "Data".MeasTwater;//сообщения для панели оператора"Data".Alarms.%X0 := "Data".MeasP;"Data".Alarms.%X1 := "Data".MeasTair;"Data".Alarms.%X2 := "Data".MeasTwater;"Data".Alarms.%X3 := "Data".T18Alarm_M;"Data".Alarms.%X4 := "Data".T20Alarm_M;"Data".Alarms.%X5 := "Data".T40Alarm_M;"Data".Alarms.%X6 := "Data".P003Alarm_M;"Data".Alarms.%X7 := "Data".P01Alarm_M;"Data".Alarms.%X8 := "Data".P02Alarm_M;"Data".Alarms.%X9 := "Data".T22Alarm_M;"Data".Alarms.%X10 := "Data".T55Alarm_M;"Data".Alarms.%X11 := "Data".T57Alarm_M;"Data".Alarms.%X12 := "Data".T60Alarm_M;

Перейдем к конфигурирования прикладного программного обеспечения операторской панели. Добавим ее в общий проект и зададим ip адрес.

Добавим некоторые настройки рантайма - выбор бита для списков и цвет алармов в зависимости от их класса.

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

Все тэги берем с блока данных 1 ПЛКВсе тэги берем с блока данных 1 ПЛК

Для тэгов Data_Alarms, DataWaterMessage и Data_PlaceMessage создадим сообщения.

Создадим Alarm Log для сообщений панели на максимальное количество записей, 500000. Весь лог будет храниться в текстовом виде на USB-флэшке, подключенной к панельке. Журнал циклический, по достижению предельного количества записей, старые будут затираться.

В настройках классов сообщений укажем, алармы каких классов будут сохраняться на фдэшке

Аналогично создадим хранилище для исторических трендов, которое будет так же храниться на флэшке в виде текстового файла. "Трендировать" будем три переменных - значение давление, температуры воздуха и температуры воды во время измерения (не на постоянной основе). Запись значений будет производиться только при изменении переменной, а не на постоянной основе. В противном случае журнал начнет весьма быстро перезатираться.

Создадим стартовый экран. Тут отображается мгновенное значение давления и температуры с датчиком, зафиксированные "экстремальные" значения, кнопки их сброса, информация о проводящемся замере и кнопка останова замеров.

Кнопкой F1 листаем экраны замеров. Остановися только на экране давления, два других аналогичны. На этом экране отображается текущее мгновенное значение давления при идущем замере, эксремумы давления, выбор места проведения измерения и график с историческими данными. Кнопкой F3 начинаем замер давления, кнопкой F4 останавливаем,

На любом экране кнопкой F2 открывается журнал событий.

Дополнительно для собственного удобство я сделал копию этой панели и изменил ее тип на PC Station с одиночной SCADA-системой WinCC RT Advanced. На 4дюймовом экране очень неудобно смотреть графики.

Зато, на 24 дюймах смотреть удобно

Установка находится в работе с 21 декабря. За это время мне стало изместно время пикового вечернего разбора воды, это в районе 22 часов местного времени. Ситуация удручающая, давление падает ниже установленных норм (0.03МПа) и падает влоть до нуля. Да и в целом давление не постоянное, оно может за секунду упасть с трех килограммов до одного, а потом за 5 секунд подняться до 2.5 килограмм.

В настоящий момент система полностью локальна. Все данные сохраняются в панельке, на фэшке переносятся на ПК и изучаются на нем.

Следующим шагом будет установка к линейку ПЛК WiFi модуля, настройка WiFi-моста и создание полноценного операторского интерфейса на базе WinCC OA под управлением Linux Debian (Buster).

Подробнее..

Промышленный интернет вещей в ПЛК Simatic S7-1x00 на примере протокола MQTT

13.01.2021 20:16:14 | Автор: admin

Обнаружил в базе знаний Siemens (SIOS) интересный пример использования контроллеров линейки S7-1200 и S7-1500 в качестве клиента протокола MQTT

Ссылка на первоисточник:https://support.industry.siemens.com/cs/document/109748872/fb-lmqtt_client-for-simatic-s7-cpu?dti=0&pnid=13685&lc=en-RU

По ссылке можно найти саму библиотеку с функциональным блоком MQTT_Client (с открытыми исходниками, кстати) и пояснительную записку к примеру. Документация сама по себе вполне исчерпывающая, знающие английский язык могут смело идти по ссылке и не читать эту заметку, подарив тем самым себе несколько увлекательных часов инженерных изысканий. А для остальных, менее опытных и/или более ленивых, я кратко расскажу основное и продемонстрирую свой пример работы клиента, творчески переработанный под более-менее реальное применение.

Кратко о терминах.

MQTT message queuing telemetry transport. Протокол телеметрии для передачи сообщений. Затрудняюсь перевести название корректно на русский.

Message сообщение. Непосредственно, сами передаваемые данные. Сообщение состоит из нескольких частей:

-Topic, тема сообщения. Символьная строка, размер которой в исходном примере ограничен 200 символами. Клиенты для получения сообщений должны предварительно подписаться на конкретную тему, т.е. topic

-QoS, quality of service. Дополнительный признак, указывающий ждать ли подтверждения получения сообщения или нет.

-Message text, текст сообщения. Текстовая строка из 500 символов.

Протокол построен на модели подписчик/издатель, общая логика которого слегка отличается от обычной клиент-серверной модели, привычной в промышленных протоколах.

При клиент-серверном подходе один или несколько участников исполняет роль сервера, т.е. узла в сети, который отвечает на запросы клиента. Сервер сам по себе не инициирует связь и не проявляет активность, он лишь отдает запрошенные данные и принимает уставки и команды. При этом адресация данных, тип данных и прочее закладываюся и структурируются изначально на этапе разработки серверной части. Клиент тоже должен знать эту карту адресов Modbus. Изменение набора данных требует перенастройки и клиента, и сервера.

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

Итак, роли в протоколе MQTT.

Издатель он же publisher. Узел, который отправляет сообщения (текстовую информацию) на определенную тему (topic).

Подписчик он же subscriber. Узел, который подписывается на определенные темы. Новые сообщения в заявленных темах подписчик получает автоматически.

Роли издателя и подписчика могут совмещаться на одном и том же узле сети. Это роли клиента.

Роль сервера в протоколе MQTT выполняет узел, который называется брокер. Именно брокер запоминает, какие клиенты на какие темы подписались. Именно брокер принимает сообщения от издателей на определенную тему и автоматически раздает их соответствующим подписчикам.

К брокеру может быть подключено несколько и подписчиков, и издателей. Максимальное количество мне неизвестно. Предположу, что все ограничено лишь мощностью железа, на которой работает брокер и настройками стека TCP/IP операционной системы.

В первоисточнике (см. ссылке в начале) идет архив с библиотекой LMQTT_Client. Архив необходимо распаковать, а библиотеку подключить к уже созданному проекту Step 7. Подключение библиотеки выполняется через пункт меню Options Global Libraries Open library. В результате Вы увидите следующее:

Библиотека подключена к проекту

Библиотека содержит две версии функционального блока клиента протокола MQTT для контроллеров S7-1200 и S7-1500. В моем примере будет использоваться младший ПЛК, S7-1214. Реализации отличаются тем, что старшие S7-1500 позволяют адресовать брокер по доменному имени, а S7-1200 только по ip-адресу. Необходимо перетащить блок LMQTT_Client из библитеки во вкладку Program Files контроллера. Типы данных скопируются в проект автоматически. Далее я отхожу от примера и осуществляю вызов ФБ MQTT_Client из своего собственного функционального блока под названием MQTTExchange:

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

enable при положительном фронте этой дискретной переменной устанавливается соединение с брокером MQTT, при отрицательном фронте соединение разрывается. Т.е. для работы необходимо держать этот вход в состоянии TRUE

publishData структура для отправки (публикации) сообщения. Состоит из бита запроса на публикацию (для отправки сообщения бит необходимо взвести в истину и снять в ложь после появления флага done или error), топика и текста сообщения, а так же признака качестве QoS

subscribeToTopic структура, которая содержит флаг запроса на подписку, флаг запроса на отписку (да, можно в любой момент отписаться от топика), непосредственно имя топика и признак качества

На начальном этапе нам важны следующие две структуры, которые необходимо для установления связи с брокером: это tcpConnParam и mqttParam. Эти структуры я заполняю единожды в OB100 при запуске контроллера. Необходимый минимум для работы протокола описывается ниже.

Аппаратный идентификатор интерфейса контроллера

Идентификатор интерфейса ( сетевой карты ) ПЛК. В моем случае у меня всего один интерфейс. Его ID я уже помню наизусть, поэтому пишу 64. Подсмотреть Hardware ID можно в аппаратной конфигурации ПЛК.

Следующее идентификатор соединения. Именно логического соединения по протоколу TCP/IP, connection ID. Должно иметь значение от 1 до 4096, назначается программистом, у каждого логического соединения должен быть свой уникальный айди, иначе связь не будет функционировать. В моем случае у меня присутствует одно-единственное соединения, и я смело назначаю ему 1

Следующее назначение IP-адрес хоста, на котором функционирует брокер.

В данном примере брокер работает на моем домашнем рабочем компьютере с публичным статическим ip-адресом. Из соображений информационной безопасности два байта ip-адреса стерты. В качестве брокера выступает mosquitto под Windows. Разные способы установки брокера хорошо описаны по ссылке:

http://www.steves-internet-guide.com/install-mosquitto-broker/#manual

Там же можно скачать уже настроенный в базе брокер, его я и использую:

http://www.steves-internet-guide.com/install-mosquitto-broker/#

Никаких особенных настроек не применяется. Сертификаты шифрования не используются (об этом чуть позже). На удаленном компьютере просто запущена программа mosquitto.exe, а на удаленном маршрутизаторе сделан проброс порта 1883 (порт для стандартных коммуникаций MQTT) в локальную сеть.

Я умышленно использую брокер, недоступный локально. Одно дело поднять все в рамках локальной сети в горячо любимом диапазоне адресов 192.168.0.х или 192.168.1.х, а совсем другое убедиться в возможности работы системы, как ей и положено, по удаленным каналам связи. Ведь сам контроллер находится сейчас в лаборатории, он подключен к местному маршрутизатору от провайдера, и у контроллера есть только локальный ip-адрес, а не публичный и, тем более, не публичный статический ip.

Кстати, помним, что для организации выхода в интернет контроллеру в данном случае необходимо явно указывать шлюз в настройках его ip-адреса.

Обязательно надо прописать router address, иначе ПЛК не сможет подключиться к внешнем ресурсам глобальной сети

Следующая настройка порт, по которому удаленный брокер слушает соединения. По непонятной причине в примере этот порт указан, как 1884, в то время как стандартным портом для нешифрованных коммуникаций является 1883. Задаю его явно:

Последняя настройка символьное имя клиента. Должно быть уникально в системе. В качестве имени у меня задано S7-1214.

Гораздо разумнее было бы сделать имя клиента уникальным автоматически. Например, сделать его составным из слова SIMATIC и уникального серийного номера центрального процессора (прочитав его соответствующей системной функцией и переведя в строковое представление). С остальными настройками рекомендую ознакомиться в документации. Остановлюсь на двух из них, весьма интересных.

Первое. Last will. Буквально на русском языке завещание (сетевые граждане такие юмористы!). Если выполнить эту настройку, то клиент при подключении к брокеру передает и ее. В случае отвала связи клиента, брокер автоматически разошлет это завещание всем участникам обмена. Завещание является таким же сообщением, у него так же задается топик и текст.

Второе. Возможность шифрования трафика. Для общего тестирования можно обойтись и без него. В продакшене ни в коем случае.

Выставив поле activateSecureConn в настройках необходимо провести еще ряд манипуляций активировать глобальные настройки безопасности проекта, импортировать сертификат брокера, создать сертификат контроллера и так далее. Вопросы зашифрованного соединения я уже поднимал в заметке про OPC UA коммуникации. В целом же действия тут больше напоминают настройки безопасного соединения для Open User Communications (SecOUC). В настоящем примере вопросами безопасности передаваемых данных я пренебрегаю. Подробности настройки описаны все в той же документации.

Закончив настройки, необходимо убедиться, что все работает, как надо. Для начала, конечно же, просто подпимем соединение с удаленным брокером. Для этого достаточно подать истину на вход enable функционального блока LMQTT_Client:

Как видно, при поданном enable выходные биты tcpEstablished и mqttEstablished содержат истину, это означает, что связь установлена успешно. В процессе испытаний я обнаружил интересное поведение блока, а именно при подачи истины на вход enable подтверждение связи появлялось на одну-две секунды и пропадало. Связь устанавливалась только со второй попытки. Кроме того, при физическом обрыве связи этот ФБ просто информирует об отсутсвии соединения, и не предпринимает попыток автоматически пересоединиться. В целях автоматического соединения и пересоединения добавлен следующий нетворк:

Смысл следующий если в течении 5 секунд установлен флаг запроса связи, но нет подтверждения, снять флаг запроса связи. И если флага запроса нет в течении 5 секунд, установить флаг запроса. В тепличных условиях лаборатории это прекрасно работает. В полевых условиях при нестабильной связи временные константы желательно скорректировать.

В данном примере предполагается, что контроллер не только отправляет какую-либо информацию брокеру, но и подписан на топик. Точнее, даже на два топика, чтобы пример был интереснее. В качестве принимаемой информации может быть, что угодно команды, уставки, запросы архивной информации или состояния оборудования. В принципе, все ограничивается исключительно объёмом свободной рабочей памяти. Вполне логичным будет подписаться на нужные топики сразу после установления связи. И для этого потребуется поиграть с полями структуры subscribeToTopic, которую лучше все воспринимать, как управляющую структуру.

По переднему фронту (то есть, по единомоментному факту) установления соединения я поднимаю локальную битовую переменную #SubscriveToTopics и выставляю номер шага процедуры подписки в 1. Номер шага используется в связи с тем, что все операции у нас ассинхронные, и их надо выполнять последовательно, одну за другой, а не все сразу (все сразу не выполнятся).

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

Смотрим. Если выставлен бит выполняем подписку и шаг = 1, то

если не выставлен бит управляющей структуры подписатся, то задать имя интересующего топика (global) и поднять бит управляющей структуры подписаться

если бит управляющей структуры подписаться поднят, то смотреть на биты выполнения задания done и error. При появлении бита done сбросить бит управляющей структуры подписаться и перейти на шаг 2. В случае возникнования ошибки выполнить переподключение к брокеру.

Шаг 2. На этом шаге не нужно делать вообще ничего. Этот шаг требуется для того, чтобы ФБ клиента вызвался хотя бы один раз со сброшенным битом управляющей структуры подписаться. Для этого есть и более изящные решения, но я обошелся простейшим таймером.

После 100мс ожидания просто идем на следующий шаг, к подписке на второй топик (шаг 3).

Шаг 3 аналогичен шагу 1, за исключением имени топика. После успешного завершения шага 3 сбрасывается локальный бит выполнить подписку (#SubscriveToTopics) и обнуляются шаги подпрограммы подписки.

После выполнения подписки можно смело проверять работу клиента. Для этого я вызываю программуmosquitto_pub.exe:

mosquitto_pub.exe -h myhost.mydomain.ru -t global -m kill all humans

, где

myhost.mydomain.ru доменное имя удаленного брокера

global топик global, на который только что подписался клиент

kill all humans текст сообщения в топике global

После отправки сообщения смотрим выходную структуру subscriptionsMessage:

Как видно, в топике global прошло сообщение kill all humans

Флаг newMessageReceived нам еще пригодится в дальнейшем. Этот флаг выставляется сразу по получению нового сообщения и действителен в течении одного цикла сканирования. По нему очень удобно будет обрабатывать полученные команды, уставки или запросы.

Вторая задача контроллера в этом примере отправлять информацию о технологическом процессе через равные промежутки времени. Например, каждые сутки. Или каждые два часа. Ну, или каждые 10 секунд, потому что в учебном примере не очень хочется терять время на пустое ожидание. Поэтому каждые 10 секунд я поднимаю локальный флаг отправить данные и выставлю шаг подпрограммы отправки в 1:

Нетворк отправки данных для разнообразия написан на SCL, а не нарисован в LAD. Право же, оперировать строками в графических языках не самое удобное дело.

Всего отправляется значение 4 переменных, три из которых заданы статично в блоке данных, а одна постоянно увеличивается на небольшую дельту. На первом шаге подпрограммы задается имя топика, это имя personal0. А так же формируется строка сообщения. Поскольку оперируем мы именно символьными данными, приходится выполнять преобразование типа REAL_TO_WSTRING и конкатенацию строк. Для контроллеров, тем более, начального уровня, это не самое лучшее занятие очень быстро расходуется память и неплохо так съедаются вычислительные ресурсы. Длина передаваемого сообщения 500 символов, есть куда развернуться. Можно, так же, добавить еще и метку времени. Можно формировать буфер сообщений, тем самым аккумулируя хотя бы минимум данных на период отсутствия связи. В общем, тут можно делать, что угодно (но лучше делать, что написано в ТЗ).

После формирования строки поднимается бит управляющей структуры отправить сообщение и меняется номер шага. На втором шаге идет проверка успешности или неуспешности отправки, после чего сбрасывается локальный бит выполнить отправку и сбрасывается номер шага подпрограммы отправки.

Запустим клиент MQTT и посмотрим, что приходит в топике personal0 (именно в этот топик ПЛК и отправляет данные):

Ну, и напоследок. Демонстрация возможностей удаленного управления. Если в топике personal0 пришло сообщение exterminate, дискретный выход Q0.0 устанавливается в значение истина.

Команда издателя:

mosquitto_pub.exe -h host.domain.ru -t personal0 -m exterminate

Нетворк программы контроллера:

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

На этом технический пример заканчивается, и хочется немного порассуждать о возможностях применения. Они есть, и, кажется, весьма широкие. Фактически, это можно применять в любых распределенных недорогих системах, где присутствуют малые объемы информации и, в силу этого, использование специализированных телемеханических протоколов нецелесообразно в виду их высокой стоимости. Ну, например, в ЖКХ. Если маленький ПЛК смотрит на расход энергоресурсов (общедомовых, а, может, даже и поквартирных) и раз в сутки отправляет сводку в диспетчерский центр. Или смотрит на состояние общедомового оборудования, шлет параметры раз в час, но при аварии моментально. Достаточно лишь снабдить ПЛК GSM-модемом, и фактически, ничего больше не требуется, кроме простого компьютера с фиксированным ip-адресом. Рассуждая дальше, можно и без физического ПК обойтись, засунув его в облако. Главное, не забыть должным образом настроить шифрование трафика. В этом случае имеет смысл данные потребления энергоресурсов из клиента сразу складывать в базу данных, но это уже требует высокоуровневого программирования

Или, как вариант, та же распределенная система, но не только сбор данных, но и взаимодействие M2M (контроллер-контроллер). Тут точно так же, достаточно минимального канала связи и доступного брокера.

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

Дальнейшие исследование показали отличное применение mqtt совместно со средой Node-RED. На Node-RED была "нарисована" программа, принимающая эти данные от брокера, разбирающая полученную строку и записывающая всю информацию (метку времени, значение) в базу данных MariaDB. Она же, программа на Node-RED позволила вытаскивать информацию за указанный временной промежуток, показывать ее в виде таблицы, графика и делать выгрузку в виде .csv файла.

Подробнее..

Клиент-серверный обмен данными между двумя PLC серии S7-1500 по протоколу OPC UA

04.01.2021 04:07:06 | Автор: admin

При написании данной заметки использовались следующие материалы:

1. Creating of OPC UA clients with .NET and helper class

https://support.industry.siemens.com/cs/document/109737901/creating-of-opc-ua-clients-with-net-and-helper-class?dti=0&pnid=13716&lc=en-RU

2. OPC UA Client Library

https://support.industry.siemens.com/cs/document/109748892/opc-ua-client-library?dti=0&pnid=13716&lc=en-RU

3. On-line справка Step 7 Professional V15.1

4. SIMATIC S7-1500, ET 200MP, ET 200SP, ET 200AL, ET 200pro Communication. Function Manual (10/2018 A5E03735815-AG)

5. Здравый смысл

Протокол OPC UA (http://personeltest.ru/aways/ru.wikipedia.org/wiki/OPC_UA) появился впервые в контроллерах Simatic во второй версии прошивки и в Step 7 версии 14. Тогда контроллер можно было настраивать только в качестве OPC UA - сервера, то есть ПЛК мог отвечать на запросы и отдавать данные, но не мог сам инициировать связь и опрашивать других участников сети.

Радикально ситуация меняется в ноябре-декабря 2018 года с выходом прошивки 2.6 и Step 7 версии 15.1. Появляется возможность настроить CPU в качестве OPC UA клиента. А это, в свою очередь дает нам возможность организовать защищенный канал обмена информацией машина-машина (контроллер-контроллер). И это важно (про существование Secure OUC мне так же известно, однако OUC являет собой обмен данными в свободном формате, чего хватает для маленьких объемов, но отсутствие строгого формата данных накладывает свой отпечаток). Большинство протоколов полевого уровня для промышленности разрабатывалось в прошлом веке и рассчитаны они, в первую очередь, на честных людей. Только время идет, честных людей становится меньше, злодеев - больше, поэтому обмен данными надо как можно лучше спрятать, зашифровать, запаролить, обложить сертификатами, а всем посторонним в ответ показывать обидные жесты и говорить неприличные слова.

Итак, настройка контроллера (S7-1512 FW2.6) в качестве OPC UA сервера.

В процессе некоторых манипуляций в TIA Portal'е, вроде включения глобальных настроек безопасности, среда будет выдавать вам предупреждающие и временами даже угрожающие окна сообщений. При описании я не акцентировал на этом внимание, так что просто нажимайте кнопку ОК и со всем соглашайтесь.

  1. По умолчанию OPC UA отключен в настройках, поэтому заходим в свойства CPU, ищем ветку OPC UA Server General и активируем OPC UA Server.

2. Использование OPC UA в контроллерах Simatic требует приобретение лицензии, поэтому покупаем лицензию, заходим в свойства Runtime licenses OPC UA и ставим тип приобретенной лицензии. Лицензии для 1500ой серии бывают трех типов: маленькая, средняя и большая, в зависимости от типа CPU. S7-1510 и S7-1512 требуют small.

3. По желанию можно задать Application name для OPC UA сервера, порт и временные интервалы. Интервалы сбора (sampling) и публикации (publishing) я выставил минимальными. Стоит помнить, что уменьшение интервалов повышает нагрузку на CPU. Имя приложения и номер порта оставил по умолчанию.

4. Базовая настройка закончена. Компилируем и грузим PLC.

5. Пора бы и проверить, работает ли вообще. Запускаем программу OPC UA Client (ее можно скачать с SIOS по первым двум ссылкам в начале заметки). Ставлю ip адрес моего контроллера 192.168.43.10 и нажимаю кнопку Get Endpoints. Получаю возможные точки входа

Захожу по самому первому варианту, без сертификатов, без шифрования, без пароля, нажимаю Connect to selected Endpoints и перехожу на вкладку Browse nodes. В контроллере загружена программа управления котельной, нахожу одну переменную (Node или узел так называется тэг или переменная в протоколе)

В правой верхней части есть строчка Node id (идентификатор узла или адрес переменной), выделяю его мышкой и жму Ctrl-C

Перехожу на вкладку Read/Write, вставляю скопированный node id и нажимаю кнопку Read

Получаю прочитанное значение переменной равное 3. Действительно, все совпадает, насосы отопительного контура должны чередоваться в третий день недели, и именно эта цифра вбита в переменную DayOfWeek блока данных OKPumpsAuto.

Закрываю программу. Базовый функционал есть, и он работает. Но этого мало. Сейчас OPC UA сервер настроен на режим заходи, кто хошь, бери, че хошь. Необходимо добавить немного безопасности.

6. Включаем глобальные настройки безопасности в свойствах CPU, Protection&Security Certificate Manager, ставим галочку Use global settings

7. В навигации всего проекта находим Security Settings Settings. Смело нажимаем кнопку Protect this project. Действие необратимое. Снять защиту с проекта уже не получится. Дело в том, что оффлайновый проект будет содержать сертификаты серверной и клиентской машины, они должны быть защищены во избежании покражи. После нажатия кнопки Protect система предложит создать администратора проекта:

Теперь любое открытие проекта TIA Portal будет требовать ввода логина и пароля. Кроме администратора проекта, разумеется, можно и нужно создать пользователей с меньшими правами.

8. После того, как мы активировали глобальные настройки безопасности, необходимо заново вручную сгенерировать сертификат контроллера. В дальнейшем лучше, конечно же, сразу защищать проект, создавать администратора проекта и ставить контроллерам глобальные настройки безопасности, но данная заметка частично носит общеобразовательный характер. Возвращаемся в настройки CPU, ищем OPC UA Server Security

Нажимаем кнопку рядом с надписью Server certificate,а в появившемся окне кнопку Add new.

Создаем новый сертификат устройства, все значения остаются по умолчанию. Сертификат, как видно, подписан серьезной организацией! В итоге все становится вот так:

Сейчас предлагаю скомпилировать проект, загрузить его в ПЛК и проверить программой, как работает связь. На самом деле, в результате последних изменений в настройках уровень секьюрности не поднялся ни на йоту. Все это было лишь подготовительным процессом. Потихоньку приходит время приступать к настройкам контроллера, который будет играть роль OPC UA клиента, проверить обмен данными, ну а потом уже закрыть все бреши в безопасности. Но вначале необходимо выгрузить xml файл с описанием всех тэгов (узлов, nodes) контроллера-сервера. Этот файл формируется достаточно просто система выбирает только те переменные, которые помечены флагом Accessible from HMI/OPC UA и разрешает их запись, если стоит флаг Writable from HMI/OPC UA . При этом блок данных тоже должен быть доступен по OPC UA (по умолчанию доступен). Например:

Для экспорта снова заходим в настройки CPU и ищем там OPC UA Server Export, нажимаем кнопку Export OPC UA XML file

Переходим к настройкам OPC UA клиента, то есть той стороны обмена, которая будет инициировать связь, запрашивать и записать переменные. В качестве клиентской части у меня выступает S7-1510 с прошивкой FW2.6.

9. Добавляю контроллер в проект и сразу активирую глобальные настройки безопасности.

10. Активирую OPC UA клиента

11. Активирую лицензию OPC UA

12. Генерирую сертификат этого второго, клиентского контроллера. В дальнейшем контроллер-сервер и контроллер-клиент обменяются (не без моей помощи) своими сертификатами, а подключение незнакомых устройств будет запрещено. В свойствах CPU идем на Protection & Security, жмем Add new.. в таблице Device certificates, жмем на кнопку в появившейся пустой строке, жмем Add new в появившемся окне, наблюдаем уже знакомое окно создания нового подписанного сертификата:

В поле usage я дополнительно указал, что сертификат используется для OPC UA Client'а, по умолчанию у меня был выставлен Tls.

13. Наступает одно из самых интересных: настройка клиентского интерфейса,а точнее настройка соединения к серверу и создания наборов данных для чтения и/или записи. В структуре проекта у второго (клиентского) контроллера:

PLC_2 OPC UA communication Client Interfaces Add new client interface

Жмем кнопку Import interface и подгружаем ранее экспортированный XML файл

Теперь открыты все Nodes серверного контроллера. Поскольку стоит задача учебного характера, требуется немного просто считать 4 переменных блока OKPumpsAuto.

Для этого я создаю Readlist и перетаскиваю интересные мне переменные из правой части экрана в левую. Разумеется, в боевых задачах список чтения может быть больше. Разумеется, в боевых задачах будет не только чтение, но и запись переменных. В этой заметке ограничился одним чтением. В итоге у нас получается следующее:

Итого у нас создан один интерфейс, состоящий из одного списка чтения. Набор данных для чтения подготовлен. Но чего-то не хватает, необходимо еще настроить само соединение интерфейса. Для этого открываем вкладку Properties в нижней части экрана

Необходимо прописать ip-адрес сервера, в моей случае 192.168.43.10.

На странице Security выбираем ранее созданный клиентский сертификат и пока ничего более не меняем.

Создание и заполнение клиентского интерфейса привело к автоматическому созданию двух блоков данных: Client interface_1_Data и Client interface_1_Configuration.

Смотрим блок данных Data, там уже указаны те переменные, которые планируем читать. Чертовски удобно.

14. Теперь необходимо написать программу, которая будет устанавливать связь с OPC UA сервером, готовить канал связи, читать информацию и закрывать соединение. Программа носит строго демонстрационный характер, в боевых задачах она должна быть написана по-другому. В онлайн справке на вызываемые блоки находится все необходимая информация. Там же приводится пример на языке SCL, лучше подходящий к промышленному применению, но не такой наглядный, как в моем случае.

В проект добавляются следующие меркерные переменные

Ищем функцию OPC_UA_Connect и перетаскиваем ее в нетворк OB1

Имя экземплярного блока я оставил по умолчанию. Что радует этот вызов имеет графический конфигуратор, как, например, вызовы S7-связи в TIA Portal'е или вызов PID-регулятора. Этот конфигуратор экономит просто уйму времени, а это не может не радовать.

Остается выбрать только созданный клиентский интерфейс (он один) и обвязать блок вспомогательными переменными.

Для лучшего понимания привожу скриншот из онлайн справки Step 7. Именно в таком порядке необходимо выполнять вызовы (как минимум три) перед тем, как приступить к чтению/записи данных. Любой вызов выполняется по переднему фронту входа Req. Успешное или неуспешное выполнения вызова я фиксирую (не сбрасывая, сброс только ручной) в переменных с именами done или err.

Теперь привожу скриншоты всех нетворков программы уже после того, как я последовательно сделал запрос на выполнение функциональных блоков. Как вы видите бит запроса req у меня выставлен в истину (блок отрабатывается только по переднему фронту), биты успешного исполнения (done) выставлены в истину. Если у вас ни done, ни error не выставляется в истину,а connectionid первого вызова остается равным нулю, значит, что-то сделано не так. Например, используется не тот Server Endpoint или неправильно установлен сертификат. Эти вызовы должны вызваться последовательно один за другим. Я взводил биты руками и смотрел на результат вызова, а только потом переходил к следующему. Очевидно, что полноценная программа должна осуществлять вызов автоматически и переходить на следующий шаг в зависимости от результата текущего.

Нетворк 4 самый важный, это цепочка чтения данных с сервера. Ради него все и делалось. Остальные нетворки вызывают функциональные блоки закрытия соединения и освобождения ресурсов. Их я пока не вызывал.

В результате вызова нетворка 4 в блоке данных DB2 появились прочитанные данные:

15. В настоящий момент два контроллера общаются по протоколу OPC UA, цель почти достигнута. Но не полностью. Настройки безопасности до сих пор находятся на уровне бери, чо хошь. Я спокойно могу подключиться программой OPC UA Client и читать/записывать все возможные данные. Давайте ограничим круг общения этих ПЛК, пусть они дружат только друг с другом. Переходим на серверный контроллер, свойства CPU, OPC UA Server Security Secure channel, убираем из Endpoint'ов политику No Security

Проматываем ниже до Trusted Clients. Добавляем сертификат контроллера-клиента (PLC_2). Снимаем галочку Automatically accept client certificates during runtime. Сервер теперь не будет общаться ни с кем, кроме как с клиентским PLC.

Переходим на User authentication, запрещаем гостевой доступ, создаем логин/пароль для пользовательского доступа: user1 / password1.

Прогружаем серверный PLC. Возвращаемся к клиентскому PLC, открываем Client interface, закладка Configuration, выбираем Security, в поле General выставляем режим и политику безопасности:

Проматываем ниже, выбираем тип аутентификации пользователь и пароль, указываем имя пользователя user1, пароль password1

Снимаем галочку Automatically accept server certificate during runtime

Жмем на зеленую стрелочку, сразу переходим на другую закладку настроек ищем там поле сертификаты партнерских устройств и добавляем в него сертификат сервера

Компилируем и прогружаем клиентский ПЛК (PLC_2), открываем мониторинг OB1 и последовательно вручную исполняем блоки в нетворках 1..4 Если все сделано правильно, то все блоки отработают успешно, а данные будут успешно прочитаны в DB2.

16. Осталось лишь проверить, пустит ли кого-нибудь еще серверный контроллер. Запускаем OPC UA Client и пробуем подключиться. Список Endpoint'ов возвращается. Подключаемся по Basic256 SignAndEncrypt, указав имя пользователя user1 и пароль password1.

Предлагается проверка сертификата сервера, принимаем его

Но соединение не устанавливается, сервер не принимает сертификат левого клиента.

Итого, поставленная цель достигнута. Настроен безопасный обмен данными на уровне ПЛК, то есть - "горизонтальное" взаимодействие.

Подробнее..

Веселые уроки WinCC OA. Установка WinCC OA под Debian и перенос прикладного проекта

30.12.2020 08:08:41 | Автор: admin

Скачивая недавно с сайта winccoa.com установщик последнего патча версии 3.17, с некоторым удивлением, постепенно перешедшим в ликование, обнаружил, что список поддерживаемых дистрибутивов Linux расширился и до Debian. Дело в том, что посмотреть на работу системы в ОС, отличной от Windows, мне хотелось давно, но из всех дистрибутивов Linux я более-менее понимаю только Debian, а привыкать к новому ради баловства откровенно не хотелось. Собственно, и под Debian установка проходит не сильно гладко.

Итак, по порядку. Считаем, что дистрибутив уже скачен с сайта. Лицензия у меня установлена на виндовую машину, да она особо и не нужна. В домашней директории пользователя (он должен находится в группе sudoers'ов) создаю директорию winccoadist, куда распаковываю содержимое скаченного архива.

Запускаю терминал (как обычно, нажатием CTRL+ALT+T) и перехожу в свою директорию с дистрибутивами. Смотрю список файлов.

В связи с тем, что никаких репозиториев тут нет, пакеты являются файлами, необходимо провести установку в правильной последовательности. Вначале установить пакет системы лицензирования codemeter, потом базовый пакет WinCC OA, а далее опциональные пакеты, среди которых мне интересны демо-проекты (Applications), справка на русском и английском (Help) и драйвер S7Plus. Как это частенько бывает в чудесном мире бесплатного линукса некоторые вещи сделаны через такое место, которое в приличном обществе все или почти все называют задницей. Касается это как самих дистрибутивов, так и поставщиков ПО под них. Предвижу ворчание со стороны опытных *nix'оидов, однако с обывательской точки зрения вот так... а я простой обыватель, поймите правильно Для установки пакетов в Debian обычно применяются команда apt, которая сама умеет проверять зависимости пакетов. Поэтому первые два ставим через apt. Для этого в терминале вводим команду

sudo apt install ./codemeter_7.10.4196.501_amd64.deb

и ждем ее завершения.

Далее устанавливаем базовый пакет WinCC OA, который содержит основную инсталляцию все менеджеры, ядро и т.д.

sudo apt install ./WinCC_OA_3.17.9-Base-debian.x86_64.deb

Установка так же завершается успешно. Можно даже посмотреть и убедиться, что система установилась, и ее можно запускать. Правда, в моем случае мне потребовалось вначале закрыть текушую сессию пользователя и залогиниться в систему заново.

Пока что все неплохо, тот же Project Administrator успешно запустился.

А вот с дополнительными пакетами все сложнее. Команда apt по непонятным мне причинам находит неразрешенную зависимость и отказывается ее устанавливать. Причем, ругается на, якобы, неустановленный базовый пакет WinCC OA. Складывается ощущение, что apt проверяет зависимости с учетом регистра. А вот команда dpkg не учитывает регистр при проверке. Поэтому далее нужные мне опции я устанавливаю командой dpkg

sudo dpkg -i ./WinCC_OA_3.17.9-Applications-debian.x86_64.deb

Аналогично устанавливаю справку и драйвер S7plus

sudo dpkg -i ./WinCC_OA_3.17.9-Help_EN-debian.x86_64.deb

sudo dpkg -i ./WinCC_OA_3.17.9-Help_RU-debian.x86_64.deb

sudo dpkg -i ./WinCC_OA_3.17.9-S7Plus-debian.x86_64.deb

Вся система и демо-проекты установились успешно в директорию /opt/WinCC_OA/3.17

Теперь я хочу перенести сюда прикладной проект, получившийся в результате моего базового учебного курса (https://vk.com/wall183956096_8006) и запустить его.

Копирую всю папку с проектом Workshop в свою домашнюю директорию в Debian. Убеждаюсь в том, что я являюсь владельцем (owner) директории Workshop и всех вложенных файлов и директорий. Теперь необходимо скорректировать вручную конфиг-файл проекта. Открываю файл /home/earl/Workshop/config/config

Необходимо скорректировать пути pvss_path (путь к установке WinCC OA) и proj_path (путь к самому проекту WinCC OA). Изменяем эти пути.

Запустим Project Administrator и зарегистрируем в системе мой проект.

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

Если начнутся проблемы с запуском менеджеров архивов в Linux, то способ решения приведен по ссылке:https://www.winccoa.com/knowledge-base/detail/can-a-wincc-oa-project-be-copied-from-windows-to-linux.html

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

Подробнее..

Управление шаговыми двигателями с помощью Simatic S7-1200 с ограниченным количеством импульсных выходов

19.06.2020 14:16:52 | Автор: admin

В конце прошлого года ко мне обратилась одна фирма, которая предоставляет комплексные решения для зерновых культур с предложением рассмотреть проект автоматизации небольшой системы отбора проб зерна. Особенностью данного проекта являлось то, что конструктивные решения и исполнительные приводы уже были разработаны и реализованы в железе. Не вдаваясь в подробности технологического процесса отбора проб можно сказать, что цель автоматизации это управление механическими задвижками зерно-воздушного потока, запуск шнекового смешителя для однородности проб, управление электродвигателями воздушных турбин, обработка управляющих сигналов оператора и датчиков некоторых шагов операций. Задвижки и смеситель были спроектированы так, что приводились в движение с помощью шаговых двигателей.
Раннее было принято решение построить систему автоматизации на базе одноплатного микрокомпьютера Orange pi plus 2e и микроконтроллера Arduino Nano. Для этих плат нашлось применения для другого подобного проекта, но это уже другая история. Но в последствии, после обсуждений всех преимуществ и недостатков остановились на PLC CPU 1214C DC/DC/DC с каталожным номером 6ES7 214-1AG40-0XB0 у которого на борту можно сконфигурировать до четырех импульсных выводов управления и модуль дискретных выходов SM 1222 DQ16 x 24VDC с каталожным номером 6ES7 222-1BH32-0XB0. Шаговые двигатели были выбраны из серии KRS56, управляемые драйверами TB6560 V2.





Выше представлено изображение из functional manual S7-1200 Motion Control V13 SP1 для понимания общей картины структуры управления




Рисунок 1 схема выходов управления драйверами ШД и схема подключения ШД



Загвоздка состояла в том, что хотели найти компромис между бюджетом и оптимально выбранными комплектующими. И первая задача по управлению состояла в том, что на борту PLC только четыре импульсных вывода, а в исходном проекте необходимо было управлять восьмью шаговыми двишатели и одним сервоприводом по ШИМ. После ознакомления с тех-требованиями алгоритма управления системы выяснилось, что одновременный запуск приводов (шаговых двигателей) был не более двух единиц. И конструкция задвижек позволяла оставлять их без удерживающего момента. Поэтому решил реализовать программно так, чтобы один импульсный вывод можно задействовать для множества драйверов шаговых двигателей, управляя разрешающим входом EN драйвера. Импульсный вывод ШИМ управления сервопривода, конечно, оставался обособленно, т.к. это является аппаратно сконфигурированным. Таким образом я реализовал функциональный блок управления ШД. Алгоритм управления системой был выполнен в программном пакете TIA Portal v14 на графическом языке LAD





Рисунок 2 Функциональный блок управления драйвером ШД



Внутри функциональный блок представляет собой простую дискретную логику на лестничной диаграмме с сохранением состояния и таймаутом на срабатывание





Рисунок 3 Функциональный блок детально



Настройка и программирование импульсных выходов для управления шаговыми двигателями подробно расписаны в мануалах, например STEP 7 S7- 1200 Motion Control V13 SP1, также в сети есть видеоролики с объснением требуемых шагов www.youtube.com/watch?v=Ve8YcLyyq4s&list=PLYQly3BqFJ9n_0Vso02h96jO_Fx816MXX&index=2&t=0s
Приведу лишь пару скриншотов настройки импульсного выхода в моем проекте





Рисунок 4 параметрирование импульсного выхода



Для управления непосредственно самим ипульсным выходом необходимо использовать библиотечные функции такие как MC_Power, MC_Home, MC_MoveAbsolute и прочие в данном разделе. Подробное описание функций находится в справочном разделе TIA Portal и в указанных мануалах.
На следующем рисунке приведены части логики, где в части и части 2 показано использование библиотечных функциональных блоков управления ипульсными выходами. Блок MC_Power используется для инициализации аппаратного управления, блок MC_Home для обнуления позиции. Так как логика работы управления ШД поворотной части задвижки основывается на выборе точных позиций опытным путем, то используется блок MC_MoveAbsolute, где значения точных позиций и скорость являются уставками и расположены в соответсвующих тегах блока данных. На скрине упущены промежуточные преобразования между управляющим экземпляром блока FB1 SHUTTER и блоком MC_MoveAbsolute. Если кратко, то выходные сигналы из первого блока, такие как SW_OUT_EN разрешение запуска драйвера ШД, являются входными для второго блока в качестве сигнала execute. На рисунке в части 3 и 4 показано управление выходными сигналами PLC, идущими на входа драйверов ШД, которые также формируются в блоке FB1 SHUTTER. Другими словами, можно создать множество экземпляров данного блока FB1 SHUTTER, связанного с управляющими бибилиотечными блоками импульсных выходов. Но соответствующие драйверы ШД будут задействованы только при наличии (или отсутствии в зависимости от схемы подключения) разрещающих входных сигналов, которые можно формировать отдельными ветвями логики.





Рисунок 5 Логика управления импульсным выходом



В следующей статье я хочу показать, как можно создать несложный HMI на основе Node-red js-библиотек scadavis.io для контроля и визацилизации процесса для Simatic PLC

Подробнее..

Самоучитель по WinCC OA. Часть 4. Повторное использование объектов. -параметры

02.12.2020 06:07:25 | Автор: admin

В предыдущей части мы завершили создание визуализиции задвижки и создали простейший скрипт, имитирующий ее поведение.

У нас есть одна панель под названием Flap, которая отображает и шлет команды для одной задвижки Flap1. Именно такая точка данных указана во всех скриптах этой панели. Возникает закономерный вопрос что делать, если задвижек не одна? И даже не две. А несколько десятков, сотен и даже тысяч (для распределенной системы WinCC OA и несколько миллионов сигналов не помеха, смотрим на Большой Андронный Коллайдер, где применяется именно это система, и завидуем).

Очевидный вариант создать несколько десятков, сотен и тысяч панелей, где каждая точки данных приведена явно, отметаем долго, хлопотно и грозит огромными трудозатратами в случае малейших изменений, неизбежных во время ПНР.

Другой вариант, но не единственный, создание шаблона на основе имеющейся панели. Создадим копию имеющейся панели Flap, для чего выберем пункт меню Panel Save Panel As. Зададим имя Panel_ref.pnl (окончание _ref подразумевает reference, т.е. ссылку, или, если будет угодно, шаблон)

Откроем панель Flap_ref (она и так должна быть открытой после сохранения). Отредактируем скрипты панели, выбрав в меню Edit Edit Panel Scripts. Откроется окно, содержащее все скрипты всех графических примитивов данной панели.

Для задвижки у нас создан собственный тип точки данных, и такие вещи, как положение или команда открытия в любом скрипте будут одинаковыми для всех задвижек. Различаться будут только имена задвижек, т.е. имена точек данных: Flap1, Flap2 и Flap3 в нашем простом случае. Для того, чтобы создать шаблон необходимо заменить имя задвижки, Flap1 на конструкцию, содержащую $-параметр (он так и называется в документации доллар-параметр). Проще все замены выполнять посредством Find&Replace в редакторе. Скрипты теперь выглядят так.

// [RECTANGLE3] [3] - [Initialize]// SimpleCtrlScriptStart {invalid}main(){  EP_setRotation();}void EP_setRotation(){  dyn_errClass err;  if( !dpExists( "System1:" + $dp + ".Inputs.Position:_online.._value") )  {    setValue("", "color", "_dpdoesnotexist");    return;  }  dpConnect("EP_setRotationCB",            "System1:" + $dp + ".Inputs.Position:_online.._value");  err = getLastError();  if (dynlen(err) > 0)    setValue("", "color", "_dpdoesnotexist");}void EP_setRotationCB(string dp1, int iNewValue){  float MIN_VALUE = 0;  float MAX_VALUE = 90;  float MIN_ROTATION = 0;  float MAX_ROTATION = 90;  float fRotation;  fRotation = ( 1.0 * (MAX_ROTATION - MIN_ROTATION) / (MAX_VALUE - MIN_VALUE)) *              (iNewValue - MIN_VALUE) + MIN_ROTATION;  if (fRotation > MAX_ROTATION) fRotation = MAX_ROTATION;  else if (fRotation < MIN_ROTATION) fRotation = MIN_ROTATION;  setValue("", "rotation", fRotation);}// SimpleCtrlScript {EP_setRotation}// DP {System1:" + $dp + ".Inputs.Position}// DPConfig {:_online.._value}// DPType {int}// PVSSRange {0}// Min {0}// Max {90}// MinRotation {0}// MaxRotation {90}// SimpleCtrlScriptEnd {EP_setRotation}// [PUSH_BUTTON1] [4] - [Clicked]main(mapping event){  dpSet("System1:" + $dp + ".Commands.Open", 1, "System1:" + $dp + ".Commands.Close", 0);}// [PUSH_BUTTON2] [5] - [Clicked]main(mapping event){  dpSet("System1:" + $dp + ".Commands.Open", 0, "System1:" + $dp + ".Commands.Close", 1);}

Было:

System1:Flap1.Inputs.Position:online..value

Стало

System1:" + $dp + ".Inputs.Position:online..value

Строка, которая ранее содержала непосредственно элемент точки данных, теперь формируется из первой постоянной части (System1), к которой добавляется $dp, после чего добавляется вторая постоянная часть, которая так же не зависит от точки данных, т.е. от конкретной задвижки. Знак + подразумевает объединение строк. Сами строки в данном случае заключены в кавычки. В явном виде такую панель использовать, разумеется, не получится. Необходимо задать подстановку для $dp (Flap2, например) при вызове этой панели.

Используя метод аналогии, можно сравнить $-параметры с формальными и фактическими параметрами функции в любом языке программирования. При вызове функции мы всегда должны задавать фактические параметры, значения которых заменят формальные, и функция отработает.

Теперь создадим новую панель. Пусть она называется Flaps. Сделаем новую панель чуть побольше.

Далее необходимо перетащить мышью из дерева проекта панель Flap_ref на рабочее поле открытой панели Flaps. При этом система автоматически определит присутствие $-параметров и предложит подставить вместо формального значения фактическое.

Drag'n'dropDrag'n'drop

Зададим имя клапана, вместо $dp укажем Flap1. Нажмем на кнопку Save and Run in QuickTest Mode и убедимся, что клапан 1 реагирует на нажатия кнопок Open и Close то есть, открывается или закрывается. Если клапан вдруг не реагирует на команды, посмотрите состояние контрол-менеджера, добавленного в предыдущей части, он может быть остановлен (тогда его надо, разумеется, запустить, или просто перевести режим запуска из ручного в автоматический, чтобы каждый раз не отвлекаться).

Все работаетВсе работает

Добавим точно так же панель для Flap2

Проверяем. Первый клапан работает (управляется), второй- не работает. Почему? Все просто, созданная вчера модель (в виде скрипта) управляет всего лишь одним клапаном Flap1. Остальные клапаны в модели не описаны.

Второй клапан не работаетВторой клапан не работает

Доработаем имеющуюся модель для того, чтобы она управляла и вторым клапаном тоже. Модифицируем быстро, но неправильно, путем копи-паста. Создаем еще две функции dpConnect и две разных callback-функции, переименовываем первую callback-функцию. В итоге получается такой код.

Не забываем перезапустить Control Manager этого скрипта (тот, что с опцией -num 2). После модификации модели проверяем изменения и видим, что оба клапана управляются независимо.

В связи с тем, что на DPE Position у нас натравлен конфиг с функцией, третий клапан на экран пока не выносим (собственно, я его и в модель-то напрасно внес).

Подробнее..

Самоучитель по WinCC OA. Часть 5. Работа с журналом тревог

04.12.2020 04:07:31 | Автор: admin

(* Начиная с этой части я перешел на версию WinCC OA 3.17. Никаких отличий в масштабах базового курса не будет. Изменятся лишь надписи на скриншотах.*)

При создании типа точки данных Flap мы уже предусмотрели битовый DPE для аварии Момент (FlapAlarmsTorque). Однако в настоящий момент для всех трех точек данных (Flap1, Flap2, Flap3) это всего лишь элемент, переменная. Для того, чтобы изменение этой переменной фиксировалось в подсистеме сообщений, необходимо повесить на этот DPE соответствующий конфиг. Для этого открываем модуль para и ищем наши точки данных.

Добавляем к DPE Torque точки данных Flap1 конфиг Alert Handling

Открываем настройки добавленного конфига. Обратите внимание, что внешний вид настройки Alert Handling зависит от типа переменной, к которой относится сам аларм. В настоящий момент мы работаем с типом данных bool, внешний вид окна следующий.

Good range касается значения самое переменной какое из значений является плохим (т.е. при каком значении считаем, что аларм сработал), а какое хорошим (аларма нет). Так же задаем текст при появлении аларма и его исчезновению. Зададим этот текст, как came (пришло) и went (ушло). За хорошее значение будем считать битовое значение ложь, т.е. аларма нет.

Класс аларма весьма важный параметр. Он определяет многие аспекты отображения сигнализации: какой будет цвет, задействовано ли мерцание, будет ли звуковое уведомление, требуется ли квитирование и т.д. Выбираем класс S7_Alarm и ставим галочку рядом со словом Active. После выставления аларма активным все настройки становятся недоступны. Обратите внимание, что настройки активного аларма недоступны для редактирования не только на визуальной форме, но и средствами скриптов (а скриптами в WinCC OA можно много чего сделать, включая и динамическую работу с алармами). Настройки допускается менять только для неактивных алармов.

Перейдем в модуле para на конфиг original, посмотрим текущее значение и поменяем его на истину.

Текущее значение переменной FALSE, аларм неактивенТекущее значение переменной FALSE, аларм неактивенТекущее значение переменной TRUE, аларм активен, текст мигает очень быстроТекущее значение переменной TRUE, аларм активен, текст мигает очень быстро

Вернем значение переменной в FALSE. Текст аларма остается came, продолжает мигать, изменилась лишь интенсивность его мигания.

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

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

На первый взгляд тут есть много точек данных, но где же DP, ответственные за классы алармов? Выставляем галочку Internal datapoints (внутренние точки данных) и видим, что полку точек данных заметно прибыло.

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

Тут и задается приоритет класса, сохранение алармов, необходимость квитирования, надо ли квитировать прошлые алармы, если тревога возникала неоднократно до действий оператора, цвет и шрифт текст алармов и т.д.

Только что мы смотрели, как работают алармы в модуле para. Это очень интересно и познавательно для разработчика, однако не очень удобно. Ознакомимся еще с одним способом просмотреть список событий. Для этого в меню gedi найдем и нажмем кнопку System Management

Перейдем в Diagnostics

Откроем Alarmscreen

И в появившемся окне нажмем на зеленый треугольник внизу панели

Откроем модуль para, найдем Flap1.Alarms.Torque и поиграем с его значениями

В окне Alarmscreen можно сквитировать алармы. По умолчанию данное окно показывает только текущие тревоги. Если активных алармов нет, а все старые уже сквитированы, то список пуст. Для того, чтобы изменить представление, необходимо нажать кнопку с отверткой и гаечным ключом внизу панели. В этих свойствах очень много закладок и очень много настроек. И все они касаются одних только алармов.

Если задать Time range в Open и выбрать Historical Data, то мы сможем увидеть историю алармов.

В связи с тем, что тема алармов весьма обширная, а это всего лишь первый взгляд, не стоит удивляться некоторым непонятным вещам. Например, мне самому непонятно, почему в этой таблице Alarm text у нас везде состоит из одного слова came (нуууу когда-нибудь, наверное, может быть, если фаза луны сойдется, то разберусь).

Alarmscreen тоже очень интересен. Смотреть на нем алармы куда лучше, чем в модуле para. Однако, мы делаем конечное приложение для конечного пользователя, т.е. для оператора. Удобно ли будет оператору каждый раз открывать через менюшки отдельное окно аварий? Лично я в этом сильно сомневаюсь. Давайте лучше займемся интеграцией окна сообщений в наше приложение пользователя.

Перейдем обратно в редактор gedi и откроем созданную ранее панель Flaps. Далее посмотрим внимательно на левую часть редактора gedi. Кроме нашего проекта (у меня он называется Workshop) есть еще один проект с названием 3.17 (в самом начале этой главы я уже признался, что перешел на новую версию, а так был бы проект с названием 3.16), который мы точно сами не создавали. Его можно развернуть и поизучать, там много всего интересного скриптов, библиотек, панелей и т.д. Пытливые умы потратят немало времени на ознакомление.

По сути, это и есть ядро системы WinCC OA. Этот проект содержит все или почти все, относящееся к самой платформе gedi, para, панель Project Administrator, панель консоли, вся логика. И, вуаля, всем этим можно пользоваться в своих приложениях. Вот, например, я открыл в редакторе gedi панель para. Его можно изменить. И использовать в своих целях измененную копию. Про модификацию стандартных (или системных?) компонент WinCC OA поговорим чуть попозже в этой же части. А сейчас наша задача разместить окно с алармами в окне пользовательского приложения.

Редактировать модуль para пока не будем (хоть и очень хочется)Редактировать модуль para пока не будем (хоть и очень хочется)

Для этого в дереве системного проекта 3.17 ищем панель по следующему пути: 3.17 (версия Вашей копии WinCC OA) Panels vision aes AESRow.pnl. Перетаскиваем ее на свободное место открытой панели Flaps. Система попросит задать значение $-параметра $SCREENTYPE. Цинично проигнорируем эту просьбу, нажав кнопку Cancel.

Внизу у нас появился небольшой элемент отображения

Проверим, работает ли оно вообще, для чего зададим в модуле para значение истина для аварийного сигнала.

Как видно, работаетКак видно, работаетСкрыл модуль para, аларм есть и активно мигает (чего на видно на скриншоте)Скрыл модуль para, аларм есть и активно мигает (чего на видно на скриншоте)

Теперь поговорим немного о модификации стандартных компонент WinCC OA. Например, сейчас я растянул в высоту окно отображения алармов. В нем добавилось строк, однако кнопки и цифры в правой части растянулись в высоту, что плохо влияет на эстетику. Упростим себе задачу и просто уберем эти кнопки.

Для этого в редакторе gedi кликнем правой кнопкой на AESRow и выберем Open panel reference

Откроется оригинальная панель

Удалим некрасивые кнопки и цифры

Нажмем Save и увидим следующее окно

Ответим утвердительно, после чего редактируемая нами панель сохранится в виде копии в структуре нашего проекта Workshop, а не по системному пути. В нашем проекте мы будем работать с этой копией. Теоритечески можно сохранять по системному пути (например, путем подмены файлов), то эти изменения становятся общими для всех проектов WinCC OA, что нежелательно. Особенно негативно сохранение по системному пути, а не в виде локальной копии, может сказаться после установки патчей. Патчи могут корректировать, в том числе, и системный проект, и даже ту панель, которую вы изменили. Вероятность этого всегда отлична от нуля. Соответственно, устанавливая патч, вы рискуете утратить свои изменения, если сохранили их в системный проект.

Есть еще один нюанс, связанный с модификацией системных панелей и патчами. Если вы работаете с локальной сохраненной копией проекта, то натравленный на систему патч исправит только системный проект, но не вашу локальную копию панели.

В общем, модификация ядра это ответственное занятие, требующее внимательности. Не забывайте читать readme патчей, затронуты ли там модифицированные вами панели. У вас в руках очень мощный инструмент, но пользоваться им надо уметь.

После внесения изменений в панель AESRow нашу панель Flaps необходимо переоткрыть, чтобы изменения вложенной панели вступили в силу.

Последний пример в этой части визуализация пиктограмм технологического оборудования, находящегося в аварином состоянии, тем же способом (цвет, мерцание и т.д.), который задан соответствующему классу аварий. Для этой демонстрации разместим на панели Flaps окружность (Circle).

Зададим динамизацию события Initialize через визард, как уже делали ранее для задвижки. Выбираем в визарде Change color

Выбираем Background color и Alert Handling, жмем Next. Выбор Alert Handling позволит системе подтянуть значение цвета от аларма.

Выбираем Datapoint element

Жмем Next, а в следующем окне Finish. Запускаем на проверку и смотрим, что получилось. В настоящий момент аларм у меня активен и не подтвержден, круг быстро моргает серым и красным (как и класс алармов S7_Alarm).

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

Взглянем на полученный скрипт

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

Подробнее..

Самоучитель по WinCC OA. Часть 6. Навигация Открытие новых окон

07.12.2020 10:20:40 | Автор: admin

До настоящего момента весь наш прикладной проект состоял, фактически, из одной экранной формы Flaps (панель Flap можно уже не рассматривать, она неактуальна, а Flap_ref является шаблоном). Реальные боевые проекты содержат, как правильно, (значительно) больше одной мнемосхемы, отображающие целую картину, отдельные технологические участки, настройки, тренды, алармы и т.д. Рассмотрим, каким образом в системе WinCC OA возможно осуществлять переход между экранами.

Создадим еще одну панель в проекте и назовем ее Trends (на будущее), сделаем ее размер сопоставимым с размером панели Flaps и поместим на нее что-нибудь читаемое. Например надпись Это тренды, чтобы быть уверенным наверняка.

Панель TrendsПанель Trends

Для вызова панели Trends из панели Flaps разместим на последней кнопки и назовем ее Panels, для чего изменим имя объекта (Name) и метку (Button label). Разумеется, имя и метка разные вещи, имя идентифицирует объект в проекте, а метка содержит текст, видимый оператору.

Посредством визарда зададим кнопке функцию открытия новой панели. Выбираем кнопку, ищем событие Clicked и выбираем рядом с событием Clicked вызов мастера Property Wizard. В визарде выбираем Panel Functions и жмем Next.

Выбираем Open panel (in new module) и снова жмем Next.

Вдумчиво смотрим на следующее окно визарда

Рассмотрим параметры визарда:

Panel file файл панели, которую мы будем показывать. Выбираем (можно через кнопку справа, только там выбор файла по умолчанию идет по расширению xml, не забудьте его изменить на all files) файл Trends.pnl

Panel name имя экземпляра панели. Очень важный параметр! На этапе повторного использования графических объектов при помощи $-параметра мы убедились, что одну и ту же панель можно вызывать множеством экземпляров. При этом (при множественном использовании панели) каждый вызываемый экземпляр должен быть четко и уникально идентифицирован. Для этого и предназначен этот параметр. Обратите внимание, что за уникальностью имени экземпляра должен следить непосредственно разработчик, система не отслеживает дубли (два или больше разных вызов с одинаковыми именами панели). Наличие таких дублей в системе чревато очень веселыми чудесами, а точнее непредсказуемым поведением окон. Зададим в визарде имя Trends. Никакие параметры в эту панель не передаются (панель содержит одну-единственную надпись), поэтому тут заполнять не требуется.

Жмем Next и смотрим следующие настройки

По умолчанию будет выполнено открытие экрана в дочерней панеле. Добавим еще опцию Panel always on top, поставим эту галочку и нажмем Finish.

Выполним панель Flaps и нажмем кнопку PANELS, появится окно Trends.

Открылось дочернее окно, при этом перейти на родительскую панель или что-нибудь на ней нажать невозможно (из-за выбранной опции modal).

Проверим следующий режим вызова, для чего вернемся к визарду кнопку PANELS. Уберем модальный режим и проверим исполнение. В этом случае родительское окно доступно, хоть и перекрыто дочерним. По крайней мере, можно нажимать кнопки управления задвижками родительского окна.

Открыл клапан 1Открыл клапан 1

При этом, если закрыть родительское окно, то дочернее так же закроется оно существует только в связке с родительским.

Выберем следующий режим открытия панели Root panel in own module

Проверяем.

До нажатия кнопки

После нажатия кнопки

Происходит полное замещение панели: Flaps пропала (все ее объекты стерты из памяти рантмайма), появилась панель Trends. Модуль при этом остался тот же, в нашем случае модуль называется _QuickTest_

Следующий режим открытие в новом модуле

В этом случае создается новый модуль (можно еще назвать его контейнером), в котором уже, в свою очередь, открывается наша панель. Если не задавать имя новому модулю (оставить пустым поле Module name), то этот вызов сводится к предыдущему панель будет открыта в исходном модуле. За уникальностью имени модуля так же необходимо следить самостоятельно. В рамках одного ui система позволяет обратиться к любому графическому элементу. Надо лишь знать его имя. Полное имя объекта складывается из имени модуля, имени панели и имени графического объекта. Пусть новый модуль будет незамысловато называться Module

Жмем Finish и проверяем.

В этом случае у нас получается два полностью независимых окна. Обратите внимание на имена модуля и панели каждого окна все соответствует. При закрытии изначального окна (_QuickTest_) новое окно остается.

Подробнее..

WinCC OA Workshop. Часть 7. Навигация Создание интерфейса АРМ

11.12.2020 08:05:27 | Автор: admin

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

Создаем панель Main, в которую будем встраивать остальные компоненты. Ее размер делаем больше размера панели Flaps.

Через Cut и Paste (вырезать/вставить) переносим панель алармов с панели Flaps на панель Main и корректируем размер панели алармов.

Создадим кнопки для открытия панелей Flaps и Trends.

Осталось определить главную рабочую область, где будут находиться основные мнемосхемы. В WinCC OA эта рабочая область называется Embedded Module (можно провести аналогию со Screen Window или Picture Window портальной или классической WinCC). Embedded Module дословно означает Встроенный модуль. Если в предыдущей части создавался некий внешний модуль, то в данном примере модуль, предназначенный для отображения панелей, интегрирован в панель. Embedded Module расположен среди инструментов графического редактора

Разместим его в свободном месте панели Main. Сразу после размещения система предлагает задать анимацию, с которой будут открываться новые окна. Можно задавать, а можно и нет. Давайте пока обойдемся без рюшечек, анимацию не задаем.

Тут возникает еще один тонкий нюанс. У каждого графического объекта и, конечно же, у каждого модуля в системе должно быть свое уникальное имя. По причинам, известным лишь разработчикам, имя только что созданного модуля, по которому с ним можно работать скриптами (и визардами, которые на выходе тоже создают скрипт) задается не на вкладке Standart редактора свойств, а на вкладке Extended.

Ниже привожу скриншот вкладки стардарт. Там указано имя модуля EMBEDDED_MODULE1, и это неправильное имя.

Это неправильное имяЭто неправильное имя

Правильное имя модуля указывается на другой вкладке, и по умолчанию в моем случае равно Vision_1

Давайте переименуем этот встроенный модуль и дадим ему имя Module. Предвидя вопросы, что у нас уже есть модуль с таким именем, отвечу, что по предыдущей схеме с созданием модуля Module в скрипте кнопки мы более работать не будем, хотя, вопрос, конечно же, хороший и правильный.

Теперь необходимо задать действия по нажатию кнопок FLAPS и TRENDS. Задаем событие Clicked, работаем в текстовом режиме (не визардом на этот раз). Нам необходима функция RootPanelOnModule. Можно и нужно использовать автоподстановку. Например, сейчас я набрал текст RootPanel и нажал кнопки CTRL+TAB.

Если функция подсвечивается синим то все неплохо. Если же черным, то в имени фунции допущена ошибка. Не забывайте, что регистр в имени функции тоже играет роль.

Первый параметр этой функции имя панели. У меня это Flaps.pnl. Второй параметр имя панели, пусть будет Flaps. Далее имя модуля, мы его только что задали, Module. Последний параметр функции это параметры, передаваемые непосредственно панели. Тип этой переменной dyn_string, динамический массив строк неопределенной заранее размерности. Сейчас никаких дополнительных параметров мы передавать не будем, однако это значении необходимо указать. Объявим переменную типа dyn_string и укажем ее в вызове функции открытия панели. Получаем следующий код открытия окна

Аналогично выглядит и скрипт кнопки TRENDS

Заостряю внимание на то, что модуль это некоторая независимая сущность. Неважно, где этот модуль находится, создается ли отдельным окном или интегрируется в другую панель, он остается модулем. Поэтому в рамках одного пользовательского интерфейса имя модуля должно быть уникальным.

Внесем косметические изменения в панели Flaps и Trends, уберем ненужные элементы (панель алармов и кнопку справа), изменим размер панелей.

Запускаем окно Main.pnl на исполнение и проверяем поведение кнопок FLAPS и TRENDS. Все должно работать. Если не работает, ищите ошибки.

Подробнее..

Самоучитель по WinCC OA. Часть 8. Тренды

14.12.2020 08:18:37 | Автор: admin

Оживим уже созданную (но пустую) панель Trends графиком изменения переменной во времени. Однако, перед тем, как смотреть на тренды, их необходимо сконфигурировать и каким-то образом задать значения, чтобы они скопились в базе данных. Необходимо, чтобы в системе была переменная, которая меняла свое значение. Необходимо повесить соответствующий конфиг на эту переменную, чтобы значения складывались в архив. Для типа точек данных Flap у нас есть DPE с названием Flow (расход) и типом int. Этот DPE и будем использовать для ознакомления с трендами. Для имитации поведения системы у нас уже есть созданный control-скрипт Model. Предлагаю его и использовать для иммитации расхода. Откроем скрипт Model

Напомню, что точкой входа в скрипт (как и в языке C) является функция main(). В настоящий момент функция main() исполняется, происходит связывание изменения DPE с его функцией-обработчиком (callback-функцией), после чего main завершает работу, но в памяти остаются сами callback-функции.

Изменяем функцию main следующим образом

main(){  dpConnect("OnOpen_CB1", "System1:Flap1.Commands.Open");  dpConnect("OnOpen_CB2", "System1:Flap2.Commands.Open");  dpConnect("OnOpen_CB3", "System1:Flap3.Commands.Open");  for (;;) {    dpSet("System1:Flap1.Inputs.Flow", rand());    dpSet("System1:Flap2.Inputs.Flow", rand());    delay(1);  }}

Теперь после выполнения связывания DPE с callback у нас стоит бесконечный цикл (при помощи конструкции for(;;)), который задает случайным образом значения расхода для клапанов 1 и 2, после чего ожидает 1 секунду при помощи функции delay. В этом случае функция main не будет завершаться и будет работать, пока запущен ее CTRL-менеджер.

(Кстати, да тут достаточно и одного вызова dpSet, а не двух.. Совершаю ошибки, про которые сам же и говорил ранее)

После перезапуска CTRL-менеджера (в связи с изменением скрипта) нужно убедиться, что модель работает, взглянув на значения расхода в модуле para.

Пришло время визуализировать изменения расхода. Откроем панель Trends. Паренесем на панель графический элемент Trend из палитры инструментов редактора gedi.

Необходимо выбрать режим отображения тренда

Стандартный режим значение по времени. Второй режим Value over value был разработан для специфических требований некоторых заказчиков для сопоставления невременных значений. Например распределение давления по трубопроводу, где по оси X километровая отметка трубы, а по оси Y значение давления на отметке. В нашем случае выбираем первый вариант, расположение горизонтальное, нажимаем ОК. Далее выбираем точку данных для отображения, выбираем расход первого клапана.

Далее через кнопку Append добавляем еще одну кривую расход второго клапана. Итого закладка Curve (кривая) выглядит так

если выбрали график #1_1, и так:

если выбрали (для настроек отображения, конечно же) график с именем #1_2. Нажимаем кнопку Close. Панель Trends в редакторе теперь выглядит так

Сохраняем и закрываем панель Trends и запускаем панель Main, нажимаем в исполнении кнопку TRENDS. После я вижу, что окно с трендами как-то меняется, то есть я вижу динамику, но пока она очень ненаглядна и весьма непонятна.

Связано это с тем, что значение меняется каждую секунду, отображается сейчас текущее значение (архивация еще не настроена), а по умолчанию выбрана временная шкала в два часа. Разумеется, график выглядит очень сжатым. Поиграв колесиком мышки на шкале времени, на шкалах оси X для каждого из трендов, а так же на основном рабочем поле, можно получить более наглядное изображение. Оси можно не только масштабировать колесиком, но и двигать, перемещая мышь с зажатой на оси левой кнопкой. Можно выделить мышью на рабочем поле тренда любую его часть, экран будет масштабирован под выделенное. Если вы что-то нажали, и все сломалось, просто нажмите кнопку 1:1 в верхней части экрана, масштабы вернутся к изначальной конфигурации.

Однакратный щелчок мышью на рабочем поле показывает линейку или ruler

При активном рулере можно с нажатым Shift снова кликнуть на рабочем поле, тогда отобразится т.н. диференциальный рулер

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

В настоящий момент, если полностью закрыть окно тестирования и открыть его заново, то все значения начнут отображаться только с момента открытия окна графиков. Это вполне закономерно, так как на точках данных не размещен конфиг архивации, т.е. данные не пишутся в базу. Вернемся в модуль para. Встанем на элемент точки данных Flap1.Input.Flow и добавим к нему конфиг _archive

Тут необходимо выбрать Archive Settings. Появился конфиг _archive, выберем его.

Тут необходимо выбрать один из шести типов архивов

Если посмотреть на консоль системы, то и менеджеров архива у нас запущено тоже шесть экземпляров

То, что я сейчас рассказываю про тренды (точнее, про архивирование) касается исключительно встроенной файловой базы данных. В WinCC OA есть возможность архивации в базу Oracle. В версии 3.17 появилась возможность использования InfluxDB, которая при создании проекта обозначена, как NextGen архиватор. Сейчас же говорим про классическую файловую базу. В качестве индекса (первичного ключа) в ней выступает метка времени сигнала. Если говорить очень приблизительно, то на все заданные тренды у нас создается одна таблица. А значения сигналов с поля меняются по-своему, но записываются в базу данных по изменению. Если свести все тренды в одну таблицу, то ее использование будет весьма неоптимальным, в таблице будет очень много пробелов. Для оптимизации нам предлагается в рамках одного менеджера архивов (у которого есть свои настройки, и они могут отличаться или отличаются от настроек соседниех архивных менеджеров) хранить переменные, интенсивность изменений которых более-менее одинаковая. Не рекомендуется совмещать в рамках одного архива переменные, которые меняют свои значения с очень разной частотой, например раз в секунду и раз в час. Это очень неточное описание, которое лишь приблизительно объясняет наличие нескольких архивных менеджеров в системе. А при наличии NextGen архиватора на базе InfluxDB, возможно, данное описание вообще теряет смысл, однако в настоящей заметке дублируется оригинальный курс, так что это объяснение сохраняется по соображениям совместимости.

В нашем демонстрационном примере обе переменные, меняющиеся раз в секунду, необходимо архивировать в рамках одного менеджера. Пусть это будет менеджер ValueArchive_0000.

Выбираем Active, нажимаем Apply.

То же самое проделываем для второго клапана. Запускаем снова на исполнение панель Main, открываем тренды, подгоняем масштаб и смотрим, что изменилось

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

На практике может потребоваться не просто визуально отобразить исторические значения переменной, но и получить их в числовом виде (в виде массива, например) для какой-либо дальнейшей обработки. Универсальным инструментов для этого является старый-добрый SQL-запрос. Для более удобного визуального формирования запроса к базе данных WinCC OA предоставляет отдельный инструмент SQL Query. Чтобы добраться до него необходимо запустить (в том же gedi в меню Module) System Management

Далее открыть Reports

И SQL-Query. Ставим тут галочку напротив слова ALL в рамке Value type (в скриншоте не проставлена, а должна быть).

На вкладке SELECT выбираем те значения DPE, которые необходимо включить в выборку. Система архивирования, на самом деле, запоминает не только пару метка времени значение, но и массу другой информации. Но мы сейчас прочитаем из базы только значение и метку времени originalvalue и originalstime. Для этого из выпадающего списка Configuration выбираем искомое и добавляем его в список Elements of the SELECT Statements при помощи кнопок Append или Insert, расположенных справа от списка. Кнопки не подписаны. К этим кнопкам и этому диалоговому меню необходимо привыкнуть, на первый взгляд там все немного неоднозначно.

На следующей вкладке From мы определяем, из каких точек данных мы хотим вытащить значения.

Тут можно воспользоваться механизмом шаблонов, написать в текстовом поле Flap* и добавить этот текст в список Elements of the FROM statement.

Все остальные параметры пока игнорируем и переходим на вкладку Data

Нажимаем вначале кнопку Create query, после чего кнопку Start query, получаем набор данных.

Попробуем эти же данные получить в программном коде. Скопируем куда-нибудь SQL-запрос. Откроем панель Main и добавим на нее кнопку с меткой EXPORT

Начнем программировать скрипт на событие Click этой кнопки. SQL-запрос, скопированный из окна SQL-query я временно разместил в комментарии к этому коду

Сейчас нам потребуется применить функцию dpQuery. dpQuery отличается от функции dpGet тем, что возвращает массив значений, которые удовлятворяют SQL-запросу, в то время, как dpGet возвращает лишь одно значение. Первый параметр функции запрос SQL. Второй параметр в переводе на русский звучит как двумерный массив неопределенного типа и неопределенной размерности, а в типах данных WinCC OA как dyndynanytype. Объявим в скрипте переменную этого типа. Разбирать полученные данные мы будет, конечно же, имея представление что именно мы запрашиваем. И это представление мы имеем, как авторы запроса.

main(mapping event){  dyn_dyn_anytype Tab;  dpQuery("SELECT ALL '_original.._value', '_original.._stime' FROM 'Flap*'", Tab);}

Результат запроса складывается в Tab, но этот результат необходимо еще куда-то вывести. В качестве примера выводить полученные данные будем Log Viewer при помощи функции DebugN. Данная функция незаменима для дебага во время разработки, тестирования и наладки системы. В качестве параметров допускается приводить несколько строк. Совет из практики указывать дополнительно в DebugN не только отладочную информацию, но и заголовок отладочной информации. Если проект очень большой и над ним работает большой штат, то не помешает так же указывать, кто именно выводит в лог. Причина на то простая разработчиков может быть много, а лог в системе один. Зная, что выводится и кто выводит можно подойти к коллеге и вежливо поинтересоваться, а что тут, собственно, происходит. Зная заголовок вывода можно провести глобальный поиск в проекте и найти этот вызов на тот случай, если вызов DebugN был оставлен после ПНР, его вывод осуществляется далеко не на постоянной основе, сам проект давно в работе, а разработчик давно уволился. Культура разработки, если двумя словами.

В конечном итоге обработчик кнопки EXPORT выглядит следующим образом

main(mapping event){  dyn_dyn_anytype Tab;  dpQuery("SELECT ALL '_original.._value', '_original.._stime' FROM 'Flap*'", Tab);  DebugN("SQL", Tab);}

Сохранив скрипт, запустим снова окно Main на исполнение. Найдем окно Log Viewer (оно открывается всегда автоматически при запуске системы), очистим вывод журнала кнопкой крестик для нашего удобства

Нажмем в рантайме кнопку EXPORT и посмотрим на вывод в журнале

Выгрузка получилась очень большая, и показано далеко не все, о чем LogViewer честно сообщает

Обидно, но логично. Выводить несколько мегабайт текста в журнал не лучший выбор. На практике переменную Tab можно разобрать и натравить на нее свою математику. Можно вывести информацию в таблицу и так далее.

Подробнее..

Самоучитель по WinCC OA. Часть 10. Подключение к живому S7-1200

26.12.2020 12:22:33 | Автор: admin

рамках базового курса в системе WinCC OA используются только внутренние переменные системы. Никаких внешних подключений не предполагается. Однако, слушатели базового курса всегда под завершение учебы просят продемонстрировать, как же считать переменную с настоящего живого ПЛК. Поскольку WinCC OA относится к продуктам компании Siemens, то логичным будет продемонстрировать подключение к контроллеру компании Siemens и чтению с него нескольких переменных. В нашем случае будем подключаться к PLC серии S7-1200.

Набор драйверов WinCC OA включает в себя два вида драйверов для контроллеров Simatic это s7 и s7plus. Разница в них следующая: s7 предназначен для связи с ПЛК классической серии S7-300 / S7-400, а s7plus для современной линейки S7-1200 / S7-1500. Драйвер s7plus указывается при установке отдельно. Он может отсутствовать в вашей системе, если вы его не устанавливали. Вне зависимости от того, какой используется драйвер (хоть iec104), общие принципы сохраняются. Необходимо в консоли добавить соответствующий драйвер. Далее сконфигурировать соединение с устройством и задать этому соединению номер добавленного драйвера, активировать. Так же требуется на DPE навесить конфиг Periphery Address и выполнить настройки, указав корректный адрес переменной.

Для начала необходимо прописать в консоли драйвер. Технически добавление драйвера в систему не отличается от добавления менеджера. Откроем консоль, нажмем в ней Append a new manager

Выберем в списке драйвер S7plus и зададим в опциях -num 2. Это связано с тем, что в системе уже есть драйвер с номером 1, это Simulation Driver, а номер драйвера в системе должен быть уникален. К слову, по словам разработчиков WinCC OA, симуляционный драйвер в реальных проектах не используется.

Теперь новый драйвер добавлен в систему и запущен

Теперь добавленный драйвер с номером 2 в системе необходимо сконфигурировать. Для этого (например через меню редактора gedi) необходимо открыть модуль System Management.

Далее открываем Driver S7

Выбираем S7+ Driver

В настоящий момент в системе нет сконфигурированных соединений. Для создания связи с существующим контроллером нажимаем кнопку Create.

Тут необходимо указать имя для соединения и способ работы с данными либо оффлайновый проект, находящийся во вложенной папке проекта WinCC OA, либо онлайн связь с уже готовым и настроенным ПЛК. В нашем случае S7-1214 с залитой конфигурацией и программой лежит на столе рядом с ПК и доступен по протоколу TCP/IP, так что, работаем онлайн. Указываем номер только что добавленного и запушенного драйвера, это номер 2. Имя соединения далее превратиться в системную точку данных, с которой возможно работать посредством программного кода.

Далее требуется указать тип контроллера, я задам его явно S7-1200, а так же ввести ip-адрес моего ПЛК. Дополнительно, если вы еще не настраивали, требуется вызвать окно настройки сетевой карты компьютера, нажав кнопку Set PC/PG Interface. По нажатию этой кнопки откроется окно, знакомое всем, кто работал с классическим Step 7 или настраивал запуск операторской системы WinCC 7, TIA Portal WinCC и т.д. Если вы незнакомы с этим окном, то там необходимо указать ту сетевую карту, при помощи которой ваш ПК подключен к вашему ПЛК. У меня данный интерфейс давно настроен, и это окно выглядит следующим образом

Моя сетевая карта присутствует тут в трех экземплярах, с окончаниями ISO, TCPIP и TCPIP.Auto. Как правило, применяется последний вариант, TCPIP.Auto. После внесения настроек связи данное окно имеет следующий вид

Ставим галочку напротив Establish Connection и нажимаем кнопку Apply. После чего WinCC OA несколько секунд тратит на установление связи. Eсли все было сделано правильно, окно приобретает следующий вид

Нажимаем кнопку ОК, а после закрываем окно модуля System Management. Далее необходимо связаться с тэгами контроллера, которые в нем уже прописаны. Ограничимся двумя дискретными переменными и одной вещественной. Для работы с точками данных у нас есть модуль para, открываем его. В нем уже есть несколько готовых типов точек данных ExampleDP_bit и ExampleDP_float, их и будем использовать. Создадим точку данных MyBlinker типа ExampleDP_bit

Добавим к точке данных конфиг переферийный адрес.

Выбираем тип драйвера SIMATIC S7PLUS

Конфигурируем

Указываем номер драйвера 2. Указываем направление данных Input, только чтение. Указываем тип преобразования Bool. Вводим способ сбора данных Polling (непрерывный опрос).

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

В графе Reference необходимо указать имя переменной в контроллере. Поскольку ПЛК у меня сконфигурирован и на связи, я могу просматривать его тэги в режиме онлайн. Нажимаем кнопку Selection в верхней части окна.

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

Теперь ставим галочку Address active, жмем Apply и можно перейти на конфиг original и посмотреть, меняется ли значение тэга в нашей SCADA.

Да, значение тэга меняется каждую секунду

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

Сейчас значение бита FALSE, бит читается.

Изменим в ПЛК его значение на TRUE

В модуле para его значение так же изменилось

Теперь попробуем поменять его значение на FALSE и посмотрим состояние бита средствами TIA Portal. Выполнить эту операцию средствами para не удается. Скорее всего, это связано с тем, что поллинг переменной идет 10 раз в секунду, в para он обновляется так же быстро, и я просто физически не успеваю внести в Original value значение FALSE, вместо TRUE. Даже выделить значение дабл-кликом или кнопками Ctrl+A не удается. Пробуем изменить DPE кнопками на панели операторской системы. Для этого в верхнюю часть главного экрана Main я размещаю две кнопки Turn On и Turn Off и создаю скрипты для них, соответственно

dpSet("System1:Emulation.:_original.._value", 1);

и

dpSet("System1:Emulation.:_original.._value", 0);

Пробуем теперь отключить тэг.

По нажатию кнопки в модуле para значение изменилось на FALSE

Значение изменилось и в блоке данных контроллера.

Поскольку средствами скриптов операторского интерфейса удается изменить значение тэга, значит со связью все в порядке. Но, все таки, хочется иметь возможность задавать значение и через para. Я рассуждаю следующим образом. Драйвер обновляет значение сигнала принудительно 10 раз в секунду (poll time равен 100 мс). Драйвер ничего не знает про то, изменилось ли значение сигнала или нет он просто поллит заданую переменную и отдает ее в event manager. EV тоже не знает, изменился ли тэг или нет, и точно так же передает любое новое значение туда, где на них существует подписка. 100 мс это очень быстро для человеческой реакции, поэтому в модуле para я просто не успеваю вбить новое значение, оно тут же перезатирается обновленным от драйвера. Значит, необходимо настроить систему таким образом, чтобы сообщения с новым значением тэга летели адресатам только в том случае, когда значение действительно изменилось. Для этих целей на DPE необходимо разместить конфиг Smoothing. Возвращаемся в модуль para и добавляем конфиг на точку данных.

Оставляем его настройки, как есть сравнение старого значения с новым. Не забываем нажать Apply.

Переходим на конфиг original этой точки данных и наблюдаем, что метка времени тэга (как online, там original) перестала меняться, как сумасшедшая. Обращаем внимание и на то, что теперь в модуле para возможно изменить значение бита, и оно доходит на контроллера.

Теперь пропишем вещественную переменную, которая находится в ПЛК по адресу Robicon.SCADAmanSP. Для переменной в WinCC OA воспользуемся готовым типом DP ExampleDP_float. Ее создание и настройка переферийного адреса аналогична предыдущим настройкам, за исключением очевидных деталей имя, адрес, тип данных. Направление зададим, как чтение/запись. На этот раз я не выбирал тэги онлайн, а вбил символьное имя переменной непосредственно в поле ввода Reference. Не забываем добавить конфиг smoothing и для этой DP.

Делаем адрес активным и смотрим конфиг original. Видим, что связь установлена, значение с ПЛК приходит.

Изменение значения так же доходит и до контроллера.

Напоследок отобразим значение этой переменной на мнемосхеме FLAPS (не создавать же новую панель ради одной проверки). Разместим на панели элемент Textfield из палитры.

Визардом на событии Initialization создами скрипт отображения значения DPE (Display value)

Значение с ПЛК отображается

Добавим еще один скрипт при помощи визарда для возможности редактирования значения прямо с операторского экрана. Для этого нам потребуется вызвать визард на событие Command.

Изменим значение сигнала с операторской системы

До блока данных изменения долетели

Подробнее..

OPC UA для CPU S7-1200 (FW4.4). Настройка сервера

31.12.2020 20:05:20 | Автор: admin

Начиная с версии 4.4 операционной системы контроллеров линейки S7-1200 появилась возможность опрашивать их по протоколу OPC UA. В настоящий момент времени поддерживается только серверная часть (ПЛК может отвечать на запросы клиентов), клиентская часть не поддерживается.

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

В первую очередь заходим в свойства и включаем сервер OPC UA.

Не забываем так же указать в настройках, что лицензия на OPC UA была приобретена.

То есть, если не вдаваться в важные тонкости, вроде шифрования трафика и вопросов ограничения доступа, все делаем, как и для S7-1500. Чтобы продемонстрировать ошибочность этого подхода, выполним загрузку CPU прямо сейчас и подпробуем к нему подключиться. В качестве клиента OPC UA применяется та же программа, которая использовалась в примерах работы протокола для линейки S7-1500. Единственное отличие заключается в том, что при установленной на программатор Windows 10 программу-клиент мне приходится запускать с администраторскими полномочиями.

Подключимся к ПЛК.

Сколько ни крути, сколько ни ищи, но никаких переменных пользовательской программы мы сейчас не найдем. Только разного рода вспомогательная информация по серверу.

Связано это с тем, что у нас не создан серверный интерфейс, то есть, не указаны переменные, которые сервер будет отдавать клиенту. В серии S7-1500 этот интерфейс создается по-умолчанию в настройках CPU, и по умолчанию OPC UA сервер отдает все тэги, для которых проставлены разрешения к доступу по OPC UA. Посмотрим скрин-шот настройки серверы для CPU S7-1516

Для 1200ой серии необходимо найти в дереве проекта OPC UA Communications Server Interfaces и нажать там Add new server interface

Далее перетащим из правой части экрана в левую те тэги, доступ к котором необходимо предоставить по протоколу OPC UA

Выполним компиляцию и загрузим ПЛК. Теперь попробуем подключиться к OPC UA заново. Теперь у нас появился интерфейс Server interface_1 и все заданные в нем переменные.

Значение переменной успешно читается.

Подробнее..

Сервер Modbus TCP для Simatic S7-1200 S7-1500

06.01.2021 08:23:22 | Автор: admin

Первая спецификация протокола Modbus была опубликова в 1979 году. Протокол предназначен для опроса подчиненных устройств по принципу запрос-ответ. Modbus RTU (Remote Terminal Unit) работает по последовательному интерфейсу передачи данных (RS-232, RS-485, RS-422). Сегодня речь пойдет о немного измененном протоколе, Modbus TCP, работающий на прикладном уровне стека протоколов TCP/IP.

Для начала посмотрим, как настраивается (программируется, если быть точнее) серверная часть. Modbus TCP Server аналог Modbus RTU Slave, то есть, является подчиненным устройством. Это важно, не путайте. Сервер лишь отвечает на запросы, но не генерирует их.

В данном примере применяется CPU S7-1516 с версией прошивки 2.6. Серия S7-1200 программируется аналогично.

Для начала разместим в OB1 экземпляр функционального блока MB_SERVER (Instructions Communications Others MODBUS TCP).

Далее необходимо сделать три вещи. Во-первых, подать что-нибудь на вход MB_HOLD_REG. Этот входной пин экземпляра ФБ должен содержать область памяти, которая выделяется на регистры хранения (holding registers).

Небольшое отступление. В версиях библиотек Modbus TCP до 5.0 переменные дискретные входы (Discrete inputs), т.е. те двоичные переменные, которые можно только читать это непосредственно все BOOL'евые переменные из области процесса %I. Coils, катушки дискретные переменные, которые можно и читать, и записывать, это область %Q. Input Registers, входные регистры это слова данных из области %I, точнее %IW. Грубо говоря, все дискретные переменные протокола Modbus и аналоговые входа являются переменными областей памяти I или Q. Это дает возможность читать непосредственно значение входов, а так же записывать значения дискретных выходов напрямую. С моей точки зрения нелогично отдавать возможность управлять дискретными выходами какой-нибудь сторонней системе, пусть даже и теоритическую, поскольку в зависимости от построения прикладного ПО контроллера, на эти выхода будет приходить правильное с точки зрения системы значение. Для того, чтобы ограничить клиентам Modbus TCP возможность прямого обращения к выходам, в экземпляре функционального блока есть несколько переменных.

Нас интересуют все переменные, которые начинаются на IB и QB. Указав в качестве значений QB_Count, QB_Read_Count и IB_Count нули, вместо значения по умолчанию 65535, мы запрещаем полностью чтение/запись входов/выходов напрямую.

Для чтения/записи регистров хранения, в свою очередь, необходимо отдельно вручную задать область данных. Мой личный опыт показывает, что наиболее удобный способ это структура, объявленная в глобальном блоке данных со стандартным (а не оптимизированным) доступом. Я сейчас продемонстрирую, как надо, а под конец данной заметки мы пройдемся по граблям и посмотрим типичные ошибки, которые возникнут, если заполнить данное поле неправильно.

В версии библиотеки, начиная с 5.0 (требуется прошивка 2.5 для S7-1500 и 4.2 для S7-1200) можно иначе переназначать входные дискреты, катушки и прочие переменные модбас. Например завести все в битовые переменные глобального блока данных. Необходимо дополнительная конфигурация, которая описана в пункте Access to data areas in DBs instead of direct access to MODBUS addresses as of version V5.0 встроенной справки.

Итак, добавляем глобальный блок данных с неоптимизированным доступом в картинках.

Нажать Add new block

Выбрать Data block и дать ему осмысленное имя, далее нажать ОК

Вызвать свойства свежедобавленного блока данных

Найти в атрибутах блока галочку Оптимизированный доступ, снять ее, подтвердить снятие и нажать ОК

Открыть в редакторе блок данных и создать в нем отдельную структуру

Заполнить поля этой структуры. Имеет смысл сразу в коментариях делать пометки о номере(адресе) регистра хранения. Откомпилировать блок данных.

Подать созданную структуру на входной пин MB_HOLD_REG

Во-вторых, требуется создать и заполнить структуру типа TCON_IP_v4 или TCON_Configured. Данная структура содержит некоторые подробности для коммуникации контроллера. Лично я предпочитаю первый способ, он мне кажется более аскетичным, а кроме того он не требует загрузки Hardware, в отличии от второго. В связи с тем, что структура относится к настроечной части протокола Modbus, ее можно разместить в уже созданном блоке данных (хотя, никто не запрещает объявить ее, где угодно).

Добавление структуры типа TCON_IP_v4

Поле InterfaceID заполним чуть позже, а сейчас пройдемся по остальным полям.

ID внутренний идентификатор соединения. Допустимые значения от 1 до 4096. Каждое соединение (экземпляр блока MBSERVER, хотя на самом деле все немного сложнее). должно иметь свой уникальный идентификатор. Ставлю равным 1.

ConnectionType тип соединения. По умолчанию стоит 11 (0B в шестнадцатиричной системе): TCP. Его и оставляем.

ActiveEstablished оставляем false, в данном случае сервер не является инициатором связи, инициатором связи являютя клиенты.

RemoteAddress если оставить нули, то к серверу сможет подключиться любой клиент. Если задать удаленный IP-адрес конкретно, то к серверу может обратиться только один явно заданный клиент. Оставляем нули.

RemotePort оставляю ноль, не органичиваю и номер порта со стороны клиента

LocalPort номер TCP порта, по которому будет отвечать сервер. В соответствии со старой-доброй традицией (и RFC) протокол Modbus TCP работает на порту 502 (а игра Doom по порту 666, но это совсем другая история). Порт 502 я указываю явно.

В итоге получаем следующее:

Осталось задать лишь ID интерфейса. Это присвоение я делаю в программное коде, разместив network с присвоением (MOVE) до вызова блока Modbus. Идентификаторы интерфейсов уже созданы в Step 7 автоматически, необходимо лишь найти нужную переменную. В моем случае Modbus будет работать на интерфейсе X1. Его я и нахожу в списке переменных, выпадающем автоматически.

Можно так же подсмотреть название нужной переменной, зайдя в аппаратную конфигурацию, выбрав нужный интерфейс и вкладку System Constants этого интерфейса. Привожу скриншот всего экрана, чтобы было понятно, где искать.

Можно так же просто указать значение 64 для переменной "ModbusData".CONNECT_Struct.InterfaceId

Далее подаем на вход CONNECT заполненную структуру и получаем следующую программу:

И, наконец, в третьих, не забываем про переменные экземпляра MB_SERVER_DB. Я о них говорил выше, описание этих переменных можно почитать во встроенной справке. Если их не трогать, то обмен, все равно, будет работать, это уже вопрос доводки обмена и доступа ко входам и выходам.

Компилируем программу, загружаем ее в контроллер и выходим Online:

Статус 7002 не означает ошибку, он говорит о том, что соединиение устанавливается. Обязательно почитайте описание возможных значений поля STATUS, пригодится. Перед тем, как начать читать/записывать данные при помощи стороннего Modbus-клиента, дадим переменным ненулевые значения (разумеется, мои любимые число зверя и три топора).

В качестве Modbus-клиента можно использовать любой проверенный софт. Главное правильно сформировать запрос со стороны клиента. В нашем случае объявлено всего 5 регистров хранения, и если запросить 10, то сервер Modbus вернет ошибку, и будет прав. Второй немаловажный момент не забывайте про порядок байт в слове: если little endian отображать, как big endian, или наоборот, то вместо разумных чисел на экране будет ерунда. На данном скриншоте клиент настроен на опрос 5 регистров хранения, представление данных, как float, настроено переворачивание байт в словах:

Чуть выше я говорил, что дискретные выхода контроллера (точнее, биты области %Q) это и есть койлы с точки зрения протокола Modbus, и что при настройках по умолчанию клиент получит возможность как читать сигналы напрямую, так и записывать их. Давайте в этом убедимся. Для начала на модуле дискретных выходов я объявляю переменную, для дальнейшего удобства:

Нулевой бит восьмого байта выходной области. Номер 64, если считать с нуля (8 * 8 + 0 = 64). Задам в контроллере значение истина и прочитаю в Modbus-клиенте:

Вижу значение истина (читаю один койл с начальным смещением 64). Изменю это значение на ложь со стороны modbus:

Значение, разумеется, так же изменилось и в Step 7, и в контроллере, и на выходе модуля (это одно и то же):

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

После этого изменения в блоке данных (прямо в online, без перезаливки и перезагрузки контроллера) клиент протокола modbus в ответ на требование записи катушки показал табличку Illegal data address, а именно такое сообщение и вернул сервер. Дополнительную информацию читаем в справке: Restriction of read access to process images as of version V5.0.

Теперь давайте посмотрим, что происходит при некорректном назначении области памяти от регистров хранения. В первую очередь достаем встроенную справку Step 7 и читаем:

В качестве регистров хранения применять глобальный блок данных с оптимизированным доступом или битовую область. И вот тут очень интересно. Потому что справка в части MBHOLDREG parameter выглядит следующим образом:

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

Эксперимент 1. Регистры хранения это структура в блоке данных с оптимизированным доступом (почти, как сделано в этом примере). В этом случае получаем ошибку 8187 : The MBHOLD_REG parameter has an invalid pointer. Data area is too small.

Эксперимент 2. Массив переменных типа WORD, объявленный в оптимизированном блоке данных. Работает, со стороны клиента переменные меняются, ошибок нет.

Эксперимент 3. Меркерная область. Работает, с клиента удалось внести значения, ошибок нет.

С моей точки зрения, в документации недостаточно ясно. Должно быть написано используйте блок данных со стандартным доступом или битовую (меркерную) память, а не оптимизированным доступом. В случае оптимизированного доступа вполне подойдут массивы слов. И с моей точки зрения самым удобным способом является способ, описанный в изначальном примере. Эксперимент 2 в принципе тоже работоспособен (и тому есть объяснение), но с моей точки зрения неудобен для работы.

В следующий раз мы займемся клиентом Modbus TCP.

Подробнее..

Клиент Modbus TCP для Simatic S7-1200 S7-1500

07.01.2021 14:19:53 | Автор: admin

Продолжаем тему программирования протокола Modbus TCP на контроллерах Simatic S7-1500. В прошлый раз речь шла о серверной части, сегодня опишем клиентскую. Клиент Modbus TCP это узел, который генерирует запросы к серверу, т.е. запрашивает данные и передает уставки/команды. В терминологии Modbus RTU это мастер, ведущее устройство. В отличии от RTU, в протоколе TCP может быть несколько мастеров (правильно клиентов).

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

По этой причине имеет смысл программировать клиента на языке SCL (ST в терминологии МЭК 61131-3) и завернуть всю обработку в функциональный блок. Для большей реалистичности в данном примере контроллер будет общаться с двумя серверами Modbus TCP с несколькими запросами к каждому.

В первую очередь создадим функциональный блок ModbusClient на языке SCL и добавим вызов его экземпляра в OB1.

Далее в области STAT переменных функционального блока необходимо прописать две структуры TCON_IP_v4. Зачем две? Затем, что у нас два соединения с двумя разными серверами. Фактически у нас два разных соединения (connection) и каждое необходимо описать. Как я говорил ранее, возможно применить и конфигурируемые соединения, но в данном примере они не используются.

Объявлено две структуры для связи с двумя серверами

Далее необходимо заполнить обе структуры. Это возможно сделать как программным кодом, так и просто занести необходимые значения данных. Пойдем по второму пути.

Первое поле, InterfaceId. Идентификатор интерфейса (или сетевой карты) нашего контроллера. Клиент Modbus работает на интерфейсе 1 контроллера, смотрим его ID в конфигурации устройства.

Его ID равен 64. Обращаю внимание, что нужен идентификатор именно интерфейса, а не его портов.

Следующее поле структуры, ID. Это идентификатор соединения. Не путать с идентификатором интерфейса. Не путать с номером модбас-устройства. Это некий внутренний логический номер коннекшена, который программист назначает самостоятельно в диапазоне от 1 до 4096. У каждого коннекшена должен быть свой уникальный идентификатор. Ответственность за корректное присвоение целиком на ваших плечах. Назначаем ID = 1 и едем дальше.

Далее идет тип соединения, ConnectionType TCP или UDP. По умолчанию значение этого поля 0x0B в hex или 11 в dec. Оставляем по умолчанию, TCP.

Флаг ActiveEstablished. Выставляем его в истину. В случае клиента именно наша сторона должна инициировать соединение.

RemoteAddress. Тут пишем IP-адрес нашего первого сервера. Пусть будет 192.168.43.100.

RemotePort. Номер порта, по которому сервер Modbus TCP будет отвечать на наши запросы. По умолчанию все сервера этого протокола должны слушать порт 502.

LocalPort. Оставляем равным нулю.

В итоге, описание соединения с первым сервером выглядит следующим образом.

Описание соединения с первым сервером

Вторая структура заполняется аналогично. Разумеется, пишем другой ID и другой IP адрес. В итоге получаем.

Сделаем первые робкие шаги и попробуем прочитать один регистр хранения с одного сервера. Для начала надо перетащить ФБ MB_CLIENT из библиотеки в программу.

После перетаскивания появится диалогое окно о создании экземпляра. Выбираем мульти-экземпляр и немного корректируем имя.

Выбираем мультиэкзепляр

Промежуточный итог

Приведем вызов в человеческий вид

Кратко пройдемся по параметрам этого вызова. Подробное описание в нашей горяч0 любимой документации, которую мало кто, почему-то, читает.

REQ активирует выполнение опроса. Пока REQ = TRUE, клиент проводит чтение данных с сервера или запись данных на сервер.

DISCONNECT разорвать соединение

MB_MODE режим работы клиента. В совокупности со входом MB_DATA_ADDR оказывает влияние на используемую функций Modbus TCP. Возможные значения описаны в документации. Для чтения одного или нескольких регистров хранения значение MODE должно быть равно 0.

MB_DATA_ADDR указывает адрес в адресном пространстве протокола Modbus TCP. Значение нашего примера 40001 первый регистр хранения

MB_DATA_LEN количество читаемых или записываемых величин. В нашем случае единица. В итоге все три указанных выше параметра означают читать один регистр хранения начиная с адреса 40001

MB_DATA_PTR переменная или структура данных, куда мы записываем прочитанное значение. Переменная может быть в любом блоке данных, я объявил локальную статическую переменную SingleHR типа INT, размер которой равен 2 байтам и совпадает с размером одного регистра хранения Modbus. При несовпадении размерности читаемой области данных с локальным хранилищем вызов функционального блока завершится ошибкой.

CONNECT уже созданная нами структура типа TCON_IP_V4

Остается только запустить на ноутбуке сервер Modbus, скомпилировать и загрузить программу контроллера, и не получить ничего. Сервер не отвечает. Ответов нет. Вообще ничего нет. Ничего. По буквам Николай, Илья, Харитон ( Остапа понесло ). Для того, чтобы уточнить ошибку, необходимо доработать программу следующим образом.

Дело в том, что флаги успешного (DONE) или неуспешного (ERROR) вызова блока живут всего один цикл сканирования программы. Естественно, невозможно заметить настолько быстрое изменение. Поэтому по флагу ошибки я копирую статус вызова в отдельную переменную. А по флагу успешного выполнения обнуляю статус.

Кроме того, добавлены флаги управления запросом и соединением.

Немного прокоментирую ошибку, которая возникла у меня. В моем случае код ошибки был 80C6. В описании на блок MB_CLIENT этой ошибки нет, поэтому я вбил код ошибки в поиск справочной системы и нашел ссылку на функцию TCON (так же при неоднозначных ошибках можно искать и среди TSEND, TRECEIVE и прочих похожих блоках). Описание: The connection partner cannot be reached (network error). Ошибки сети. Ответ был очень прост и заключался в том, что программа-иммитатор Modbus не была прописана в разрешениях встроенного в Windows Firewall. Точнее, разрешение на ее работу было настроено только на частные сети, а интерфейс программатора был назначен в качестве публичной сети. Это лишний раз подчеркивает, что техника виновата в последнюю очередь, а чаще всего ошибку надо искать в радиусе закругления рук и в соответствии этого радиуса ГОСТам. После изменения настроек брэндмауэра ОС обмен заработал.

Усложним теперь задачу самую малость, и попробуем считать с сервера одну вещественную переменную. Одна вещественная переменная (REAL) это 4 байта. Или 2 регистра. Итого, в вызове я увеличил количество читаемых регистров до 2, и изменил указатель на прочитанные данные. Этот указатель все еще весьма прост это внутренняя статическая переменная типа REAL (дальше будет интереснее).

Хотелось бы обратить внимание на одну важную деталь. Если прогружать измененное прикладное ПО на горячую, с переинициализацией переменных нашего функционального блока ModbusClient в то время, когда контроллер ведет опрос сервера Modbus, то обмен может прекратиться, и на выходе блока будет стоять статус 80A3. Связано это, разумеется, со вмешательством во внутренние структуры обмена (из-за переинициализации всего блока). В моем случае это приводило к полной невозможности коммуникаций до рестарта контроллера переключателем старт/стоп. Я намеренно изменил сейчас функциональный блок (добавил еще одну переменную), чтобы продемонстрировать эту ошибку:

После стоп/старта контроллера и поднятия флага Srv1Req обмен успешно возобновляется. Чтобы не допустить такого зависания обмена (как ни крути, это частный случай) необходимо поднимать флаг Srv1Disconnect, проводить изменения в переменных функционального блока (имеются в виду именно переменные, т.е. интерфейсная часть блока, а не сам программный код), выполнять загрузку с переинициализацией, а потом вручную возобновлять обмен. Помните же, что флаги REQ и DISCONNECT у нас подключены к переменным, и этими переменными можно управлять, как вручную, так и посредством программного кода.

Пока мы не продвинулись дальше, хочется продемонстрировать описанный выше случай с разноконечностью (little-endian и big-endian) данных. В моем примере сервер modbus держит в двух регистрах хранения вещественную переменную со значением 0.666. Наш клиент Modbus вместо этого числа читает 1.663175E+38, что сильно отличается от нужного (увы, вне зависимости от того, покупаем мы или продаем). Связано это, конечно же, с порядком байт в словах и порядком самых слов в двойном слове. Пробуем разрешить ситуацию. Доработаем программный код следующим образом.

Инструкция SWAP меняет порядок байт (конкретно тут в двойном слове). Но в данном случае она не помогает, на выходе (в переменной Data.Test) все еще находится неправильное значение. Скорее всего, это означает, что сервер отдает регистры в неправильном порядке, а байты в правильном, то есть информация перемешалась. Радует, что байта всего 4 и составить их в нужном порядке дело техники.

Итак, снова модифицируем запрос. Складываем результат чтения двух регистров в массив из 4 байт и объявляем еще один массив из 4 байт, которым и будем манипулировать.

Функция Deserialize складывает массив из байт в какое-либо конкретное значение, в данном случае в вещественную переменную. Потребовалась всего одна итерация, чтобы получить корректное значение переменной с сервера Modbus.

Перед тем, как вернуться к штатной рутинной работе, необходимо рассказать про одну очень частую ошибку, возникающую при обмене по протоколу Modbus TCP, а именно указание или неуказание адреса (номера) подчиненного устройства (сервера). В протоколе Modbus RTU все слейвы имеют свой уникальный адрес в сети. Мастер, формируя запрос, указывает адрес слейва, однобайтовое поле в заголовке пакета. Unit ID, Device ID, адрес неважно, как называется, смысл один. В протоколе Modbus TCP адресом абонентского устройства является его IP-адрес. Тем не менее, поле Device ID в заголовке сохранилось. И это часто вносит путаницу, непонимание и ошибки. Дело в том, что в соответствии со спецификациями обычный сервер Modbus TCP должен игнорировать поле ID в запросе к нему. Unit ID учитывается лишь для устройств, преобразующих Modbus RTU в Modbus TCP (гейты, шлюзы, конвертеры протоколов и так далее). На практике же многие сервера Modbus проверяют и однобайтовый адрес Unit ID. При несовпадении своего адреса и адреса в запросе, сервер в этом случае чаще всего не отправляют никакую ответную телеграмму, и клиент возвращает ошибку опроса. Если на практике вы столкнетесь с таким странным поведением, то откройте экземпляр функционалного блока клиента Modbus и поищите в его статических переменных байтовую величину MB_Unit_ID. Это и есть адрес подчиненного устройства, т.е. сервера Modbus. По умолчанию его значение равно 0xFF или 255. Нормальные сервера его игнорируют, достаточно уже самого факта установления соединения по протоколу TCP/IP. Если же попался ненормальный, то поставьте тут вручную Unit ID вашего устройства. Связь должна установиться.

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

Закончив описание подводных камней, вернемя к изначальной задаче. Будем считывать с первого сервера 3 вещественных переменных (6 регистров), начиная с 40001, и записывать одну (начальный адрес 40011). Предполагаем, что порядок слов и байт правильный. Шесть регистров (шесть слов данных) и три вещественных переменных. Можно, конечно, просто в лоб читать информацию в локальный массив байт, а потом средствами дополнительной обработки представлять их в виде трех вещественных величин (тем же Deserialize, например), но не стоит создавать себе лишнюю работу. Гораздо удобнее будет сразу разложить читаемую информацию в собственную структуру. В блоке данных Data я создаю структуру, состоящую из трех полей типа REAL.

Обращаю внимание, что блок данных Data должен быть стандартным или неоптимизированным, в противном случае вы будете получать ошибку опроса, к примеру 818B.

Разумеется, содержание этой структуры полностью зависит от того, в каком порядке и в какой форме отдаются данные от сервера.

Программа обретает следующий вид, и данные успешно читаются.

0.5, 0.7, 0.33 (с) ВИА Несчастный случай

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

В итоге получаем вот такую программу.

Переменная, на основании который выбирается опрос, называется у меня Server1Query (будет еще Server2Query). Выбор запроса выполнется в операторе CASE. Номер запроса меняется на следующий лишь в случае успешного или неуспешего выполнения текущего опроса.

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

Со вторым сервером у нас настроено отдельное соединение, есть отдельный экземпляр функционального блока modbus. Его мы можем вызывать одновременно с коммуникациями первого сервера, поэтому в общей программе есть два оператора CASE, которые работают независимо друг от друга.

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

Тем не менее, описание работы с протоколом Modbus TCP на этом я заканчиваю. В следующий раз посмотрим на программирование протокола Modbus RTU.

Подробнее..

Программирование Modbus RTU Master на примере Simatic S7-1200 и ПЧ Sinamics V20

08.01.2021 22:18:59 | Автор: admin

Давно хотел рассказать про тонкости программирования обмена по протоколу Modbus RTU в случае, когда контроллер (в нашем случае S7-1214) выступает RTU Master'ом. Недавно меня попросили помочь с обменом между ПЛК и частотным преобразователем Sinamics V20, ну и почему бы не написать заодно заметку, постаравшись приблизить решение задачи к боевым условиям.

Собственно говоря, сами немцы эту тему давно осветили:

SINAMICS V: Speed Control of a V20 with S7-1200 (TIA Portal) via USS protocol/MODBUS RTU with HMI

https://support.industry.siemens.com/cs/ru/ru/view/63696870/en

Смотрите этот пример, он сделан очень толково, с визуализацией,диалогами и квестамии возможностью расширить прикладную программу до опроса множества ПЧ V20 по нескольким интерфейсам (S7-1200 позволяет установить в свою корзину до 4 портов RS-485/422). Пример сделан очень хорошо и очень педантично. Вопросов коммуникаций по протоколу Modbus TCP я уже касался ранее, они есть на хабре.

Поэтому некоторые нюансы я повторно объяснять не буду, просто сразу напишу, как стоит делать правильно и удобно с моей точки зрения конкретно в случае опроса V20. Первоначальная настройка преобразователя частоты описана в документации, в том числе и в сопутствующей документации к вышеуказанному примеру. Вынесем лишь важные для нас пункты в качестве вводных.

Адрес подчиненного устройства модбас в сети: 1

Параметры связи: 9600 8-Even-1

Регистры хранения подчиненного устройства для чтения:

40110 ZSW Слово состояния

40111 HIW Текущая скорость

Регистры хранения для записи:

40100 STW Слово управления

40101 HSW Задание скорости

Параметр частотника Telegram off time (ms) P2014[0] рекомендую оставить по умолчанию, равным в 2000 мс (2 секунды), хоть пример и рекомендует снизить эту величину до 130 мс. Конкретно к протоколу Modbus это замечание не относится, разумеется, просто у меня при таймауте в 130 мс, ПЧ терял связь и выдавал ошибку Fault 72.

С частотником разобрались. Теперь о моей конфигурации ПЛК. Это S7-1214 с коммуникационным модулем 1241 под RS-485/422:

Среда программирования Step 7 V15.1 Update 4, версия прошивки CPU 4.3.

Итак, приступим. Для опроса подчиненных устройств с контроллера Simatic нам необходимо применить два функциональных блока: Modbus_Comm_Load (единовременно, только для конфигурации коммуникационного процессора) и Modbus_Master (циклически для чтения и/или записи регистров/катушек). Поэтому в программе экземпляр FB Modbus_Comm_Load у нас будет встречаться только один раз, а экземпляр Modbus_Master несколько раз, но с разными входными параметрами, в зависимости от адреса подчиненного устройства, типа читаемых данных и их количества, а так же направления передачи данных (чтение или запись). Обращаю ваше внимание, что для одного коммуникационного процессора (а их в системе может быть очень много) у вас не может быть больше одного экземпляра каждого блока данных.

С моей точки зрения весь обмен удобнее завернуть в один внешний функциональный блок, а сам блок, с учетом необходимости разбирать данные, реализовать на текстовом языке SCL. Поэтому создаем в проекте функциональный блок с именем ModbusMasterV20 на языке SCL. Сразу после создания открываем его свойства и снимаем настройку оптимизированный доступ, т.е. используем стандартный доступ. Личный опыт показал, что использование оптимизированного доступа рано или поздно приведет к ошибкам работы блока Modbus_Master и невозможности обмена. Это связано с порядком, в котором переменные идут в объявленной структуре данных, при стандартном доступе порядок соответствует заданному в программе, при оптимизированном система сама раскидывает переменные, как сочтет нужным.

Объявляем следующие входные переменные

Init (Bool) инициализация коммуникационного процессора, ее необходимо выполнить один раз перед началом обмена

PORT (PORT) аппаратный идентификтор коммуникационного процессора

BAUD (UDINT) скорость обмена по порту

STOP_BITS (USINT) количество стоповых бит кадра

PARITY (USINT) четность, где 0 нет четности, 1 odd, нечет, 2 even, чет

В статической области переменных так же прописываем переменную с именем Step и типом UInt, она отвечает за номер опроса или шаг работы алгоритма

Так же в статической области объявляем экземпляры ФБ для работы по протоколу Modbus RTU

Строки программы, отвечающие за инициализацию обмена.

По флагу инициализации выставляем номер шага 1. Следующие строчки очень важны для работы

#instModbusCommLoad.MODE := 4; //для линии RS-485 должна быть 4!

#instModbusCommLoad.STOPBITS := #STOP_BITS;

Тут мы задаем значения статических переменных экземпляра ФБ Modbus_Comm_Load, которые отвечают за физику передачи. Не понимаю, почему немцы поместили эти важные конфигурационные параметры в статическую область, а не в область входов. Дело в том, что они (переменные) все описаны во встроенной справке. Беда лишь в том, что большинстволенивых жопновичков до этого пункта справку не читает, а потом тратят несколько часов, а то и дней, пока не найдут ответ. А справка-то, вот она:

Переменная MODE отвечает за режим, в котором будет работать коммуникационный процессор. Как видно из справки, для RS-485 надо явно выставить 4. Значение по умолчанию 0, от этого большинство ошибок у программистов.

STOP_BITS количество стоповых бит.

Далее следует вызов блока настройки коммуникационного интерфейса Modbus_Comm_Load. Про параметр PORT (аппаратный идентификатор) будет рассказано чуть ниже. Параметры BAUD и PARITY скорость и четность приходят на вход внешнего блока данных, куда мы и завернули весь обмен. А вот параметр MB_DB интересен. На этот вход надо подать структуру типа P2P_MB_BASE, которая находится в области статических переменных экземпляра функционального блока Modbus_Master. Этот экземпляр в нашем большом функциональном блоке уже объявлен, привожу скриншот:

Следующая часть: функциональный блок приступает к циклическому обмену.

Я сразу заворачиваю обмен в CASE, чтобы не переписывать код в дальнейшем, но пока мы ограничимся чтением слова состояния и скорости ПЧ, т.е. прочитаем два регистра хранения.

Давайте посмотрим на вызов блока Modbus Master повнимательнее:

#instModbusMaster(REQ := TRUE,MB_ADDR := 1,MODE := 0,DATA_ADDR := 40110,DATA_LEN := 2,DATA_PTR := #ZSWHIW);

Входной параметр REQ включить опрос. Пока на входе TRUE, он выполняется, если FALSE не выполняется. Нет необходимости подавать положительный фронт на этот вход самостоятельно (в отличии от работы Modbus RTU в системах S7-300/S7-400), поэтому я просто даю TRUE константой

MB_ADDR адрес подчиненного устройства Modbus RTU. В моем случае адрес частотника = 1.

MODE направление передачи данных, 0 чтение, 1 запись

DATA_ADDR адрес интересуемых нас данных. В моем случае необходимо прочитать два регистра хранения (поэтому первая цифра4), начиная со 110го. В протоколе Modbus (что RTU, что TCP) очень часто возникает путаница в понятиях адрес и номер. И очень часто производитель оборудования эту путаницу добавляет в свою систему. Вот смотрите. Мы должны прочитать 2 регистра, начиная с адреса 40110. Для чтения регистров хранения в протоколе Modbus используется функция с номером 3. Именно 3 будет передаваться в телеграмме Modbus. А в качестве адреса в телеграмме будет передаваться не 40110, а 109. Связано это с тем, что код функции уже содержит описание области данных. А в самой телеграмме мы передаем не адрес, а номер требуемого регистра или катушки. И эта нумерация идет не с единицы, а с нуля. Сейчас я работаю именно с адресами и режимом (чтении или запись), поэтому мне достаточно указать то, что я нашел в документации. Если же в вашем устройстве будет указано входной регистр номер 0 содержит текущий статус устройства, то вам на вход DATA_ADDR необходимо будет подать 30001. Так же имейте в виду, что из-за частой путаницы с номерами и адресами, иногда эта адресация съезжает на единицу, поэтому не бойтесь экспериментировать. Если вместо полезных данных по запросу 16ого регистра вам прилетает полная чехарда, не имеющая ничего общего с документацией, прочитайте 15ый регистр. Не помогло? Опрашивайте 17ый. Более подробно с материалом необходимо ознакомиться опять же во встроенной справке.

DATA_LEN количество читаемых регистров, их 2

DATA_PTR указатель на область памяти, куда необходимо положить результат чтения регистров. Собственно, это те данные, которые мы прочитали и необходимо подсказать функциональному блоку, куда эти данные надо записать. С моей точки зрения самый удобный способ в этом случае это объявить в области STAT неименованную структуру должного размера. Поля структуры мы объявляем, в зависимости от самих читаемых данных, ведь это могут быть и наборы бит, и вещественные числа (расположенные в двух соседних регистрах). И нам еще повезет, если порядок байт в слове и слов в двойных словах контроллера и подчиненного устройства совпадут, иначе нам еще потребуется осуществить сдвиги байт/слов.

В данном случае я счел уместным объявить структуру из двух слов и скормить ее на вход FB:

, где

ZSW слово состояния (так оно называется в документации на ПЧ)

HIW скорость вращения двигателя

После вызова блока мастера, необходимо проанализировать успех или неуспех его выполнения. В принципе, на этом скриншоте в комментариях уже все написано:

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

Пока оставляем прием данных без обработки, компилируем и грузим программу, смотрим на результат. Кстати, обращаю еще внимание на один факт. Поскольку мы работаем, завернув системные вызовы в свой функциональный блок, то любое изменение своего ФБ с последующей загрузкой ПЛК, будет нарушать обмен в связи с переинициализацией экземпляра нашего ФБ. Например, будет уходить в ноль значение шага обмена. Да и внутренние статические переменные коммуникационных вызовов тоже пострадают. Самый простой способ стоп и старт контроллера. В боевом проекте это опасно, поэтому там на вход Init я бы подал еще одну переменную и поднимал ее самостоятельно после изменений в коммуникациях. Пока же боремся с остановом обмена простым стоп-стартом ПЛК.

Добавляем вызов нашего функционального блока в OB1 и грузим CPU

Переменная FirstScan имеет значение истина при первом цикле выполнения программы OB. Она назначается операционной системой ПЛК автоматически, ее применение настраивается в свойствах CPU.

Port. Это значение смотрим в проекте Step 7, аппаратная конфигурация:

Остальные параметры касаются скорости, четности и количества стоповых бит. Загружаем контроллер и смотрим, что нам приходит в ответ на единственный циклический запрос, открыв экземпляр нашего ФБ

В слове состояния что-то есть, скорость равна нулю. Открываем документацию и смотрим состав слова состояния ZSW:

Low enabled в примечаниях означает инверсию. К примеру, бит 15, перегрузка частотника, возникает, когда этот бит равен 0, а в нормальном состоянии приходит значение 1. Посмотрим на это слово состояния в watch table и посмотрим, какие его биты выставлены, а какие нет, оценим общее состояние ПЧ:

Тут нам везет, порядок байт в словах совпадают. Если вкратце, то видно, что ПЧ не готов, не включен, и сейчас активен сигнал аварии (fault, бит 3).

Далее я попытался разложить слово состояния в биты состояния, заменив WORD на структуру из бит, но что-то явно пошло не так.

Если посмотреть внимательно, то в таком представлении нулевой и первый байты явно не на своих местах. В общем, вопрос порядка следования информации в зависимости от того или иного представления он всегда важный и требует вдумчивости. Получил на этом этапе облом, я решаю вернуться к хранению внутри нашего ФБ только сырых данных, а удобочитаемый для человека формат представления информации перенести куда-нибудь во внешний глобальный блок. Добавляю в проект блок данных DataV20:

После чего задумываюсь, убираю из имен переменных окончание Inv и дописываю функциональный блок:

Теперь в глобалном блоке данных у нас находятся статусные биты преобразователя частоты без какой-либо инверсии:

Думаю, что сразу в блок данных надо вписать переменную типа Real, которая будет содержать текущие обороты двигателя. Текущие обороты приходят от ПЧ в виде определенного численного кода, и мы вольны трактовать этот код, как нам удобнее. Допустим, что хочу трактовать этот код, как Герцы, поданные на двигатель.

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

Обратимся к документации и посмотрим состав слова управления частотным преобразователем:

Знаете, мне, откровенно говоря, лень писать все эти переменные в глобальном интерфейсном блоке данных. В моей практике управление простыми технологическими процессами с применением преобразователей частоты ограничивалось командами включить, квитировать аварию и дать задание скорости. Поэтому, пойдем на поводу моей лени и в блоке данных V20Data пропишем всего лишь бит включить, бит квитировать и задание частоты в формате Real.

Изменю алгоритм на шаге 1, при успешном или неуспешном завершении опроса сделаю переход на шаг 2.

Добавим еще локальную структуру ФБ, которая содержит слово управления и слово задания скорости:

Дорабатываю программу обмена. Не забываем, что при изменении переменных функционального блока, после загрузки изменений в ПЛК происходит его переинициализация, посему надо выполнять стоп/старт CPU.

Параметры функционального блока модбас в данном случае отличаются от первого вызова. Разумеется, у нас тут другой адрес регистра. А так же отличается режим (MODE), он равен 1, так как в данном случае данные не читаются с частотника, а записываются в него. Разумеется, указатель на область данных так же другой.

Обратите внимание, что некоторые биты слова управления я принудительно выставляю в истину, другие в ложь. Всего два бита управления (включить и квитировать) доступны для внешней программы. Необходимое значение бит управления я вычитал в документации примера. Разумеется, это указано и в документации на сам преобразователь частоты. Изучая исходный пример, я обратил внимание, что если частотнику отдавать пустое (все биты выставлены в ноль) слово управления, то это подчиненное устройство модбас возвращает ошибку Invalid data. Однако, в этом примере я пробовал слать полностью пустое слово управления, и V20 принимал его. Однако, некоторые биты управления, все равно, должны быть установлены. К примеру, если снять бит Control by PLC, то запускаться ПЧ не будет. RTFM, как говорится!

Теперь пора перейти к регистру, который отвечает за задание скорости (ну и сразу же к регистру, который отображает текущую скорость). Из исходного примера я понял, что этот регистр меняет свое значение в пределах от 0 до 16384. Это же мельком нашел и в документации. Пока не будем делать никаких переводов величин, и зададим ПЧ максимальную скорость жестко прямо в программном коде.

Откроем наш блок данных DataV20 и выставим команду пуск:

V20 запустился и работает, судя по индикации своего экранчика, на максимальной скорости, т.е. на 50 Гц. Давайте посмотрим еще сырые данные его скорости, которые приходит по modbus.

Значит, пришло время доработать шаг 1 обмена (перевести коды скорости в герцы), ну и шаг 2 в части обратного преобразования, герцы в численное значение задания скорости. Математика самая простая, без проверок на достоверность и выход за диапазон, хотя все это не помешает.

После загрузки изменений откроем блок данных DataV20 и поуправляем частотником из него.

Даем задание 25 Гц, даем пуск и наблюдаем за появлением сигнала Running и текущей скоростью.

Все регистры, которые можно считать с V20, описаны в документе по ссылке

https://support.industry.siemens.com/cs/attachments/109768394/V20opinstr0419en-US.pdf?download=true

Судя по описанию регистров, есть и другие способы управления, но нас сейчас интересует не это. В моей практике с пребразователей частоты еще частенько просили считать ток(и), напряжение(я), мощность, детализацию ошибок и т.д. Давайте последуем этой старой-доброй традиции и считаем дополнительно вот эти параметры, а так же переведем их в понятное представление:

Параметры, разумеется, могут быть любыми, но предположим, что заказчику очень хочется именно эти. Я не буду подробно описывать процесс программирования и сразу покажу результат.

В принципе, мотор маленький, крутится без нагрузки, поэтому значения похожи на достоверные. Тем не менее, задача стоит в демонстрации считывания данных, поэтому будем считать наличие хоть каких-либо цифры за огромный технологический прорыв. Итак, вы уже заметили, что я добавил читаемые параметры в блок данных DataV20. Дополнительно был доработан функциональный блок коммуникаций:

Читаются (mode = 0) четыре регистра хранения по адресу 40025. Результат помещается во внутренний статический массив [0..4] of WORD. Далее эти слова переводятся в формат Real и помещаются во внешний блок данных в результате несложных преобразований.

Ну, и напоследок остается проанализировать качество связи. Ведь не зря же на каждом шаге после выполнения ФБ Modbus_Master смотрю его флаги DONE или Error (кстати, эти флаги имеют значение истина только на протяжении одного вызова после успешного или неуспешного выполнения запросы, в остальное время ложь). Для этого я объявил массив из булевых переменных

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

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

Подробнее..

О несомненной пользе применения современного оборудования Simatic

20.01.2021 12:12:03 | Автор: admin

Году эдак в 2011 я на правах фрилансера написал и внедрил ПО для управления водонапорной станцией. По итогам система получилась распределенной и даже немного сложной, но начиналось все с локальной станции управления одной насосной установкой.

Проектная организация заложила CPU Simatic S7-313C. Аппаратный релиз был еще с 64 килобайтами рабочей памяти, тогда как актуальные сегодня версии оборудованы аж 128 килобайтами. Контроллер управлял двумя задвижками с приводами Auma Matic AM01.1 по профибасу и, по профибасу же, частотным преобразователем Robicon (модель не помню, здоровенная хреновина как минимум в мегаватт полезной мощности).

То есть, тут у нас технологически несложная насосная установка - задвижка на напоре, задвижка на всасе, датчики давления, насос, обвязанный датчиками вибрации и температуры, вакуумный выключатель для частотника, лампочки/кнопочки на шкафу, панель оператора.

Неожиданно мне стало интересно, как это будет выглядеть на новой линейке Simatic? Влезет ли прикладная программа, легко ли она портируется? Сколько стоит решение - внедренное ранее и выбранное мной сейчас?

С моей точки зрения такую несложную систему надо сегодня делать на S7-1200, поэтому она и была выбрана в качестве целевой системы.

Портировать прикладную программу оказалось несложно. Повозиться пришлось лишь в нескольких местах. Во-первых, в классическом Step 7 (версии 5.5 SP4) провел замену оборудования до актуальных заказных номеров. Это Step 7 знает всё своё железо с момента выхода в свет. TIA Portal ограничен более-менее актуальными релизами. А мигрировать прикладное ПО со старого Степа на новый решил полностью, включая аппаратную конфигурацию.

Второй источник боли - это прикладная программа в части вставок кода на языке STL. Дело в том, что S7-1200 не поддержкивает STL от слова вообще. А я его применял. Для копирования переменных, для несложных рассчетов и т.д. Ну, на не LADe же городить ветки с блоком MOVE. К слову, исходная программа написана на языке LAD с этими самыми вставками на STL.

В конечном итоге это превратилось в программу на языке LAD с нетворками на языке SCL.

Третье затруднение - это системные вызовы для обмена ЛСУ с вышестоящим мастер-контроллером, как профинет-подчиненнное устройство. В S7-300 через CP 343-1 LEAN для этого применялись отдельные функции. Тут я не стал заморачиваться и внедрил обмен через область процесса. Удобнее. А для контроля качества связи в обоих направлениях соорудил счетчик. В общем, никаких системных функций.

Работа с профибас-подчиненными устройствами осталась такой же. То есть, через функции DPRD_DAT и DPWR_DAT. В принципе, ничего не мешало использовать ту же область процесса. Только вот как контролировать качество связи - я при таком подходе не придумал. Поэтому оставил, как было.

Ну, а теперь немного картинок. И, конечно же, про деньги.

Так выглядит исходная (и внедренная) конфигурацияТак выглядит исходная (и внедренная) конфигурация

Большинство дискретных сигналов управления влазило на блок CPU, но, все равно, не хватало, поэтому проектировщики добавили модули DI16 и DO16. DI16 и DO16 оказалось слишком много. По хорошему эти два модуля можно спокойно заменить на комбинированный DIO8/8. На аналоговых модулях AI8 висят сигналы давления и обвязка насоса.

Новая сборка на базе S7-1214Новая сборка на базе S7-1214

Все сигналы управления влезли вот в такой вариант. S7-1214 это минимальной возможный подходящий CPU. И его хватает с запасом. С учетом того, что технологическое оборудование работает по шине Profibus, пришлось доставлять отдельно CM1243-5, а это значительно удорожает спецификацию. Увы, но без него никак, ибо я мигрирую систему управления, но не всю технологию.

Пришло время посмотреть, что получилось с прикладной программой.

Исходный вариант:

Итого, примерно 24 килобайта рабочей памяти и 35 килобайт загрузочной.

Портированный на S7-1200 вариант:

14 килобайт рабочей памяти. Почти в два раза меньше. Частично это достигается отказом от функций PNIOSEND, PNIOECV. Частично некоторой оптимизацией кода. Не смог я просто скопировать наработки семилетней давности, ряд вещей переработал. Напоминаю, что сейчас сравниваются программы с абсолютно идентичным функционалом. И в новой линейке для того же функционала требуется меньше памяти. Удивительно, я ожидал обратного.

А теперь, конечно же, про деньги.

Стоимость исходной конфигурацииСтоимость исходной конфигурации

Обратите внимание, что тут я для оптимизации заменил два дискретных модуля DI16 и DO16 на один комбинированный DI/DO 8/8. А так же выбрал карту памяти MMC на 64 кБ в целях удешевления системы (разумный подход к делу требует, чтобы объем загрузочной памяти, т.н. флешки, был больше рабочей памяти). Округляя, получаем стоимость 3400 ойро без НДС.

Интересно, а что же получилось на S7-1200?

Стомость новой конфигурацииСтомость новой конфигурации

Даже с учетом применения дорогого модуля Profibus DP Master, даже с учетом применения дин-рейки за 30 евро, сборка получилась почти в два раза дешевле, 1630 евро. При том же функционале.

Подробнее..

Мобильная Установка Доказательства Актуальности Контроля Измерений. Часть 2. Нам срочно нужна связь!

01.03.2021 10:14:41 | Автор: admin

-Нам срочно нужна связь

-Половую предложить не могу!

(с) Звездные Войны. Буря в стакане. Студия "Божья Искра" (ст. о/у Goblin)

Собранная и запрограммированная в конце прошлого года Мобильная Установка Доказательства Контроля Измерений (аббревиатуру сотавите сами) периодически включалась, строила графики давления и снова выключалась. Все бы хорошо, да применяемая там панель серии Basic позволяет хранить треды, в лучшем случае, дней за пять, чего маловато. Как я уже говорил ранее, применять панель Unified Comfort, в которой таких ограничений нет, совершенно не с руки. Панель Unified не является мобильной, вся установка - это набор модулей на дин-рейке (корпуса нет, да уже и не будет), в общем - рассыпуха на коленке. Сейчас она "живет" в ванной комнате, заводить кабель в ванную - тоже не с руки... это либо оставлять дверь открытой, либо долбить стену. А ради чего?

В общем, единственный выход - это беспроводные сети, благо, WiFi есть у каждого или почти у каждого.

Правда, я уже с трудом понимаю, зачем я все это продолжаю, так как за последние два месяца я провел собрание собстенников в доме, запустил голосование, по результатам которого уже скоро тут начнет работать другая управляющая комнания, а не эти дерзские бездельники из "Жилсервисуюта" (а в Госжилинспеции - тоже наглые бездельники... в общем, да покарает их всех Аллах, а Кришна - добавит еще люлей!).

Тем не менее, хоть как-то проверить, как настраивается и работает беспроводная связь от Siemens, тоже интересно. И вот у меня на руках точка доступа Scalance W774-1 RJ45. Этот модуль беспроводной связи может работать в двух режимах: и как точка доступа, и как клиентский модуль. Дома у меня уже есть бытовая сеть, поэтому модуль сразу настрою в клиентский режим.

Для небольшого ускорения процесса настройки модуля рекомендую скачать маленькую программу Proneta (в базовой редакции) по ссылке: https://new.siemens.com/global/en/products/automation/industrial-communication/profinet/proneta.html

Далее запитываем модуль, накручиваем на него антены, подключаем его кабелем (я подключил первым портом) к местному маршрутизатору и запускаем Proneta на компе. Программа Proneta должна обнаружить точку доступа, сама точка - получить IP адрес по DHCP (именно поэтому я для упрощения жизни подключаю модуль сразу в свою сеть).

Далее следует кликнуть правой кнопкой и выбрать пункт меню Set Network Parameters

Задать статические настройки IP

После этого программа Proneta не нужна, ее можно закрыть. Все дальнейшие настройки выполняем в браузере.

По умолчанию логин admin, пароль admin. Во время первого входа сразу предлагается сменить пароль. Воспользовавшись своим мощным интеллектом выставил новый пароль - admin1

Следующий шаг - это открыть визард и перевести модуль в клиентский режим. Данная операция потребует перезагрузки.

После перезапуска точки снова запускаем визард, и на второй вкладке выбираем страну и (опционально) задаем имя точки доступа.

Следующие две вкладки я пропускаю - адрес уже задан, включение/отключение SSH и т.д. не особо важно для моей локальной задачи. А вот тип антен надо задать обязательно.

Следующая вкладка - включаем беспроводной интерфейс, выбираем диапазоны и т.д. Самое главное - это первое, ВКЛЮЧИТЬ радиоинтерфейс. А то я дважды уже этого клиента настраивал и дважды забывал про эту галочку.

Настройки клиента. Тут я снимаю галочку Any SSID и явно указываю свою сеть.

Каналы. Оставляю, как есть - клиент использует любой доступный канал автоматически.

Шифрование. Тут все просто, выбираю тип аутентификации на моей точки и ввожу "пароль" в двух местах.

На закладке "итоги" жму кнопу Set Values.

Настройки сохранятся автоматически в течении 1 минуты. Хотя, процесс можно и ускорить.

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

И, напоследок, проверяем, можно ли достучаться до ПЛК по воздуху из TIA Portal. Связь устанавливается, можно зайти в online CPU.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru