System Analysis and Design
Desktop environments
ООP
Industrial Programming
SCADA
May 13

Объектно ориентированное програмирование в графических языках

Объектно-ориентированное программирование (ООП) – концепция, которая призвана облегчить разработку сложных систем, за счет введения новых понятий, более приближенных к реальному миру, чем функциональные и процедурные языки программирования. Как пишет википедия, «Обычный человеческий язык в целом отражает идеологию ООП, начиная с инкапсуляции представления о предмете в виде его имени и заканчивая полиморфизмом использования слова в переносном смысле, что в итоге развивает выражение представления через имя предмета до полноценного понятия – класса.»




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


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


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



Рисунок 1. Пример программы управления АЭС в графической нотации

Слева входные сигналы, справа команды.

Мне кажется, что такой алгоритм прочитать может даже ребенок:

  • Если насос включен в течении 60 секунд и расход меньше 10, то задвижку на рециркуляции открыть.
  • Если насос включен, то подавать в течении 5 секунд на задвижки 001 и 002 команду открыть.
  • Если расход больше 20 и насос включен, то в течении 5 секунд на задвижку 003 подавать команду закрыть.

В бытность мою студентом я подрабатывал, создавая библиотеку компонентов для Delphi и был знаком с ООП не понаслышке. Потом, когда столкнулся с реальными программами управления АЭС, очень удивился что нет никакого абстрагирования, инкапсуляции и, прости господи полиморфизма, только чистый Си, и еще желательно урезанный правилами и рекомендация MISRA C, чтобы все было надёжно, переносимо, безопасно.


Вершиной обрезания Си в моей практике был язык FIL, для систем управления реакторами РБМК. В нем функции заранее писались на Си, компилировались, а потом вызывались на основе текстового файла, где они были описаны на языке FIL. В итоге, можно было вызвать только ограниченный, но тщательно проверенный и отлаженный набор функций. И все это – во имя безопасности и надежности.


Но при этом система управления реактором и в целом система управления АЭС – это как раз тот случай, где принципы ООП должны применятся в полный рост. В само деле, есть множество однотипного оборудования – задвижки, насосы, датчики, всё легко классифицируется, есть готовые объекты, соответствующие реальному оборудованию. Казалось бы, вот оно – применяй ООП, классы, наследование, абстрагирование и полиморфизм. Но нет, нужен чистый Си и это требования безопасности.


А дальше – еще интереснее. На самом деле программу для управления АЭС пишет не программист, а технолог – только он знает, что и когда закрывать, открывать, включать, а главное – он знает, когда всю это байду выключать, чтобы не долбануло. А программист должен аккуратно всё это реализовать на коде Си. А еще лучше, что бы программиста вообще бы не было, а технолог сам рисовал технологические алгоритмы управляющих программ в графическом виде, автоматически генерировал код Си и загружал его в аппаратуру управления. Это рекомендуют международные стандарты по безопасности, в этом случае программист – как скрипач – не нужен. Он вносит только дополнительные ошибки и искажения в реализацию мыслей технолога.




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


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


/* Index=0
   UID=0
   GeneratorClassName=TSignalReader
   Name=KBA__AA.KBA31EY001.alg_inp
   Type=Вход алгоритма */

state_vars->kbaalgsv0_out_1_ = kba31ap001_xb01;
state_vars->kbaalgsv0_out_4_ = kba31cf001_xq01;

Просто присвоение переменных.
Любой блок описывается как вычисление выхода по входу, с учетом параметров, заданных в списке констант. Например блок «Больше» выглядит в коде так:


/* Index=5
   UID=5
   GeneratorClassName=TLogBlock
   Name=KBA__AA.KBA31EY001.smu.GT2
   Type=Операция БОЛЬШЕ */

locals->v5_out_0_ = state_vars->kbaalgsv0_out_4_ > consts->kbaalgsv3_a_;

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


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


/* Index=14
   UID=14
   GeneratorClassName=TSignalWriter
   Name=KBA__AA.KBA31EY001.alg_out
   Type=Выход алгоритма */

if((action==f_InitState)||(action==f_GoodStep)||(action==f_RestoreOuts)){
 kba31ey001_yb01 = locals->v8_out_0_;
 kba31ey001_yb11 = state_vars->kbaalgsv9_out_0_;
 kba31ey001_yb12 = state_vars->kbaalgsv12_out_0_;
 kba31ey001_yb02 = locals->v13_out_0_;
};

А где здесь классы, спросите вы?


Вся методология, связанная с ООП, находится в именах переменных. Казалось бы, что такого может быть в имени переменной? А там может быть цела бездна. Например имя переменной kba31ap001_xb01, просто переменная в коде Си отвечающая требованием по наименованию переменных. Однако для технолога проектанта она выглядит примерно так: «Реакторное отделение, система промышленного водоснабжения, первый насос, пуск». Все это волшебство преобразования происходит благодаря замечательной немецкой системе кодирования (Kraftwerk-Kennzeichensystem) KKS, цитата:


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


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


Но самое интересное в последней части имени — _xb01, то что задается через знак подчеркивания. Если посмотреть на базу сигналов для проекта управления, то мы увидим там классы, понятные и знакомые всем, кто когда-то, как-то и где-то интересовался ООП (см. Рис. 2).



Рисунок 2. Пример структуры базы сигналов для системы управления АЭС.

У нас есть классы, или таблицы, на рисунке это столбец «Категории». Например, «KD1» у которых есть таблица шаблонных сигналов, полей класса Верхний предел измерения, нижний предел измерения, показание датчика и т.д. — это абстракция.


А так же есть реализация данного класса — конкретный датчик, например ТК21F02B1, расположенный в контуре, как вы уже догадались по его названию, в «Реакторном отделении, системе промышленного водоснабжения, у первого насоса», да и то, что это датчик расхода, тоже есть в этом названии, но это не точно.


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


На этом месте можно сказать, постой это же не совсем ООП, или даже совсем не ООП, тут же просто структуры данных, это есть и в стандартном Си. А где инкапсуляция методов в состав класса? Обработка данных должна быть в классе, тогда это и будет настоящий кошерный метод ООП.
Посмотрим, как выглядит в графическом виде подпрограмма контроля достоверности датчика. На рисунке 3 часть схемы обработки сигналов:



Рисунок 3. Пример программы обработки сигнала.

Видно, что в подпрограмме обработки используются имена переменных ТК21F02B1_XQ04, сформированные по правилам ККS и на основании таблицы полей класса. В приведенном примере происходит вычисление показания датчика в процентах ТК21F02B1_XQ03 по заданным значениям полей экземпляра класса таким, как ТК21F02B1_Xmin и ТК21F02B1_Xmax.


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


 /* Index=12
   UID=12
   GeneratorClassName=TSignalReader
   Name=KD1.kd3_45.SR6
   Type=Чтение из списка сигналов */

state_vars->su100v12_out_0_ = tk21f02b1_ai;

И присвоение результата расчета, тоже как простое присвоение переменной (с проверкой на действительность числа, что бы не уронить систему если в результате обработки сигналов мы получили ошибку)


/* Index=100
   UID=100
   GeneratorClassName=TSignalWriter
   Name=KD1.kd3_45.SW3
   Type=Запись в список сигналов */

if(isfinite(locals->v63_out_0_)){
 tk21f02b1_xq04 = locals->v63_out_0_;
};

А в какой же момент появляется объединение данных полей класса методов обработки? На самом деле я знаком с двумя вариантами этого фокуса. Сейчас разберем один из них. (Второй вариант разобран здесь..)
Посмотрим, как на схеме настраивается блок в котором расположена схема программы обработки (см. рис. 4).


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


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


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



Рисунок 4. Пример настройки схемы обработки датчика.

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



Рисунок 5. Настройка имени переменных в блоках чтения.

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


Подведу итог: в графических языках программирования методы ООП так же применяются и приносят пользу. А после генерации исходного кода управляющих программ, все артефакты методологии ООП, исчезают и остается, чистый С, безопасный, надежный, верифицируемый.


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

Рассказать про второй вариант инкапсуляции в графических языках программирования?
92.3% Да интересно, можно еще показать полиморфизм? 36
12.8% Нет все понятно и так. 5
39 users voted. 18 users abstained.
+14
9.2k 37
Comments 30
Top of the day