[Промышленное программирование, SCADA] Самоучитель по WinCC OA. Часть 9. Control-скрипт. Небыстро, но правильно. Полноценный запуск ui
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В процессе создания модели поведения клапанов мы создали (и в прошлой части модифицировали его) скрипт Model, в котором было несколько вызовов dpConnect и несколько callback-функций. Тогда я писал, что это «быстрый, но неправильный способ». Эта пауза была необходима, чтобы предварительно ознакомиться с функцией dpQuery. Предлагаю вернуться немного назад и реализовать модель медленно и очень занудно правильно, теперь при помощи функций семейства dpQueryConnect. Откроем наш скрипт Model.
// $License: NOLICENSE
//--------------------------------------------------------------------------------
/**
@file $relPath
@copyright $copyright
@author akcou
*/
//--------------------------------------------------------------------------------
// Libraries used (#uses)
//--------------------------------------------------------------------------------
// Variables and Constants
//--------------------------------------------------------------------------------
/**
*/
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);
}
}
void OnOpen_CB1(string dp1, bool bNewValue)
{
if (bNewValue) {
dpSet("System1:Flap1.Inputs.Position", 90);
} else {
dpSet("System1:Flap1.Inputs.Position", 0);
}
}
void OnOpen_CB2(string dp1, bool bNewValue)
{
if (bNewValue) {
dpSet("System1:Flap2.Inputs.Position", 90);
} else {
dpSet("System1:Flap2.Inputs.Position", 0);
}
}
void OnOpen_CB3(string dp1, bool bNewValue)
{
if (bNewValue) {
dpSet("System1:Flap3.Inputs.Position", 90);
} else {
dpSet("System1:Flap3.Inputs.Position", 0);
}
}
В функции dpConnect мы явно указываем одну или несколько DPE, на которую происходит подписка. Функциями типа dpQueryConnect мы подписываемся на неопредленное явно количество точек данных. На те точки данных, которые попадают под указанный SQL-запрос. Очевидно, что в нашем случае необходимо подготовить SQL-запрос, который возвращает все наши клапаны: Flap1, Flap2 и Flap3. Существует два вида функций — dpQueryConnectSingle и dpQueryConnectAll. Первая осуществляет передачу коллбэк-функции только одного значение. Того, которое изменилось. Например, если изменение коснулось только второго клапана, то передается значение DPE именно второго клапана. При использовании второй функции в обработчик прилетают значения со всех «подписанных» клапанов, даже если они не изменялись.Необходимо так же иметь в виду, что в рамках системы WinCC OA у нас есть возможность создавать точки данных динамически, в том числе — и через программный код, не перезапуская при этом рантайм целиком. Например, сейчас у нас есть три клапана, и посредством dpQueryConnectSingle произошла подписка. Все работает. Не останавливая систему, мы создаем Flap4. И вот в этом случае подписки на Flap4 не произойдет, так как эта точка данных была добавлена после старта скрипта и выполнения подписки. Такая ситуация не является безвыходной, есть решение. Необходимо подписаться на событие «добавление точки данных». Если произошло такое событие, смотрим, какая точка добавлена. Если добавлена точка данных «клапан», выполняем вначале отписку от изменений (от Flap1, Flap2, Flap3), после чего подписываемся заново (уже на все 4 клапана). Проблема решена.Продемонстрируем «правильную» подписку на изменения. Для начала откроем снова окно SQL-query и составим следующий запрос.
Откроем скрипт Model и приведем функцию main к следующему виду
main()
{
dpQueryConnectSingle("OnOpenAll_CB",FALSE, "", "SELECT '_original.._value' FROM 'Flap*.Commands.Open'");
//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);
}
}
Отдельные dpConnect исключаем из программы. Используем dpQueryConnectSingle. Обработчиком будет функция OnOpenAll_CB.Привожу текст обработчика. Описание работы callback-функции находится в комментариях к программному коду. На вход функции мы получаем динамический массив, первая строка которого нам неинтересна, она содержит заголовок выполнения SQL-запроса. Нам, по сути, интересна только вторая строка и не более, но, зачем-то, я обрабатываю все строки в цикле, хотя в этом конкретном случае строк будет всего две.Первый элемент второй строки — имя точки данных команды Открыть для задвижки. Имя содержит в том числе и FlapX, где X — это номер задвижки. Для того, чтобы задать Position того или иного клапана нам необходимо вычленить этот FlapX. А потом, зная новое значение этой «команды» мы задаем фактическое положение клапана. Новое значение — это второй элемент второй строки. ИтогоsDPE — получаем имя точки данных команды, например System1:Flap2.Commands.OpenbNewValue — получаем значение этой команды, true или falsesplit — разбиваем строку с DPE на подстроки, нам интересна только первая подстрока, которая содержит в себе явное имя клапана, например System1:Flap2Далее в зависимости от состояния команды задаем «процент открытия» клапана, зная точно, какой это клапан.
OnOpenAll_CB(string s, dyn_dyn_anytype ddaTab)
{ //ddaTab contains DPE and the changed value
int z; //вспомогательная переменная
string sDPE; //элемент точки данных, который инициировал скрипт
dyn_string split; //для парсинга DPE
bool bNewValue; //значение команды задвижки
for(z=2;z<=dynlen(ddaTab);z++) //цикл тут необязателен, это я делаю копи-паст из примера справки
{
sDPE = ddaTab[z][1]; //получить DPE, по которому вызвался обработчик - это DPE команды, типа System1:FlapX.Commands.Open
bNewValue = ddaTab[z][2]; //получить значение коменды, тру или фолс
split = strsplit(sDPE, "."); //разбить имя DPE на составляющие части, нам нужна первая часть, типа System1:FlapX
//DebugN("AGK", sDPE, bNewValue);
//DebugN("AGK", split);
if (bNewValue) { //если команда "открыть"
dpSet(split[1] + ".Inputs.Position", 90); //дать значение Position в 90 градусов
} else {
dpSet(split[1] + ".Inputs.Position", 0); //иначе в ноль градусов
}
}
}
Сохраним скрипт, перезапустим его менеджер, откроем в исполнении панель Main и убедимся, что модель работает.В настоящий момент осталось лишь разобраться с тем, как полноценно запускать пользовательский интерфейс. Выполнение панели Main через QuickTest годится только для отладочных целей, но вряд ли подходит для операторских систем. Конечному заказчику необходим запуск пользовательского интерфейса, а не среды разработки. Для этого необходимо в консоли добавить ui-менеджер.
Нам необходим User Interface. В опциях указываем номер 2 (-num 2), потому как первый ui — это редактор, и он уже есть. Вторая опция, «-p Main.pnl» — это имя панели, которая будет открываться в менеджере. Дополнительно я выбрал автоматический запуск менеджера (always). Нажимаем ОК и наблюдаем за запуском ui.
===========
Источник:
habr.com
===========
Похожие новости:
- [Промышленное программирование] Квалификация инструментов для разработки встраиваемого ПО
- [Тестирование IT-систем, Программирование, Java, IT-стандарты, Промышленное программирование] Принцип слоеного теста
- [Программирование, IT-инфраструктура, Виртуализация, Промышленное программирование, Управление разработкой] Цифровая индустрия: непрерывная оптимизация процессов
- [Промышленное программирование, SCADA] WinCC OA Workshop. Часть 7. Навигация: Создание интерфейса АРМ
- [Экология, IT-компании] Microsoft и ещё 12 крупных компаний присоединились к климатической инициативе Amazon
- [Промышленное программирование, SCADA] Самоучитель по WinCC OA. Часть 6. Навигация: Открытие новых окон
- [SCADA, Разработка для интернета вещей, Производство и разработка электроники, Интернет вещей] Заказная разработка контроллеров для IIoT
- [Промышленное программирование, Разработка под Linux, Процессоры] Error: success и что делать по этому поводу
- [Промышленное программирование, SCADA] Самоучитель по WinCC OA. Часть 5. Работа с журналом тревог
- [Промышленное программирование, Программирование микроконтроллеров, Разработка под Arduino, Производство и разработка электроники] Кому в микроконтроллере жить хорошо?
Теги для поиска: #_promyshlennoe_programmirovanie (Промышленное программирование), #_scada, #_wincc, #_siemens, #_simatic, #_scada, #_promyshlennoe_programmirovanie (
Промышленное программирование
), #_scada
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 09:30
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В процессе создания модели поведения клапанов мы создали (и в прошлой части модифицировали его) скрипт Model, в котором было несколько вызовов dpConnect и несколько callback-функций. Тогда я писал, что это «быстрый, но неправильный способ». Эта пауза была необходима, чтобы предварительно ознакомиться с функцией dpQuery. Предлагаю вернуться немного назад и реализовать модель медленно и очень занудно правильно, теперь при помощи функций семейства dpQueryConnect. Откроем наш скрипт Model. // $License: NOLICENSE
//-------------------------------------------------------------------------------- /** @file $relPath @copyright $copyright @author akcou */ //-------------------------------------------------------------------------------- // Libraries used (#uses) //-------------------------------------------------------------------------------- // Variables and Constants //-------------------------------------------------------------------------------- /** */ 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); } } void OnOpen_CB1(string dp1, bool bNewValue) { if (bNewValue) { dpSet("System1:Flap1.Inputs.Position", 90); } else { dpSet("System1:Flap1.Inputs.Position", 0); } } void OnOpen_CB2(string dp1, bool bNewValue) { if (bNewValue) { dpSet("System1:Flap2.Inputs.Position", 90); } else { dpSet("System1:Flap2.Inputs.Position", 0); } } void OnOpen_CB3(string dp1, bool bNewValue) { if (bNewValue) { dpSet("System1:Flap3.Inputs.Position", 90); } else { dpSet("System1:Flap3.Inputs.Position", 0); } } Откроем скрипт Model и приведем функцию main к следующему виду main()
{ dpQueryConnectSingle("OnOpenAll_CB",FALSE, "", "SELECT '_original.._value' FROM 'Flap*.Commands.Open'"); //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); } } OnOpenAll_CB(string s, dyn_dyn_anytype ddaTab)
{ //ddaTab contains DPE and the changed value int z; //вспомогательная переменная string sDPE; //элемент точки данных, который инициировал скрипт dyn_string split; //для парсинга DPE bool bNewValue; //значение команды задвижки for(z=2;z<=dynlen(ddaTab);z++) //цикл тут необязателен, это я делаю копи-паст из примера справки { sDPE = ddaTab[z][1]; //получить DPE, по которому вызвался обработчик - это DPE команды, типа System1:FlapX.Commands.Open bNewValue = ddaTab[z][2]; //получить значение коменды, тру или фолс split = strsplit(sDPE, "."); //разбить имя DPE на составляющие части, нам нужна первая часть, типа System1:FlapX //DebugN("AGK", sDPE, bNewValue); //DebugN("AGK", split); if (bNewValue) { //если команда "открыть" dpSet(split[1] + ".Inputs.Position", 90); //дать значение Position в 90 градусов } else { dpSet(split[1] + ".Inputs.Position", 0); //иначе в ноль градусов } } } Нам необходим User Interface. В опциях указываем номер 2 (-num 2), потому как первый ui — это редактор, и он уже есть. Вторая опция, «-p Main.pnl» — это имя панели, которая будет открываться в менеджере. Дополнительно я выбрал автоматический запуск менеджера (always). Нажимаем ОК и наблюдаем за запуском ui. =========== Источник: habr.com =========== Похожие новости:
Промышленное программирование ), #_scada |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 09:30
Часовой пояс: UTC + 5