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

Wincc

Мобильная Установка Доказательства Актуальности Контроля Изменений. Часть 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).

Подробнее..

Веселые уроки 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

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

Подробнее..

Самоучитель по WinCC OA. Часть 2. Знакомство с графическим редактором gedi и функцией dpConnect

27.11.2020 06:05:27 | Автор: admin

В прошлый раз мы закончили на создании типа точки данных (DPT Flap) и трех экземпляров точки данных (DPs Flap1, Flap2, Flap3). Пора переходить к визуальной составляющей операторского интерфейса. Открываем модуль gedi. В gedi мы видим название нашего проекта в дереве и его составные части. Нас сейчас интересуют панели, поэтому разворачиваем их.

Определенная структура панелей в проекте уже имеется, но для упрощения мы будем работать в корне. Создадим новую панель, для чего необходимо выполнить правый клик мышкой и нажать пункт меню Add new panel.

Зададим имя панели Flap.pnl, после чего панель появляется в структуре проекта.

Все панели в проекте это тоже текстовые файлы, только они бывают двух типов: pnl или xml. Xml в силу своей структурированности использовать в боевых проектах гораздо удобнее (это же заявляют и люди, работающие с WinCC OA профессионально), его удобнее редактировать в сторонних редакторах. Мы же в рамках базы работаем только в gedi, по этой причине нам достаточно и базового формата. Для изменения формата панели необходимо выполнить save as (сохранить как пункт в меню, потом надо выбрать другой формат данных). Привожу скриншот содержимого пустой панели Flap.pnl:

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

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

Обратите внимание на окно свойств и событий графического объекта, находящееся слева от самой панели, оно нам понадобится. К примеру, для изменения цвета фона объекта необходимо найти свойство Background color.

При двойном клике на этом свойстве появляется таблица цветов.

Система, к сожалению, не позволяет задавать цвет явно в формате RGB, а только лишь выбирать из готового списка цветов. Для создания своего цвета необходимо будет открыть палитру цветов, задать цвет и его имя, после чего этот он появится в проекте. Работать с цветом можно только через его имя. Ограничимся лишь выбором цвета blue.

Сделаем копи-паст одной трубы

Сделаем еще один копи-паст. Это будет сама задвижка (первые два прямоугольника это труба).

Этот третий объект необходимо повернить вертикально (считается, что задвижка в нормальном положении закрыта). Тут появляется еще один нюанс. Начало системы координат графического объекта на панели определяется т.н. реперной точкой (reference point). По умолчанию в нашем случае эта точка располагается в верхнем левом углу (на скриншоте она обозначена, как едва заметный желтый круг). Если прямо сейчас найти свойство Rotation и задать угол 90, то мы получим следующее изображение.

Как видно, реперная точка теперь находится в левом нижнем углу (на самом деле референсная точка осталась на своем месте, а относительно нее и произошло вращение). Обнулю свойство Rotation и перенесу реперную точку в середину прямоугольника. Сдвигать reference point необходимо в окне свойств объекта, непосредственно мышью перемещать эту точки нельзя.

Теперь можно спокойно задать свойство Rotation. И немного скорректировать размер задвижки, чтобы она помещалась в трубу.

Рядом с кнопкой Save есть кнопка Save and run, нажмем ее и увидим графическое изображение нашей задвижки в режиме исполнения.

Приступим к динамизации изображения пусть задвижка вращается в зависимости от DPE Position нашей DP Flap. Выберем графический примитив задвижка, найдем в списках событий (под графическими свойствами) событие Initialize и ждем кнопку Open property wizard этого события.

Выбираем Rotate object

Выберем DPE Flap1.Inputs.Position, зададим значения минимум и максимум, нажмем Next, а в следующем окне Finish

Нажмем Save and run и понаблюдаем поворот задвижки. В моем случае еще с прошлого раза значение DPE Flap1.Inputs.Position было равно 11, что мы и наблюдаем сейчас визуально.

Если открыть сейчас модуль para и для конфига Flap1.Inputs.Position._original дать значение 90, задвижка полностью откроется

Теперь давайте вернемся к событию Initialize для графического примитива задвижка. Только в этот раз нажмем кнопку Open script editor:

// SimpleCtrlScriptStart {valid}main(){  EP_setRotation();}void EP_setRotation(){  dyn_errClass err;  if( !dpExists( "System1:Flap1.Inputs.Position:_online.._value") )  {    setValue("", "color", "_dpdoesnotexist");    return;  }  dpConnect("EP_setRotationCB",            "System1:Flap1.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:Flap1.Inputs.Position}// DPConfig {:_online.._value}// DPType {int}// PVSSRange {0}// Min {0}// Max {90}// MinRotation {0}// MaxRotation {90}// SimpleCtrlScriptEnd {EP_setRotation}

Собственно, как я и угрожал везде текстовые файлы. В данном случае мы видим скрипт, который был создан автоматически средствами Wizard. Да, визарды есть в системе WinCC OA, но по сути их назначение вызов шаблона, рыбы, которую мы в дальнейшем будем править руками. Как видно скрипт написан на языке, очень похожем на C. В WinCC OA встроенный язык называется Control.

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

main(){  EP_setRotation();}

Функция main() содержит единственную строчку вызов функции EP_SetRotation(). Посмотрим на ее содержимое.

void EP_setRotation(){  dyn_errClass err;  if( !dpExists( "System1:Flap1.Inputs.Position:_online.._value") )  {    setValue("", "color", "_dpdoesnotexist");    return;  }  dpConnect("EP_setRotationCB",            "System1:Flap1.Inputs.Position:_online.._value");  err = getLastError();  if (dynlen(err) > 0)    setValue("", "color", "_dpdoesnotexist");}

В первую очередь эта функция выполняет проверку, существует ли в системе точки данных. Если точки данных (точнее, DPE) не существует, то функция прекращает свою работу и выставляет цвет с именем точкаданныхнесуществует. Далее идет вызов dpConnect. Нет, не так

ДАЛЕЕ ИДЕТ ВЗОВ ФУНКЦИИ dpConnect!!!

Я намеренно выделяю эту мысль. Функция dpConnect и сам механизм подписки основопологающий механизм всей системы WinCC OA. В обязательном порядке необходимо понять действие этого механизма, и правильно им пользоваться. В неумелых руках, при недальновидности, при глупости или при лени у вас получится не нормальный программный продукт в виде легкой и надежной операторской системы, а одно огромное разочарование, состоящее из глюков, тормозов и костылей. И виной тому будет не среда разработки WinCC OA, а чьи-то кривые руки. Запомните, я предупрежал!

Что же делает функция dpConnect в нашем случае?

dpConnect("EP_setRotationCB", "System1:Flap1.Inputs.Position:_online.._value");

Во-первых, эта функция сообщает системе о наличии Callback-функции EP_setRotationCB (она описана ниже). Во-вторых, вызов колбэк-функции EP_setRotationCB осуществляется по событию System1:Flap1.Inputs.Position:_online.._value, то есть, по изменению элемента точки данных. Ответственным за раздачу сообщений об изменении точки данных у нас, как вы помните, является менеджер событий (EV). Таким образом, изменение угла наклона прямоугольника у нас происходит тогда и только тогда, когда DPE Position точки данных Flap1 меняет свое значение. Как я говорил ранее, постоянного поллинга данных у нас нет, вся система событийная.

Можно провести некую аналогию с протоколом MQTT, работу которого я недавно разбирал на примере S7-1200. Менеджер событий у нас является брокером в этой аналогии. Драйвер связи с контроллером является издателем. А пользовательский интерфейс подписчиком. Драйвер подключается к брокеру и публикует сообщение. UI подписывается на изменение точки данных (при помощи функции dpConnect). И как только это изменение произошло, вызывает обработчик этого события в виде callback-функции. В обычном состоянии подписчик не опрашивает брокера постоянно, а лишь слушает его в фоновом режиме, что и обеспечивает событийность любой обработки. Поскольку вся визуализация только тем и занимается, что меняет цвета, картинки, положение и т.д. в зависимости от значений переменных (тэгов), то этих вызовов dpConnect у вас будет очень много.

Причем, обратите внимание. Функция main состоит только из вызова функции EP_SetRotation. После этого вызова работа функции main прекращается. А вот функция EP_SetRotation, в свою очередь, осуществляет привязку изменений DPE к другой функции, и тоже прекращает свою работу. Возникает закономерный вопрос а кто работать-то будет? Ответ на этот вопрос простой dpConnect создает отдельный поток (подпроцесс) на компьютере, который и будет обрабатывать изменение графического элемента, и это в-третьих. В нормальном режиме, как я уже говорил, этот подпроцесс спит и ничего не делает, включаясь только по событию. Из этого делаем вывод, что программирование в WinCC OA это многопоточное программирование, и это тоже надо иметь в виду. В реальной работе при таком подходе потребуются механизмы разделения, такие, как семафоры, или иные методы синхронизации.

Посмотрим саму callback-функцию

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);}

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

Кроме проверки значений параметров эта функция содержит вызов setValue. Ее параметры:

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

второй (rotation) имя свойства, которое мы меняем, а меняем мы угол наклона, это имя свойства берется из документации и из графического редактора свойств объекта только программное имя свойства и графическое далеко не всегда совпадает (программисты классического WinCC меня поймут);

третий (fRotation) значение свойства.

Возможно так же работать со свойствами в объектно-ориентированной нотификации, создавая объект типа Shape, привязываясь к существующему объекту (GetShape) и так далее. Это уже продвинутый уровень.

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

Добавим еще на панель пару кнопок открыть и закрыть. В настоящий момент мы работаем исключительно в WinCC OA, поэтому реакцию системы на команды реализуем тоже средствами этой системы. Перетаскиваем элемент Push Button на экран и меняем его свойство Button label

Далее необходимо запрограммировать нажатие кнопки Open. Для этого в списке событий выбираем Clicked и вызываем Script Editor. В редакторе я набираю вызов функции dpSet, это еще одна очень полезная функция, которая нам сильно пригодится в работе:

Параметры этой функции вначале имя DPE, далее идет значение DPE. В меню ToolsDatapoint Selector есть возможность вызвать список всех точек данных проекта.

Выбираю там Flap1.Inputs.Position. Это делается осознанно, в качестве самого примитивного примера, далее проект будем улучшать. Значение положения в открытом состоянии 90 (в соответствии с нашей мощной моделью данных). Редактор скриптов ловит только самые грубые синтаксические ошибки. Ошибки семантики не определяются. Например, очень распространенная ошибка некорректная точка данных не будет определена на этом этапе. Вывод? Не забываем про отлов ошибок в самом скрипте. В этом скрипте мы ошибки не ловим никак, и это неправильно, в реальных проектах надо, как минимум, выполнить проверку dpExists.

Аналогично описываем обработчик события нажатия кнопки Close. С одной лишь разницей в DPE Position записываем не 90, а 0. Теперь можно нажать на кнопку Save and run и проверить работу панели.

Нажали "Закрыть"Нажали "Закрыть"Нажали "Открыть"Нажали "Открыть"

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

dpSet("System1:Flap1.Inputs.Position", 90);

заключается в (привожу аналог)

System1:Flap1.Inputs.Position:= 90;

Но теперь давайте вспомним структуру системы. Сам скрипт присвоения выполнятся в менеджере ui. Значение DPE расположено в EV. Взаимодействие между ui и EV осуществляется по протоколу TCP/IP. Один вызов dpSet это одно событие, передающееся от ui в EV. Один вызов (одно присвоение) это ерунда. А если присвоений будет существенно больше? Например, два? ("Саша, существенно больше" (с) День Радио). Два миллиона? Или двести тысяч? Что произойдет в этом случае? А в этом случае от ui в сторону EV полетит не одно сообщение, а двести тысяч сообщений. Или два миллиона. И это может легко привести к переполнению очереди событий. От чего система гарантированно умрет. Вообще, как уже было замечено выше, WinCC OA в ленивых руках становится опасной, но при грамотном вдумчивом уважительном подходе способна на очень многое.

Как же поступать правильно? Правильно отправлять множество присвоений путем одного вызова функции dpSet. Например, вместо трех вызовов:

dpSet("System1:Flap1.Inputs.Position", 90);

dpSet("System1:Flap2.Inputs.Position", 90);

dpSet("System1:Flap3.Inputs.Position", 90);

делать один вызов

dpSet("System1:Flap1.Inputs.Position", 90, "System1:Flap2.Inputs.Position", 90, "System1:Flap3.Inputs.Position", 90);

Подробнее..

Самоучитель по WinCC OA. Часть 3. Глобальные скрипты (control scripts)

30.11.2020 14:07:23 | Автор: admin

Запрограммированое в предыдущей части поведение задвижки нелогично. В обработчике нажатия кнопки Открыть или Закрыть мы непосредственно задавали значение элемента под названием Position. При том, что в самом DPT у нас уже предусмотрены команды Открыть и Закрыть.

Datapoint type FlapDatapoint type Flap

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

Но для начала изменим обработчик нажатия на кнопки Открыть и Закрыть.

Скрипт на нажатие кнопки OpenСкрипт на нажатие кнопки Open

Меняем на

main(mapping event){  dpSet("System1:Flap1.Commands.Open", 1, "System1:Flap1.Commands.Close", 0);}
Измененный скрипт кнопки OpenИзмененный скрипт кнопки Open

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

Аналогично поступаем со скриптом кнопки Close

Проверим выполнение скрипта нажатия кнопок, меняются ли переменные команд в модуле Para

Нажатие кнопки OpenНажатие кнопки OpenНажатие кнопки CloseНажатие кнопки Close

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

Для создания скрипта мы должны в gedi найти в дереве проекта Scripts, нажать правую кнопку мыши и выбрать Add New CTRL Script

Дадим осмысленное имя файла скрипта, я его назвал Model

После нажатия ОК скрипт появляется в дереве проекта

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

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

  2. Создаем callback функцию на изменение значения команды в рамках модели поведения.

Другие варианты поведения модели неверны с точки зрения идеалогии WinCC OA. Если в самом глобальном скрипте вызывать dpGet для чтения значения переменной команды, то этот скрипт надо вызывать постоянно, с определенной периодичностью.

Напоминаю, что у функции dpConnect есть два аргумента. Первый аргумент имя callback-функции (я назвал ее OnOpen_CB), второй имя точки данных, на которую происходит подписка. Итого, в упрощенном виде, без каких-либо проверок функция main скрипта Model выглядит так:

main(){dpConnect("OnOpen_CB", "System1:Flap1.Commands.Open");}

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

void OnOpen_CB(string dp1, bool bNewValue){;}

Дописываем обработчик, он очень простой. Если значение команды открыть равно истина, то задаем положение задвижки, равное 90. Если значение равно ложь, то значение 0.

void OnOpen_CB(string dp1, bool bNewValue){if (bNewValue) {dpSet("System1:Flap1.Inputs.Position", 90);} else {dpSet("System1:Flap1.Inputs.Position", 0);}}

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

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

В консоли WinCC OA нажимаем кнопку Append new manager, выбираем в появившемся окне менеджер Control. Для первого запуска свежесозданного скрипта имеет смысл выбрать режим запуска (Start mode) ручной (manual), чтобы не наблюдать попытки перезапуска в случае ошибок в самом скрипте. В качестве параметров вызова менеджера необходимо указать его номер в системе. В нашем случае это будет номер 2. Почему 2? Потому что менеджер с номером 1 в системе уже существует. Менеджеры одного типа в системе должны запускаться со своим уникальным номером. Именно одного типа. Это значит, что в системе может быть ui с номером 1 и ctrl с номером 1, а вот два ui (или ctrl) с одним и тем же номером быть не должны. Поэтому в качестве агрумента запуска я указываю строку -num 2. Кроме того, требуется передать имя исполняемого скрипта. Вызов нового менеджера выглядит следующим образом:

Свойства Control менеджера для симуляции задвижкиСвойства Control менеджера для симуляции задвижки

Остается нажать кнопку ОК и запустить менеджер вручную кнопкой Manager Start либо мышью через вспывающее меню (новый менеджер при этом должен быть выделен в списке). Если все сделано правильно, то добавленный менеджер позеленеет и примет статус 2.

Скрипт симуляции успешно запущенСкрипт симуляции успешно запущен

Теперь остается проверить работу симуляции.

По нажатию кнопки Open визуализируется открытиеПо нажатию кнопки Open визуализируется открытиеКнопка Close визуализирует закрытиеКнопка Close визуализирует закрытие

Следует обратить внимание на то, что Control Manager запускает скрипт (точнее, свою функцию main) один раз при старте. После выполнения функции main() необходимые callback функции продолжают находится в памяти ПК, они исполняются при выполнении условий, указанных в dpConnect (по изменению значения переменной). Однако, если в сам скрипт были внесены изменения, то необходимо вручную остановить соответствующий экземпляр control-менеджера и запустить его заново. Без останова-запуска изменения не будут приняты.

Сам control manager при запуске создает свой отдельный процесс. Его функция main выполняется в отдельной нитке (потоке, thread). Callback функция (в нашем случае OnOpen_CB) так же запускается в отдельном потоке. После выполнения функция main перестает работать, но callback продолжает находится в памяти ПК (в своем потоке) и вызывается при изменениях подписанной переменной.

Подробнее..

Самоучитель по 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.

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

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

Подробнее..

Категории

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

  • Имя: Макс
    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