ООП в графических языках программирования. ч.2 МОП и ООП

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


В первой части я попытался показать, что черная кошка ООП в темной комнате графических языков существует, даже если это не кошка, а полудохлый кот живодера Шредингера, который то есть, то его нет. Был показан пример реализации методологии объектно-ориентированного программирования, когда программа – это не код на языке С++ или Java, а диаграмма Simulink, SimInTech, SimulationX или SCADE Esterel, — любая графическая нотация описания алгоритма.

В рекламных материалах Мatlab Simulink часто используют термин МОП — Модельно Ориентированное Проектирование (Model-based design). Во многих текстах они подчёркивают, что графическая схема алгоритма это модель, что, конечно, верно. Однако в первоначальном определении МОП, модель – это прежде всего модель объекта, к которому разрабатывают систему управления, включая управляющее программное обеспечение. Таким образом, разрабатывая систему управления по методологии МОП можно и нужно использовать методологию ООП для разработки управляющего ПО. И что бы окончательно закрыть вопрос с моделями, вот вам картинка с отличиями одного от другого. Если все понятно, то можно дальше не читать.

Но и тут, как бы удивительно это не звучало, разработка все равно идет с использованием методологии ООП. Потому как, когда ООП использовать нельзя, но очень хочется, то можно. Правда, потом нужно все вернуть назад, и что бы никакого С++ и итоговом коде не оказалось, ибо безопасность и нормативы «?ber alles». Как говорится, и рыбку съесть, и за нарушения не сесть.

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

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

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

Программа обработки показаний датчика

Это метод класса. Ему соответствует в базе данных своя категория «Датчики» — абстрактный класс с заданным наборов полей и экземпляр класса KBA31CFO1 конкретный датчик. У данного датчика поля имеют конкретные значения, часть полей задаются пользователем, часть полей рассчитывается процессе выполенения программы. см. рис. 2

База данных сигналов с открытой категорией «Датчик»

Пока все как в первом варианте, где у нас формировалась привязка расчетной схемы к конкретному датчику при установке блока на схему. «А где разница?» — спросите вы. А разница – внутри блока. Если в первом варианте внутри была расчетная схема, которая копировалась при каждой установке блока, то в этом варианте внутренность выглядит примерно так:

Внутренности схемы блока экземпляра класса.

Вместо расчетной схемы внутри блока «изображены» только передача и прием данных.
А само выполнение расчета происходит в другом месте, на схеме с рисунка 1. В некоторых случаях можно вообще не использовать блоки на расчетной схеме, достаточной наличия экземпляров класса датчика в базе данных сингналов. Это и есть второй способ реализации инкапсуляции в графических языках. Фишка в том, что все блоки на схеме рисунка 1 векторные, и они обрабатывают не один сигнал, а вектор сигналов со всех датчиков данного типа в расчетной схеме. Если включить режим отображения результатов на линии связи, то мы увидим, что каждая линия связи содержит не одну цифру, а вектор из 4 чисел (по числу датчиков в базе данных).

Схема обработки сигнала датчиков в режиме просмотра значений.

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

Параметры блока ограничителя в схеме расчета.

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

/* Index=104    UID=104    GeneratorClassName=TSignalReader    Name=buz1.sensor.Macro6.Macro3.Macro157.SignalReader3    Type=Чтение из списка сигналов */ }; state_vars->kbastdv104_out_0_[0] = kba31cf001_mf_type; state_vars->kbastdv104_out_0_[1] = kba32cf001_mf_type; state_vars->kbastdv104_out_0_[2] = kba33cf001_mf_type; state_vars->kbastdv104_out_0_[3] = uf40y329084320_mf_type 

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

/* Index=211    UID=211    GeneratorClassName=TAndSrc    Name=buz1.sensor.Macro6.And61    Type=Оператор И */ for(i=0;i<4;i++){ locals->v211_out_0_[i] = state_vars->kbastdv125_out_0_[i] && (!(locals->v191_out_7_[i] > 0.5));  /* Index=212    UID=212    GeneratorClassName=TMulDbl    Name=buz1.sensor.Macro6.Mul_oper1    Type=Перемножитель */  locals->v209_out_2_[i] = consts->kbastdv121_a_[i]*state_vars->kbastdv127_out_0_[i];  /* Index=213    UID=213    GeneratorClassName=TSumSrc    Name=buz1.sensor.Macro6.Add_oper1    Type=Сумматор */  locals->v209_out_3_[i] = (1)*consts->kbastdv122_a_[i]+(1)*locals->v209_out_2_[i];  … } 

После расчета записываем сигналы для каждого экземляра класса:

/* Index=776    UID=776    GeneratorClassName=TSignalWriter    Name=buz1.sensor.Macro6.Macro3.SignalWriter4    Type=Запись в список сигналов */   kba31cf001_mf_xb01 = state_vars->kbastdv207_out_0_[0];  kba32cf001_mf_xb01 = state_vars->kbastdv207_out_0_[1];  kba33cf001_mf_xb01 = state_vars->kbastdv207_out_0_[2];  uf40y329084320_mf_xb01 = state_vars->kbastdv207_out_0_[3]; 

Как видим, в конечном коде никаких объектов нет. Чистый, невинный и безопасный СИ. В приведенном примере реализации ООП в графическом языке векторная схема обсчитывает все однотипные датчики. Такой прием позволяет изменить одну схему для изменения обработки всех датчиков.

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

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

На рисунке 6 — пример двух блоков классов «родитель» и «наследник». Внутри блока сохраняется схема расчета родительского класса. Все данные уходят в общий векторный блок, аналогичный блоку на рис. 4. Полностью повторяется метод родительского класса. А потом у класса-наследника появляются дополнительное поле Yатм и дополнительный пересчет величины с помощью блока линейной интерполяции.

Таким образом, сохраняется возможность менять методы обработки родителя, которые поменяются во всех классах-наследниках, а так же индивидуально настраивать поведение наследника.

Полиморфизм у наследников классов.

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

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


Источник: m.vk.com

Комментарии: