Открытая SCADA система OpenSCADA принадлежит к классу SCADA(Supervisory Control and Data Aquisition) систем. Системы данного класса используются как элемент систем автоматизации технологических процессов(АСУ-ТП).
В отдельных случаях SCADA системы могут использоваться на уровне контроллеров, при этом совмещая функции контролера с функциями SCADA системы.
Для организации пользовательских вычислений в среде промышленных контроллеров и SCADA систем используются различные языки программирования. В случае с контроллерами в качестве таких языков чаще всего используются языки низкого уровня (ассемблеры), однако в последнее время всё чаще используются языки высокого уровня (C, Pascal и другие), а также формальные языки (блочные схемы, релейные схемы, логические схемы и другие). В случае с SCADA системами вычисления чаще обеспечиваются языками программирования высокого уровня и формальными языками.
Предоставление среды пользовательского программирования любой программной системой вообще является важным показателем качества и развитости системы.
Данный проект предназначен для разработки среды программирования системы OpenSCADA.
2 Назначение
Функционально разработка предназначена для создания в системе OpenSCADA компонентов среды программирования, с помощью которых можно реализовывать различные вычисления и управляющие алгоритмы внутри системы OpenSCADA.
Эксплуатационным назначением разработки является:
расширение сферы применения системы OpenSCADA за счёт увеличения гибкости среды программирования;
сокращения затрат труда на создание математических моделей технологических процессов;
возможность создания крупных моделей с использованием наработок предыдущих моделей.
3 Требования
3.1 Архитектурные требования к среде программирования
Под архитектурными требованиями подразумевается то, какой структуре должна следовать разработка.
Особенность архитектуры системы OpenSCADA накладывает соответствующие требования к архитектуре разработки. Так, учитывая модульность, гибкость и масштабируемость системы OpenSCADA, нужно обеспечить такими же характеристиками данную разработку.
С такой целью среду программирования архитектурно можно разделить на три части:
декларирующие объекты в ядре системы OpenSCADA;
библиотеки объектов функций (API объектной модели OpenSCADA);
вычислительные контроллеры и другие вычислительные узлы системы.
Объекты функций должны содержать только структуру параметров функций и алгоритм вычисления. Реальные значения объектам функций должны передаваться в виде кадра значений различными вычислительными контролерам и узлами.
Декларирующие объекты должны содержаться в ядре системы OpenSCADA для предоставления доступа всем компонентам среды программирования.
Кадры значений, которые передаются объектам функций в процессе вычислений, должны храниться в узлах, требующих вычислений. Такими узлами могут быть:
вычислительные контроллеры;
среда визуализации;
логический уровень параметров;
другие вычислительные узлы.
3.2 Общие требования
Сформулируем требования к среде программирования, исходя из архитектурных требований, которые требуют разделения среды программирования на три части.
Ядро системы OpenSCADA должно обеспечить:
хранение декларирующих структур: объектов функций, параметров функций и кадра значений функций;
связывание кадра значений с функцией;
поддержку параметров стандартных типов системы OpenSCADA: целые, вещественные, логические и строчные;
возможность тестирования функций для указанных входных данных;
В рамках проекта нужно реализовать следующие библиотеки функций и вычислительные контролеры:
модуль(и) статических библиотек функций;
модуль вычислителя на языке программирования высокого уровня и модуль библиотек функций на языке высокого уровня (по совместительству);
модуль вычислителя на языке блоков.
Модуль(и) статических библиотек функций должны обеспечить:
формирование библиотеки стандартных математических функций;
формирование библиотеки функций совместимости с SCADA Complex1 фирмы НІП “ДІЯ”;
обслуживание системных команд запуска и остановки модуля с целью обеспечения поддержки автоматического обновления модуля библиотек.
Модуль вычислителя на языке программирования высокого уровня и модуль библиотек функций на этом языке по совместительству должны обеспечить:
конфигурацию контролеров данного типа с возможностью установки периода вычисления и количества вычислений в одном цикле;
простые типы параметров для доступа к данным вычислителя из системы;
создание параметров вычислительной функции контролера подсистемы "DAQ" (таблицы данных);
создание программы вычисления для контролера подсистемы "DAQ";
поддержку обращений к атрибутам параметров контролера подсистемы "DAQ" из программы вычисления;
формирование библиотек функций пользователя;
формирование функций пользователем;
редактирование параметров функций с упреждением пользователей функций (кадры значений) про смену структуры, то есть без прерывания вычислений и блокирования возможности модификации;
создание программы функции.
Модуль вычислителя на языке блоков должен обеспечить:
конфигурацию контролеров данного типа с возможностью установки периода вычислений и количества вычислений в одном цикле;
простые типы параметров контролера подсистемы "DAQ" для доступа к данным вычислителя из системы;
формирование блоков блочной схемы;
связывание блоков один с другим и с атрибутами параметров.
3.3 Требования к разрабатываемому языку программирования высокого уровня
Для реализации модуля вычислителя на языке программирования высокого уровня и модуля библиотек функций на этом же языке нужно использовать грамматику одного из известных языков программирования высокого уровня.
Программа на выбранном языке должна компилироваться в код внутренней виртуальной машины. Главными требованиями к виртуальной машине являются: высокая надёжность и производительность. Для удовлетворения требований высокой надёжности виртуальная машина должна содержать механизмы учёта и ограничения времени обсчёта.
В обязательном порядке нужно реализовать следующие возможности языка программирования:
поддержка основных операций;
поддержка сложных выражений с приоритетами операций;
поддержка скобок для отделения высокоприоритетных участков;
поддержка основных типов данных системы OpenSCADA: целый, вещественный, логический и строковый;
поддержка облегчённой схемы приведения типов;
поддержка условных операторов, в их числе: if, for, while;
поддержка скобок для группирования выражений;
поддержка вызова функций, в их числе встроенные и внешние (из объектной модели ОМ).
3.4 Требования к разрабатываемому языку блочного программирования
Язык блочных схем может строиться на основе функций доступных библиотек функций объектной модели OpenSCADA. Каждый блок может ассоциироваться с функцией и формировать структуру значений в соответствии со структурой функции. Каждый блок должен обеспечивать включение и исключение из процесса обработки, не останавливая вычисления всей схемы.
Для связывания блоков один с другим нужно обеспечить поддержку следующих типов связей:
межблочные - подключение входа блока к выходу другого блока, а также входа одного блока к входу другого;
дальние межблочные – объединение блоков разных контроллеров данного модуля;
коэффициент – преобразование входа в константу, все входы/выходы по умолчанию инициируются как константы;
внешний атрибут параметра.
Связи должны поддерживать их "горячую", в процессе вычисления, установку.
4 Проектирование
4.1 Постановка задачи
Любой сложный программный комплекс должен содержать среду программирования. Наличие такой среды значительно расширяет гибкость системы и, как следствие, сферу её применения. Среда программирования, которая используется в таких программных комплексах, может основываться на различных языках программирования. Однако чаще всего используются языки, наделённые высоким уровнем формализма, например, языки блоков (блочные схемы, логичные схемы, релейные схемы и другие). Однако, на уровне с ними часто используются языки высокого уровня типа С и С++ в своей упрощённой реинкарнации: Java и JavaScript. Не в последнюю очередь также используются языки логического вывода: Prolog и Lisp.
Система OpenSCADA разрабатывается как гибкая и высоко-масштабируемая SCADA система, поэтому создание собственной среды программирования является важной задачей. Главной целью создания среды пользовательского программирования является предоставление в систему OpenSCADA гибкого и мощного механизма программирования.
Кроме того, среда программирования предназначена для предоставления возможности создания:
алгоритмов управления технологическими процессами;
моделей технологических, химических, физических и других процессов, а также для реализации адаптивных механизмов управления по моделям;
программ пользователя для управления внутренними функциями системы OpenSCADA и её подсистемами и модулями;
гибкого формирования структур параметров на уровне пользователя (логический уровень);
дополнительных вычислений.
4.2 Объект функции, как ключевой элемент среды программирования
Ключевым элементом среды программирования в роли объекта выбрана функция, где функция определена как контейнер, который содержит структуры данных (параметры) и алгоритм их вычисления. При этом параметры могут быть как входами, так и выходами. Такая, несколько упрощённая объектная структура, совмещает объект с механизмом, тем самым упрощая начальное восприятие и создание среды программирования. Кроме того, такой выбор не исключает возможности создания полноценной объектной модели (ОМ) в будущем.
То есть, имеем элементарный модуль рис.1.
Рис. 1. Функция
Где:
X – множество входов;
Y – множество выходов;
A – алгоритм обработки входов.
Как видно, такой модуль не содержит состояний, потому как не хранит значений ни входов ни выходов. Добавление состояния, как текущий контекст, модуль получает в так называемых вычислителях. Такая схема позволяет один модуль функции использовать в разных модулях вычислителей (рис. 2).
Рис. 2. Основа среды программирования системы OpenSCADA
4.3 Выбор языка высокого уровня для реализации вычислителя на этом языке
Для реализации языка программирования высокого уровня нужно определиться с языком, который будет взят за основу. Исходя из принципа, что язык должен быть достаточно известным и, по возможности, использоваться другими проектами в качестве внутреннего языка вычислений, остановимся на следующих языках: С, Java, JavaScript и Phyton. Поскольку языки С, Java и JavaScript практически вышли из языка C, то ограничим круг до языков Java и JavaScript. Язык C++ является несколько сложным, поскольку содержит механизмы работы с адресами и указателями. Эти возможности являются избыточными для задач среды пользовательского программирования.
Остановимся на языках Java и JavaScript, грамматика которых упрощена. Сначала реализуем общую грамматику для языков Java и JavaScript с последующим уклоном к языку JavaScript, поскольку он часто используется внутри других проектов (Web-браузеры, KDE).
4.4 Проектирование грамматики синтаксического анализатора. Выбор инструмента для создания синтаксического и лексического анализаторов
Известно, что для построения компилятора или интерпретатора нужно создать лексический анализатор, синтаксический анализатор и генератор кода или интерпретатор.
Лексический анализатор строится достаточно просто.
Синтаксический анализатор основывается на таком понятии, как формальные грамматики и его создание - задание нетривиальное. Поэтому существуют готовые инструменты для построения синтаксических анализаторов по указанной грамматике. Из них можно отметить генератор синтаксических анализаторов Yacc (AT&T и Berkeley). На основе этого известного генератора синтаксических анализаторов создано множество других, например Bison и Zubr. Все они совместимы и могут заменять один другого.
Для разработки языка программирования был выбран генератор синтаксических анализаторов Bison, поскольку он свободно распространяется практически со всеми современными дистрибутивами ОС Linux, развивается и является многоплатформенным.
Любую формальную грамматику можно записать в виде:
G = <A, N, г, P>
где :
A – терминальный алфавит;
N – нетерминальный алфавит;
г - аксиома;
P – правило грамматики.
Запишем терминальный и нетерминальный алфавиты:
A = {ERR, IF, ELSE, =, ADDEQ, SUBEQ, MULEQ, DIVEQ, FUNC, B_FUNC, B_FUNC1, B_FUNC2, VAR, CONST, ?, :, OR, AND, |, &, ^, >, <, GE, LE, EQ, NE, +, -, *, /, %, UN_MINUS, !, , ',', (, ), '{', '}', ; }
N = {root, solver, solve,assign, expr, prmlst, if, if_expr, end }
Описание терминального алфавита приведено в таблице 1. Описание нетерминального алфавита приведено в таблице 2. Правила P грамматики приведены в таблице 3. В качестве аксиомы грамматики определено: г = root;
Таблица 1: Терминальный алфавит Java-подобного языка (A)
Терминал
Описание
ERR
Ошибка. Вставляется лексическим анализатором в случае ошибки в нём.
error
Ошибка. Прерывает работу синтаксического анализатора.
IF
Ключевое слово условия - “if”.
ELSE
Ключевое слово условия - “else”.
=
Операция – присвоить.
ADDEQ
Операция - “+=”
SUBEQ
Операция - “-=”
MULEQ
Операция - “*=”
DIVEQ
Операция - “/=”
FUNC
Внешняя функция.
B_FUNC
Встроенная функция без параметров.
B_FUNC1
Встроенная функция с одним параметром.
B_FUNC2
Встроенная функция с двумя параметрами.
VAR
Переменная, автоматическая переменная, атрибут системного параметра или параметр функции.
CONST
Константа.
?
Первый символ операции “?:”
:
Второй символ операции “?:”
OR
Логическая операция - “||”
AND
Логическая операция - “&&”
|
Операция – побитовое “ИЛИ”
&
Операция – побитовое “И”
^
Операция – исключающее “ИЛИ”
>
Операция – больше.
<
Операция – меньше.
GE
Операция – больше или равно.
LE
Операция – меньше или равно.
EQ
Операция – эквивалентно.
NE
Операция – неэквивалентно.
+
Операция – сложение.
-
Операция – вычитание.
*
Операция – умножение.
/
Операция – деление.
%
Операция – остаток от целочисленного деления.
UN_MINUS
Операция – унарный минус.
!
Операция – инверсия.
~
Операция – побитовая инверсия.
,
Разделитель (отделяет параметры в функциях).
(
Выделение приоритета в выражениях и параметров в функциях.
)
Выделение приоритета в выражениях и параметров в функциях.
{
Выделение блока выражений.
}
Выделение блока выражений.
;
Завершение выражения.
Таблица 2: Нетерминальный алфавит Java-подобного языка (N)
Нетерминал
Описание
root
Корень. Все выражения должны сворачиваться в него.
solver
Решение.
solve
Елемент решения.
assign
Присвоение.
expr
Выражение.
prmlst
Список параметров функции.
if
Условие.
if_expr
Выражение условия.
end
Конец условия или программы.
Таблица 3: Грамматика Java-подобного языка
Правило
Описание
root: solver end | error
разрешение и окончание программы; ошибка.
solver: /*empty*/ | solver solve
пустое решение; рекурсия решения.
solve: assign ';' | IF '(' expr ')' if solve end | IF '(' expr ')' if solve end ELSE solve end | FUNC '(' prmlst ')' ';' | '{' solver '}'
присвоение; глобальное короткое условие; глобальное полное условие; процедурный вызов внешней функции; блоки кода в скобках “{“ “}”.
assign: VAR '=' expr | VAR ADDEQ expr | VAR SUBEQ expr | VAR MULEQ expr | VAR DIVEQ expr
простое присвоение; присвоение с добавлением; присвоение с вычитанием; присвоение с умножением; присвоение с делением.
константа; переменная; присвоение переменной внутри выражения; встроенная функция без параметров; встроенная функция с одним параметром; встроенная функция с двумя параметрами; внешняя функция; добавление; вычитание; умножение; деление; остаток от целочисленного деления; побитовое “ИЛИ”; побитовое “И”; побитовое исключающее “ИЛИ”; скобки в выражении; логическое “ИЛИ”; логическое “И”; меньше; больше; больше и равно; меньше и равно; равно; не равно; логическое отрицание; побитовая инверсия; унарный минус; условие внутри выражения;
prmlst: /*empty*/ | expr | prmlst ',' prmlst
пустой список параметров; выражение как параметр; перечень параметров через “,”.
if: /*empty*/
Старт условия (для создания кода условия).
if_expr: '?'
Старт условия внутри выражения.
end: /*empty*/
Конец программы или условия.
На основе этих правил можно построить синтаксический анализатор. Однако, для работы синтаксического анализатора нужен лексический анализатор, который должен выполнять анализ потока входной программы, выделять лексемы и предоставлять их синтаксическому анализатору.
Реализуем лексический анализатор, который должен выполнять следующие функции:
определять позицию символа во входном потоке и передавать её синтаксическому анализатору для сообщения позиции ошибок;
обрабатывать комментарии;
контролировать конец потока программы;
выделять цифры четырёх типов: десятичное, восьмеричное, шестнадцатеричное и вещественное;
выделять строки;
выделять символы по приоритетному списку:
ключевые слова;
константы;
встроенные функции;
внешние функции;
уже зарегистрированные символы переменных;
новые атрибуты системных параметров;
новые параметры функции;
новая автоматическая переменная.
выделять многосимвольные лексемы, исходя из терминального алфавита.
Для упрощения языка будем использовать неявное определение локальных переменных, которое предполагает определение новых переменных во время присвоения ей первого значения. Кроме того, тип локальной переменной должен устанавливаться в соответствии с типом значения, которое впервые было ей присвоено. Например, выражение <Qr=Q0*Pi+0.01;> должно определить переменную Qr типом переменной Q0.
В работе с различными типами данных используем механизм автоматического приведения типов, в местах где такое приведение имеет смысл.
Для комментирования участков кода предусмотрим символы «//». Всё, что идёт после данных символов до конца строки, должно игнорироваться компилятором (лексическим анализатором).
Предусмотрим, чтоб в процессе генерации кода виртуальной машины компилятор выполнял оптимизацию по константам и приведение типов констант к нужному типу.
Под оптимизацией по константам подразумевается выполнение вычислений над двумя константами в процессе построения кода и последующая вставка результата в код. Например, выражение <y=pi*10;> должно свернуться к простому присваиванию <y=31.4159;>.
Под приведением типов констант к необходимому типу подразумевается формирование в коде константы, которая исключает приведение типа в процессе исполнения. Например, выражение <y=x*10> в случае вещественного типа переменной <x>, должно преобразоваться в <y=x*10.0>.
4.4.1 Элементы языка
В результате разработки реализуем следующие элементы языка программирования: Ключевые слова: if, else, true, false. Постоянные:
десятичные -- цифры 0–9 (12,111, 678);
восьмеричные -- цифры 0–7 ( 012, 011, 076);
шестнадцатеричные -- цифры 0–9, буквы a-f или A-F (0x12, 0XAB);
вещественные -- 345.23, 2.1e5, 3.4E-5, 3e6;
логические -- true, false;
строковые -- “hello”.
Типы переменных:
целые -- -231...231;
вещественные -- 3.4 * 10308;
логические -- false, true;
строка -- длина до 256 символов без перехода на другую строку.
Встроенные константы: pi = 3.14159265, e = 2.71828182. Атрибуты параметров системы OpenSCADA. Функции объектной модели системы OpenSCADA.
4.4.2 Операции
Операции, поддержку которых языком программирования мы получим, представлены в таблице 4. Приоритет операций уменьшается сверху вниз. Операции с одинаковым приоритетом входят в одну цветовую группу.
Таблица 4. Операции Java-подобного языка
Символ
Описание
()
Вызов функции.
{}
Программные блоки.
-
Унарный минус.
!
Логическое отрицание.
~
Побитовое отрицание.
*
Умножение.
/
Деление.
%
Остаток от целочисленного деления.
+
Сложение
-
Вычитание
>
Больше
>=
Больше или равно
<
Меньше
<=
Меньше или равно
==
Равно
!=
Неравно
|
Поразрядное «ИЛИ»
&
Поразрядное «И»
^
Поразрядное «Исключающее ИЛИ»
&&
Логический «И»
||
Логический «ИЛИ»
?:
Условная операция (i=(i<0)?0:i;)
=
Присваивание.
+=
Присваивание с сложением.
-=
Присваивание с вычитанием.
*=
Присваивание с умножением.
/=
Присваивание с делением.
4.4.3 Встроенные функции языка
Для обеспечения высокой скорости работы в математических вычислениях модуль предоставляет встроенные математические функции, которые вызываются на уровне команд виртуальной машины. Встроенные математические функции:
sin(x) - синус x;
cos(x) - косинус x;
tan(x) - тангенс x;
sinh(x) - синус гиперболический от x;
cosh(x) - косинус гиперболический от x;
tanh(x) - тангенс гиперболический от x;
asin(x) - арксинус от x;
acos(x) - арккосинус от x;
atan(x) - арктангенс от x;
rand(x) - случайное число от 0 до x;
lg(x) - десятичный логарифм от x;
ln(x) - натуральный логарифм от x;
exp(x) - экспонента от x;
pow(x,y) - возведение x в степень y;
sqrt(x) - корень квадратный от x;
abs(x) - абсолютное значение от x;
sign(x) - знак числа x;
ceil(x) - округление числа x до большего целого;
floor(x) - округление числа x до меньшего целого.
4.4.4 Операторы языка
Языком поддерживаются два типа условных операторов. Первый - это условный оператор для использования внутри выражения, второй - глобальный.
Условный оператор для использования внутри выражения строится на операциях «?» и «:». В качестве примера можно записать следующее практическое выражение <st_open=(pos>=100)?true:false;>, что читается, как «Если переменная <pos> больше или равна 100, то переменной <st_open> присваивается значение "true", иначе "false".
Глобальное условие строится на основе ключевых слов "if" и "else". В качестве примера можно привести то же выражение, но другим способом <if(pos>100) st_open=true; else st_open=false;>. Как видно, выражение записано по-другому, но читается также.
4.4.5 Примеры программы на Java-подобном языке
Приведём несколько примеров программ на разработанном Java-подобном языке:
Результатом работы синтаксического анализатора вместе с лексическим может быть или непосредственно интерпретация(выполнение программы), или генерация кода внутренней виртуальной машины.
Интерпретация не требует дополнительных усилий на разработку виртуальной машины и является хорошим решением в случае не сильно требовательных вычислений.
Виртуальная машина позволяет реализовывать высоко производительные вычисления за счёт оптимизации кода и исключения лишнего анализа лексического и синтаксического анализаторов, однако требует значительных усилий на её разработку.
Поскольку в условиях разработки есть пункт, требующий обеспечения высокопроизводительных и надёжных вычислений, то остановимся на разработке виртуальной машины.
Существует множество способов построения виртуальной машины, из них можно отметить:
стековые машины;
код на основе четверок.
Стековые машины являются удобным способом построения виртуальной машины. Особенностью стековой виртуальной машины является:
операнды любой операции размещаются в стеке;
операция вынимает операнды из стека, выполняет действие и кладёт результат в стек;
выбирается следующая команда и цикл повторяется.
Недостатком стековых машин является то, что стек является динамической структурой, на работу с которой затрачивается относительно много времени, кроме того, для реализации памяти на статические даные отдельного вычислительного сеанса нужно предусматривать отдельную структуру данных.
Другим классом виртуальных машин являются виртуальные машины, основанные на так называемых четверках. Суть четвёрок состоит в том, что команда состоит из: <код> <результат> <операнд1> <операнд2>
Где результат и операнды являются ссылками на регистры памяти или структуры. Код виртуальной машины такого класса получается несколько больше, но структура памяти является статичной и может совмещать функции хранения статических и динамических данных вычисления. Поэтому, для построения виртуальной машины выберем схему, основанную на четверках.
Все даные нашей виртуальной машины будем размещать в регистрах. Регистр виртуальной машины должен представлять структуру, которая может сохранять следующие данные:
значения логического типа;
значения целого типа;
значения вещественного типа;
значения строкового типа;
ссылки на параметр функции;
ссылки на атрибуты параметров подсистемы "DAQ".
Команды построенной виртуальной машины приведено в приложении A. Механизм работы виртуальной машины будет следующим:
Синтаксический анализатор выделяет команды и генерирует код в семантических процедурах Bison. Во время генерации кода виртуальной машины формируется перечень регистров. Регистры, которые содержат ссылки на параметры данной функции, атрибуты параметров подсистемы "DAQ" и внутренние переменные, будут инициализироваться во время генерации кода и больше не использоваться на другие цели. Другие регистры (динамические даные промежуточных вычислений) должны резервироваться и использоваться на временные цели. Такие регистры инициируются конкретными значениями во время выполнения программы в виртуальной машине. Полученная конфигурация регистров (структура памяти) и код программы виртуальной машины используются в режиме вычисления виртуальной машины. При этом конфигурация регистров не меняется, что позволяет использовать быстрые механизмы для доступа к регистрам и первичной инициализации кадра регистров.
Для адресации регистров использован один байт, что обеспечивает поддержку до 255 параметров функции (в сумме с внутренними переменными).
4.6 Разработка языка блочного программирования
Языки блочного программирования основываются на понятии блочных схем. При чем, в зависимости от сущности блока блочные схемы могут быть: логическими схемами, схемами релейной логики, модель технологического процесса и другое. Суть блочной схемы состоит в том, что она содержит список блоков и связи между ними.
С формальной точки зрения блок - это элемент, который имеет входы, выходы и алгоритм вычисления. Исходя из концепции среды программирования и его основы (рис. 2), блок - это кадр значений параметров, ассоциированный с объектом функции.
Разумеется входы и выходы блоков нужно соединять для получения цельной блочной схемы. Предусмотрим следующие типы связей:
межблочные - подключение входа одного блока к выходу другого, а такоже входа одного блока к входу другого;
дальние межблочные - соединение блоков контроллеров данного модуля;
коэффициенты - преобразование входа в константу, все входы/выходы по умолчанию инициированы как константы;
внешний атрибут параметра.
Условно соединения блоков можно изобразить как связи между блоками в целом (рис. 3) или детализация связей (рис. 4). В процессе связывания параметров блоков не будем придерживаться строгого соответствия типам параметров. Это означает, что параметры разных типов смогут легко связываться один с другим. Причем в процессе вычисления будет выполняться автоматическое приведение типов.
Поскольку блочный вычислитель основан на объектах функций объектной модели системы OpenSCADA, то и типы параметров блоков будут такими же, то есть: целое, вещественное, логичное и строка.
Рис. 3. Общие связи между блоками блочной схемы
Рис. 4. Детализированные связи между блоками
4.7 Проектирование архитектуры
Исходя из модульной архитектуры системы OpenSCADA и размышлений, приведенных в разделах выше, была разработана архитектура среды программирования (рис.5).
Рис. 5. Общая структура среды программирования
Как можно видеть, архитектура среды программирования состоит из трёх частей:
API объектной модели системы OpenSCADA с декларацией основных объектов;
библиотеки функций;
вычислительные контроллеры.
Такая архитектура среды программирования позволяет распределить процесс создания и использования. То есть, алгоритмы вычислений в виде объектов функций предоставляются одними компонентами, а используются для вычислений другими.
При такой схеме должна существовать прослойка, которая объединяет компоненты, предоставляющие алгоритмы с компонентами, которые их используют, особенно это важно в свете того, что эти компоненты могут быть отделены от системы, то есть являться модулями. Этим слоем и должен выступать API объектной модели системы OpenSCADA.
Исходя из структуры среды программирования, создадим классы объектов. Статическую диаграмму классов с отделением каждого компонента среды программирования приведено на рис.6. Описание классов приведено в таблице 5.
Рис. 6. Статическая диаграмма классов среды программирования
Таблица 5. Классы среды программирования
Класс
Ответственность
Связи
TFunction
Класс функции. Содержит описание параметров (IO). В предке должен содержать реализацию алгоритма функции.
Используется вычислителями для связывания с кадром значений. Является абстрактным, наследуется компонентами, которые предоставляют собственные библиотеки функций.
TValFunc
Класс значений функции. Содержит значения функции в соответствии с составом параметров (IO) класса TFunction.
Агрегируется с объектом функции TFunction для совместных вычислений. Может наследоваться классами вычислителя. Экземпляр класса TValFunc передаётся классу TFunction во время вычисления для выполнения алгоритма в TFunction над значениями в TValFunc.
IO
Класс параметра функции. Содержит описание параметра, его тип и атрибуты.
Используется классом значений TValFunc для определения значений параметров.
UserLib
Класс библиотеки пользователя.
Может предоставлять инструмент создания функций пользователя.
UserFunc
Класс функции пользователя. Предоставляет параметры функции и алгоритм вычисления.
Наследует класс функции TFunction.
Block
Пользовательский класс вычислителя. Содержит ассоциированный с функцией TFunction кадр значений TValFunc. Вызывает процесс вычисления
Наследует класс кадра значений.
4.7.1 Архитектура вычислителя на Java-подобном языке
Исходя из структуры среды программирования (рис.5) и её диаграммы классов (рис.6), создадим классы вычислителя на Java-подобном языке (рис.7). Описание классов приведено в таблице 6.
Рис. 7. Статическая диаграмма классов модуля "JavaLikeCalc"
Таблица 6. Классы модуля JavaLikeCalc
Класс
Ответственность
Связи
TipContr
Коренной класс модуля, главным назначением которого является точка входа в модуль.
Наследует класс интерфейса модулей подсистемы «Источников данных» TTypeDAQ для интеграции в систему OpenSCADA. Содержит:
библиотеки функций предоставляемые этим модулем;
объекты классов (Bfunc) встроенных в язык функций;
объекты классов (NConst) именованных констант языка.
Contr
Класс реализации контроллера. Содержит механизм периодических вычислений над алгоритмом функции этого модуля.
Наследует класс интерфейса контроллера TController. Наследует класс кадра значений функции вычисления TValFunc.
Prm
Класс реализации параметра. Содержит механизм отображения таблицы данных контроллера на структуру параметра системы OpenSCADA.
Наследует класс интерфейса параметра TParamСontr. Содержит ссылки на таблицу данных контроллера.
Lib
Класс библиотеки функций данного модуля. Содержит механизм создания функций на Java-подобном языке.
Содержит функции Func данной библиотеки.
Func
Класс реализации функции. Содержит механизм создания параметров функции и программы вычисления. Также содержит механизм вычисления в виде виртуальной машины и компилятор для построения программы виртуальной машины.
Наследует класс интерфейса функции TFunction. Содержит:
рабочие регистры;
регистры вычисления;
ссылки на использованные внешние функции.
BFunc
Класс встроенной функции. Содержит описание и количество параметров функции.
Содержится в классе TTipContr. Используется компилятором и виртуальной машиной в TFunc.
NConst
Класс именованной константы. Содержит описание и значение константы.
Содержиться в классе TTipContr. Используется компилятором и виртуальной машиной в TFunc.
Reg
Класс регистра виртуальной машины. Содержит информацию про тип регистра, его содержимое и другую информацию, необходимую для построения программы виртуальной машины.
Содержится в классе функции Func. Используется компилятором и виртуальной машиной.
RegW
Класс рабочего регистра виртуальной машины. Содержит только данные про тип и значение регистра, которых достаточно для работы виртуальной машины.
Содержится в классе функции Func. Используется виртуальной машиной во время вычисления.
UFunc
Класс внешней функции, который используется программой виртуальной машины.
Содержится в классе функции Func. Используется виртуальной машиной во время вычисления.
Для непосредственного вычисления нужно обеспечить создание и связывание контроллера с функцией этого же модуля. Для связывания с функцией в контроллере создаётся кадр значений TValFunc, над которым и производятся периодические вычисления.
Для экспорта полученных значений из контроллера в систему OpenSCADA и для импорта значений из системы в контроллер нужно использовать параметры контроллера подсистемы "DAQ". Параметры контроллера связываются с параметром вычислительной функции (полем таблицы данных) и должны выполнять отражение значений.
4.7.2 Архитектура вычислителя на языке блоков
Исходя из структуры среды программирования (рис.5) и её диаграммы классов (рис.6), создадим классы вычислителя на языке блоков (рис.8). Описание классов приведено в таблице 7.
Рис. 8. Статическая диаграмма классов модуля "BlockCalc"
Таблица 6. Классы модуля BlockCalc
Класс
Ответственность
Связи
TipContr
Коренной класс модуля, главным назначением которого является точка входа в модуль.
Наследует класс интерфейса модулей подсистемы «Источников данных» TTypeDAQ для интеграции в систему OpenSCADA.
Contr
Класс реализации контроллера. Содержит механизм периодических вычислений над алгоритмом блочной схемы.
Наследует класс интерфейса контроллера TController. Содержит блоки блочной схемы.
Prm
Класс реализации параметра подсистемы "DAQ". Содержит механизм отражения параметров блоков блочной схемы на структуру параметра подсистемы "DAQ" системы OpenSCADA.
Наследует класс интерфейса параметра TParamСontr. Содержит ссылки на параметр блока блочной схемы.
Block
Класс блока. Содержит кадр значений в соответствии с ассоциированной функцией. Содержит механизм связей.
Наследует класс интерфейса кадра значений TValFunc. На него ссылается объект класса параметра (Prm). Экземпляры класса этого типа могут содержать связи один на другого.
Каждый контроллер этого модуля содержит блочную схему, которую он должен вычислять в соответствии с указанной периодичностью.
Сами блоки при этом не содержат структуры входов/выходов(IO), а только содержат значения, исходя из структуры параметров связанной функции. Для соединения с блоком могут использоваться любые функции объектной модели (ОМ) системы OpenSCADA.
Для предоставления возможности экспортирования значений из блочной схемы в систему OpenSCADA предусмотрим возможность отображения атрибутов блоков на параметры контролера системы OpenSCADA. Таким же образом значения из системы OpenSCADA могут попасть в блочную схему контроллера.
4.7.3 Стандартная архитектура библиотек статических функций
Исходя из структуры среды программирования (рис.5) и её диаграммы классов (рис.6), создадим архитектуру классов для библиотек статических функций (рис.9). Описание классов приведено в таблице 8.
рис. 9. Статическая диаграмма классов статических библиотек функций
Таблица 7. Классы статической библиотеки функций
Класс
Ответственность
Связи
Lib
Коренной класс модуля, главным назначением которого является точка доступа в модуль. Выполняет функцию библиотеки, а значит содержит объекты статических функций.
Наследует класс интерфейса модулей подсистемы «Специальные» TSpecial для интеграции в систему OpenSCADA. Содержит объекты функций.
Func
Класc функции. Содержит структуру параметров и алгоритм их вычисления.
Наследует класс интерфейса функции TFunction.
На основе данной архитектуры построены следующие статические библиотеки функций:
FLibComplex1 - библиотека функций совместимости с SCADA Complex1 (таблица 8);
FLibMath - библиотека стандартных математических функций (таблица 9);
FLibTime - библиотека функций для работы с временем (таблице 10);
Таблица 8. Функции библиотеки Complex1
Id
Имя
Описание функции. Формулы вычислений функций
alarm
Сигнал
Сигнал по шкале параметра:
out = if(val>max || val<min) then true; else false;
cond <
Условие '<'
Условие '<' по формуле:
out=if(in1<(in2_1*in2_2*in2_3*in2_4)) then in3_1*in3_2*in3_3*in3_4; else in4_1*in4_2*in4_3*in4_4;
cond >
Условие '>'
Условие '>' по формуле:
out=if(in1>(in2_1*in2_2*in2_3*in2_4)) then in3_1*in3_2*in3_3*in3_4; else in4_1*in4_2*in4_3*in4_4;
cond_full
Полное условие
Полное условие по формуле:
out = if(in1<(in2_1*in2_2*in2_3*in2_4)) then in3_1*in3_2*in3_3*in3_4; else if( in1>(in4_1*in4_2*in4_3*in4_4) then in5_1*in5_2*in5_3*in5_4; else in6_1*in6_2*in6_3*in6_4;
out = if( sel = 1 ) then in1_1*in1_2*in1_3*in1_4; if( sel = 2 ) then in2_1*in2_2*in2_3*in2_4; if( sel = 3 ) then in3_1*in3_2*in3_3*in3_4; if( sel = 4 ) then in4_1*in4_2*in4_3*in4_4;
out = in1_1*in1_2*(in1_3+in1_4/in1_5) + in2_1*in2_2*(in2_3+in2_4/in2_5) + in4_1*in4_2*(in4_3+in4_4/in4_5) + in5_1*in5_2*(in5_3+in5_4/in5_5);
sum_mult
Сумма с умножением
Сумма с умножением по формуле:
out = in1_1*in1_2*(in1_3*in1_4+in1_5) + in2_1*in2_2*(in2_3*in2_4+in2_5) + in4_1*in4_2*(in4_3*in4_4+in4_5) + in5_1*in5_2*(in5_3*in5_4+in5_5);
Таблица 9. Стандартные математические функции
Id
Имя
Описание
abs
Модуль
Мат. функция - модуль от числа.
acos
Арккосинус
Мат. функция - арккосинус.
asin
Арксинус
Мат. функция - арксинус.
atan
Арктангенс
Мат. функция - арктангенс.
ceil
Округл. до большего
Мат. функция - округления до большего целого.
cos
Косинус
Мат. функция - косинус.
cosh
Косинус гиперболический
Мат. функция - косинус гиперболический.
exp
Экспонента
Мат. функция - экспонента.
floor
Округл. до меньшего
Мат. функция - округления до меньшего целого
if
Условие Если
Функция - условие "Если".
lg
Десятичный логарифм
Мат. функция - десятичный логарифм.
ln
Натуральный логарифм
Мат. функция - натуральный лагорифм.
pow
Степень
Мат. функция - возведение в степень.
rand
Случ. число
Мат. функция - генератор случайных чисел.
sin
Синус
Мат. функция - синус.
sinh
Синус гиперболический
Мат. функция - синус гипербалический.
sqrt
Корень квадратный
Мат. функция - корень квадратный.
tan
Тангенс
Мат. функция - тангенс.
tanh
Тангенс гиперболический
Мат. функция - тангенс гиперболический.
Таблица 10. Функции времени
Id
Имя
Описание
date
Полная дата
Возвращает полную дату в составе: секунда, минута, час, день месяца, месяц, год, день недели, день в году.
time
Полное время (с 01.01.1970)
Возвращает полное время в виде числа секунд от начала эпохи
ctime
Полное время в виде строки
Возвращает строку полного времени вида "Wed Jun 30 21:49:08 1993".
5. Реализация
Начальную реализацию среды программирования в коде было выполнено с помощью полученной UML-модели путём генерации C++ кода в программе Umbrello. В результате был разработан интерфейс объектной модели (API) и компоненты в виде модулей к системе OpenSCADA:
модуль “JavaLikeCalc” подсистемы “DAQ”;
модуль “BlockCalc” подсистемы “DAQ”;
модуль “FLibComplex1” подсистемы “Специальные”;
модуль “FLibMath” подсистемы “Специальные”;
модуль “FLibTime” подсистемы “Специальные”.
Общую структуру всех этих компонентов и API приведено на рис.10.
рис. 10. Общая структура компонентов среды программирования
ПРИЛОЖЕНИЕ A. Команды внутренней виртуальной машины
Таблица. Команды внутренней виртуальной машины модуля “JavaLikeCalc”
Команда
Код
Описание
End
01
Окончание программы или условной команды.
MviB
02 R V
Загрузка логического признака [V] в регистр [R]. Производится инициализация регистра.
MviI
03 R V V V V
Загрузка целого числа [V V V V] в регистр [R]. Производится инициализация регистра.
MviR
04 R V V V V V V
Загрузка вещественного числа [V V V V V V] в регистр [R]. Производится инициализация регистра.
MviS
05 R N S . .
Загрузка строки [S . .] длиной [N] в регистр [R]. Производится инициализация регистра.
AssB
06 R0 R1
Присвоение логического признака из регистра [R1] регистру [R0].
AssI
07 R0 R1
Присвоение целого из регистра [R1] регистру [R0].
AssR
08 R0 R1
Присвоение вещественного из регистра [R1] регистру [R0].
AssS
09 R0 R1
Присвоение строки из регистра [R1] регистру [R0].
MovB
0A R0 R1
Перемещение логического признака из регистра [R1] в регистр [R0]. Производится инициализация регистра.
MovI
0B R0 R1
Перемещение целого из регистра [R1] в регистр [R0]. Производится инициализация регистра.
MovR
0C R0 R1
Перемещение вещественного из регистра [R1] в регистр [R0]. Производится инициализация регистра.
MovS
0D R0 R1
Перемещение строки из регистра [R1] в регистр [R0]. Производится инициализация регистра.
AddI
OE R0 R1 R2
Сложение целых: R0 = R1 + R2.
AddR
OF R0 R1 R2
Сложение вещественных: R0 = R1 + R2.
AddS
1O R0 R1 R2
Сложение строк: R0 = R1 + R2.
SubI
11 R0 R1 R2
Вычитание целых: R0 = R1 - R2.
SubR
12 R0 R1 R2
Вычитание вещественных: R0 = R1 - R2.
MultI
13 R0 R1 R2
Перемножение целых: R0 = R1 * R2.
MultR
14 R0 R1 R2
Перемножение вещественных: R0 = R1 * R2.
DivI
15 R0 R1 R2
Деление целых: R0 = R1 / R2.
DivR
16 R0 R1 R2
Деление вещественных: R0 = R1 / R2.
RstI
17 R0 R1 R2
Остаток от целочисленного деления: R0 = R1 % R2.
BitOr
18 R0 R1 R2
Побитовое “ИЛИ”: R0 = R1 | R2.
BitAnd
19 R0 R1 R2
Побитовое “И”: R0 = R1 & R2.
BitXor
1A R0 R1 R2
Побитовое исключающее “ИЛИ”: R0 = R1 ^ R2.
LOr
1B R0 R1 R2
Логическое “ИЛИ”: R0 = R1 || R2.
LAnd
1C R0 R1 R2
Логическое “И”: R0 = R1 && R2.
LTI
1D R0 R1 R2
Целое меньше: R0 = R1 < R2.
LTR
1E R0 R1 R2
Вещественое меньше: R0 = R1 < R2.
GTI
1F R0 R1 R2
Целое больше: R0 = R1 > R2.
GTR
20 R0 R1 R2
Вещественное больше: R0 = R1 > R2.
LEI
21 R0 R1 R2
Целое меньше или равно: R0 = R1 <= R2.
LER
22 R0 R1 R2
Вещественное меньше или равно: R0 = R1 <= R2.
GEI
23 R0 R1 R2
Целое больше или равно: R0 = R1 >= R2.
GER
24 R0 R1 R2
Вещественное больше или равно: R0 = R1 >= R2.
EQI
25 R0 R1 R2
Целое равно: R0 = R1 == R2.
EQR
26 R0 R1 R2
Вещественное равно: R0 = R1 == R2.
EQS
27 R0 R1 R2
Строка равно: R0 = R1 == R2.
NEI
28 R0 R1 R2
Целое не равно: R0 = R1 != R2.
NER
29 R0 R1 R2
Вещественное не равно: R0 = R1 != R2.
NES
2A R0 R1 R2
Строка не рано: R0 = R1 != R2.
Not
2B R0 R1
Отрицание: R0 = !R1.
BitNot
2С R0 R1
Побитовая инверсия: R0 = ~R1.
NegI
2D R0 R1
Инверсия целого: R0 = -R1.
NegR
2E R0 R1
Инверсия вещественного: R0 = -R1.
If
2F R E E N N
Условие, если [R] действительно, то исполняются команды после этой операции и по завершению выполняется переход по относительному адресу [N N]; иначе выполняются команды по относительному адресу [EE] и по завершению выполняется переход по относительному адресу [N N].
FSin
30 R0 R1
Функция синус: R0 = sin(R1).
FCos
31 R0 R1
Функция косинус: R0 = cos(R1).
FTan
32 R0 R1
Функция тангенс: R0 = tan(R1).
FSinh
33 R0 R1
Функция гиперболический синус: R0 = sinh(R1).
FCosh
34 R0 R1
Функция гиперболический косинус: R0 = cosh(R1).
FTanh
35 R0 R1
Функция гиперболический тангенс: R0 = tanh(R1).
FAsin
36 R0 R1
Функция арксинус: R0 = asin(R1).
FAcos
37 R0 R1
Функция арккосинус: R0 = acos(R1).
FAtan
38 R0 R1
Функция арктангенс: R0 = atan(R1).
FRand
39 R0 R1
Случайное число: R0 = rand(R1).
FLg
3A R0 R1
Десятичный логарифм: R0 = lg(R1).
FLn
3B R0 R1
Натуральный логарифм: R0 = ln(R1).
FExp
3С R0 R1
Экспонента: R0 = exp(R1).
FPow
3D R0 R1 R2
Возведение в степень: R0 = pow(R1,R2).
FSqrt
3E R0 R1
Корень квадратный: R0 = sqrt(R1).
FAbs
3F R0 R1
Абсолютное значение: R0 = |R1|.
FSign
40 R0 R1
Знак: R0 = sign(R1).
FCeil
41 R0 R1
Округление до большего: R0 = ceil(R1).
FFloor
42 R0 R1
Округление до меньшего: R0 = floor(R1).
CProc
43 F N R P P . .
Процедурный вызов внешней функции [F], с параметрами [P P . .], в количестве [N]. [R] - не используется.
CFunc
44 F N R P P . .
Вызов внешней функции [F], с параметрами [P P . .], в количестве [N]. Результат функции помещается в [R].