В предыдущей части мы завершили создание визуализиции задвижки и создали простейший скрипт, имитирующий ее поведение.
У нас есть одна панель под названием 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'dropЗададим имя клапана, вместо $dp укажем Flap1. Нажмем на кнопку Save and Run in QuickTest Mode и убедимся, что клапан 1 реагирует на нажатия кнопок Open и Close то есть, открывается или закрывается. Если клапан вдруг не реагирует на команды, посмотрите состояние контрол-менеджера, добавленного в предыдущей части, он может быть остановлен (тогда его надо, разумеется, запустить, или просто перевести режим запуска из ручного в автоматический, чтобы каждый раз не отвлекаться).
Все работаетДобавим точно так же панель для Flap2
Проверяем. Первый клапан работает (управляется), второй- не работает. Почему? Все просто, созданная вчера модель (в виде скрипта) управляет всего лишь одним клапаном Flap1. Остальные клапаны в модели не описаны.
Второй клапан не работаетДоработаем имеющуюся модель для того, чтобы она управляла и вторым клапаном тоже. Модифицируем быстро, но неправильно, путем копи-паста. Создаем еще две функции dpConnect и две разных callback-функции, переименовываем первую callback-функцию. В итоге получается такой код.
Не забываем перезапустить Control Manager этого скрипта (тот, что с опцией -num 2). После модификации модели проверяем изменения и видим, что оба клапана управляются независимо.
В связи с тем, что на DPE Position у нас натравлен конфиг с функцией, третий клапан на экран пока не выносим (собственно, я его и в модель-то напрасно внес).