Модуль | Имя | Версия | Лицензия | Источник | Языки | Платформы | Тип | Автор | Описание |
---|---|---|---|---|---|---|---|---|---|
JavaLikeCalc | Вычислитель на Java-подобном языке. | 5.6 | GPL2 | daq_JavaLikeCalc.so | en,uk,ru,de | x86,x86_64,ARM | DAQ | Роман Савоченко | Предоставляет вычислитель и движок библиотек, основанные на Java-подобном языке. Пользователь может создавать и модифицировать функции и их библиотеки. |
Модуль источника данных предоставляет в OpenSCADA механизм создания функций и их библиотек на Java-подобном языке. Описание функции на Java-подобном языке сводится к обвязке параметров функции алгоритмом. Кроме этого модуль наделён функциями непосредственных вычислений путём создания объектов вычислительных контроллеров.
Непосредственные вычисления обеспечиваются созданием объекта контроллера и связыванием его с функцией этого же модуля. Для связанной функции создаётся кадр значений (контекст), над которым и выполняются периодические вычисления.
Модулем реализуются функции горизонтального резервирования, а именно — совместная работа с удалённой станцией этого-же уровня. Кроме синхронизации значений и архивов атрибутов параметров, модулем осуществляется синхронизация значений вычислительной функции, с целью безударного "подхвата" алгоритмов.
Parameters of functions can be freely created, deleted or modified. The current version of the module supports up to 65535 parameters of the function, in the sum with the internal variables. The view of editing functions in the OpenSCADA configurator is shown in Figure 1. The parameter name's rows after the first one treat as help.
После любого изменения текста программы или конфигурации параметров, выполняется перекомпиляция программы с уведомлением объектов значений TValCfg, связанных с функцией. Компилятор языка построен с использованием известного генератора грамматики "Bison", который совместим с не менее известной утилитой "Yacc".
Язык использует неявное определение локальных переменных, которое заключается в определении новой переменной в случае присваивания ей значения. Причём тип локальной переменной устанавливается в соответствии с типом присваиваемого значения. Например, выражение Qr=Q0*Pi+0.01; определит переменную Qr с типом переменной Q0.
В работе с различными типами данных язык использует механизм автоматического приведения типов в местах, где подобное приведение является целесообразным.
Для комментирования участков кода в языке предусмотрены символы "//" и "/* ... */". Всё, что идёт после "//", до конца строки, и между "/* ... */", игнорируется компилятором.
В процессе генерации кода, компилятор языка производит оптимизацию по константам и приведение типов констант к требуемому типу. Под оптимизацией констант подразумевается вычисление двух констант и вставка результата в код, в процессе построения байт-кода. Например, выражение y=pi*10; свернётся в простое присваивание y=31.4159;. Под приведением типов констант к требуемому типу подразумевается формирования константы в коде, которая исключает приведение типа в процессе исполнения. Например, выражение y=x*"10";, в случае вещественного типа переменной x, преобразуется в y=x*10;.
Выражения присваивания могут записываться через символ ',', например:
var1 = 1, var2 = 3, var4 = var1+var2;
for(var1 = 0, var2 = 0, var3 = -1; var1 < 10; var1++, var2++) var3++;
Язык поддерживает вызовы внешних и внутренних функций. Имя любой функции, вообще, воспринимается как символ, проверка которого на принадлежность к той или иной категории производится в следующем порядке:
Вызов внешней функции, как и глобального атрибута параметра DAQ, записывается в виде адреса к узлу объектной модели OpenSCADA: "DAQ.JavaLikeCalc.lib_techApp.klapNotLin". Для статических функций Вы можете следующим образом осуществлять динамическое подключение:
function klapNotLin = "DAQ.JavaLikeCalc.lib_techApp.klapNotLin";
rez = klapNotLin(prm1, prm2, ..., prmN);
Для предоставления возможности написания пользовательских процедур управления различными компонентами OpenSCADA, этим модулем предоставляется реализация API прекомпиляции пользовательских процедур отдельных компонентов OpenSCADA на Java-подобном языке. Такими компонентами, например, являются: шаблоны параметров подсистемы "Сбор данных" и среда визуализации и управления (СВУ).
Ключевые слова: if, else, while, for, in, break, continue, return, function, using.
Постоянные:
Типы переменных:
Встроенные константы: pi = 3.14159265..., e = 2.71828182..., EVAL_BOOL(2), EVAL_INT(-9223372036854775807), null,EVAL,EVAL_REAL(-1.79E308), EVAL_STR("<EVAL>")
Глобальные атрибуты параметров DAQ (начиная с подсистемы "DAQ" и в виде {Тип модуля DAQ}.{Объект контроллера}.{Параметр}.{Атрибут}).
Функции и параметры объектной модели OpenSCADA.
The EVAL (Error VALue) variants and null are processed specially in conversion one to one depending on used the base type, that is you are free in use only null or EVAL for any cases.
Операции, поддерживаемые языком, представлены в таблице ниже. Приоритет операций уменьшается сверху вниз. Операции с одинаковым приоритетом входят в одну цветовую группу.
Символ | Описание |
() | Вызов функции. |
{} | Программные блоки. |
++ | Инкремент (пост и пре). |
-- | Декремент (пост и пре). |
- | Унарный минус. |
! | Логическое отрицание. |
~ | Побитовое отрицание. |
* | Умножение. |
/ | Деление. |
% | Остаток от целочисленного деления. |
+ | Сложение |
- | Вычитание |
<< | Поразрядный сдвиг влево |
>> | Поразрядный сдвиг вправо |
> | Больше |
>= | Больше или равно |
< | Меньше |
<= | Меньше или равно |
== | Равно |
!= | Не равно |
| | Поразрядное "ИЛИ" |
& | Поразрядное "И" |
^ | Поразрядное "Исключающее ИЛИ" |
&& | Логический "И" |
|| | Логический "ИЛИ" |
?: | Условная операция "i=(i<0)?0:i;" |
= | Присваивание. |
+= | Присваивание со сложением. |
-= | Присваивание с вычитанием. |
*= | Присваивание с умножением. |
/= | Присваивание с делением. |
Виртуальной машиной языка предусматривается следующий набор встроенных функций общего назначения:
Для обеспечения высокой скорости работы в математических вычислениях модуль предоставляет встроенные математические функции, которые вызываются на уровне команд виртуальной машины:
Общий перечень операторов языка:
Языком поддерживаются два типа условий. Первый — это операции условия для использования внутри выражения, второй — глобальный, основанный на условных операторах.
Условие внутри выражения строится на операциях '?' и ':'. В качестве примера можно записать следующее практическое выражение:
st_open = (pos >= 100) ? true : false;
Что читается как — если переменная pos больше или равна 100, то переменной st_open присваивается значение true, иначе — false.
Глобальное условие строится на основе условных операторов "if" и "else". В качестве примера можно привести тоже выражение, но записанное другим способом:
if(pos > 100) st_open = true; else st_open = false;
Поддерживаются три типа циклов: while, for и for-in. Синтаксис циклов соответствует языкам программирования: C++, Java и JavaScript.
Цикл while, в общем, записывается следующим образом: while({условие}) {тело цикла};
Цикл for записывается следующим образом: for({пре-инициализ};{условие};{пост-вычисление}) {тело цикла};
Цикл for-in записывается следующим образом: for({переменная} in {объект}) {тело цикла};
Где:
Язык поддерживает определение и вызов внутренних функций. Для определения внутренней функции используется ключевое слово "function" и в целом определение имеет синтаксис: function {имяФ} ({пер1}, {пер2}, ... {перN}) { {тело функции} }. Определение внутренней функции внутри другой недопустимо однако допустим вызов ранее определённой.
Вызов внутренней функции осуществляется в типовой способ, как процедура {имяФ}({var1}, {var2}, ... {varN}); или как функция {перРез} = {имяФ}({пер1}, {пер2}, ... {перN});. Вызов внутренних функций допустим только после их декларации выше!
Все переменные, определённые в основном теле, недоступны в середине внутренних функций и могут быть переданы через двухсторонние аргументы вызываемой внутренней функции или через аргументы основной функции. Все переменные, определённые в середине внутренней функции, имеют собственную область имён и недоступны из основного тела, или любой другой внутренней функции, и могут быть переданы в основное тело через двухсторонние аргументы, результат вызываемой внутренней функции или через аргументы основной функции. Переменные внутренней функции регистрируются для сохранения/восстановления их контекста после второго и более входа в функцию, т.е. они целиком поддерживают рекурсивные вызовы!
Оператор "return", в середине внутренней функции, осуществляет контролируемое её завершение и помещение указанной переменной, или результата выражения, как результат вызываемой внутренней функции.
Пример типового определения и использования внутренней функции представлен далее:
function sum(a, b, c, d) { return a + ((b==null)?0:b) + ((c==null)?0:c) + ((d==null)?0:d); }
rez = sum(1, 2);
Языком предусмотрена поддержка следующих специальных символов строковых переменных:
JavaLikeCalc предоставляет поддержку типа данных объект "Object". Объект представляет собой ассоциативный контейнер свойств и функций. Свойства могут содержать как данные четырёх базовых типов, так и другие объекты. Доступ к свойствам объекта может осуществляться посредством записи имён свойств к объекту obj.prop, через точку, а также посредством заключения имени свойства в квадратные скобки obj["prop"]. Очевидно, что первый механизм статичен, а второй позволяет указывать имя свойства через переменную. Удалить свойство объекта можно директивой "delete". Имя свойства через точку не должно начинаться с цифры и содержать символы операций, иначе, для первой цифры, должен использоваться префикс объекта — SYS.BD.SQLite.db_1s, или осуществляться запись в квадратных скобках — SYS.BD.SQLite["1+s"], для символов операций в названии. Чтение неопределённого свойства вернёт null-EVAL. Создание объекта осуществляется посредством ключевого слова new: varO = new Object(). Базовое определение объекта не содержит функций. Операции копирования объекта, на самом деле, делают ссылку на исходный объект. При удалении объекта осуществляется уменьшение счётчика ссылок, а при достижении счётчика нуля, объект удаляется физически.
Разные компоненты OpenSCADA могут доопределять базовый объект особыми свойствами и функциями. Стандартным расширением объекта является массив "Array", который создаётся командой varO = new Array(prm1,prm2,prm3,...,prmN). Перечисленные через запятую параметры помещаются в массив в исходном порядке. Если параметр только один то массив инициируется указанным количеством пустых элементов. Особенностью массива является то, что он работает со свойствами, как с индексами и основным механизмом обращения является заключение индекса в квадратные скобки arr[1]. Массив хранит свойства в собственном контейнере одномерного массива. Цифровые свойства массива используются для доступа непосредственно к массиву, а символьные работают как свойства объекта. Детальнее про свойства и функции массива можно прочитать по ссылке.
Объект регулярного выражения "RegExp" создаётся командой varO = new RegExp(pat, flg), где pat — шаблон регулярного выражения, а flg — флаги поиска. Объект работы с регулярными выражениями основан на библиотеке "PCRE". При глобальном поиске устанавливается атрибут объекта "lastIndex", что позволяет продолжить поиск при следующем вызове функции. В случае неудачного поиска атрибут "lastIndex" сбрасывается в ноль. Детальнее про свойства и функции объекта регулярного выражения можно прочитать по ссылке.
Для произвольного доступа к аргументам функции предусмотрен объект аргументов, обратиться к которому можно посредством символа "arguments". Этот объект содержит свойство "length" с количеством аргументов у функции и позволяет обратиться к значению аргумента посредством его номера или идентификатора. Рассмотрим перебор аргументов по циклу:
args = new Array();
for(var i = 0; i < arguments.length; i++)
args[i] = arguments[i];
Частичными свойствами объекта обладают и базовые типы. Свойства и функции базовых типов приведены ниже:
var rez = "Java123Script".search("script","i"); // rez = 7
var rez = "Java123Script".search(new RegExp("script","i")); // rez = 7
var rez = "1 plus 2 plus 3".match("\\d+","g"); // rez = [1], [2], [3]
var rez = "1 plus 2 plus 3".match(new RegExp("\\d+","g")); // rez = [1], [2], [3]
rez = "1,2, 3 , 4 ,5".split(new RegExp("\\s*,\\s*")); // rez = [1], [2], [3], [4], [5]
rez = "Javascript".replace(4,3,"67"); // rez = "Java67ipt"
rez = "123 321".replace("3","55"); // rez = "1255 5521"
rez = "value = \"123\"".replace(new RegExp("\"([^\"]*)\"","g"),"``$1''")); // rez = "value = ``123''"
Для доступа к системным объектам(узлам) OpenSCADA предусмотрен соответствующий объект, который создаётся путём простого указания точки входа "SYS" корневого объекта OpenSCADA, а затем, через точку, указываются вложенные объекты, в соответствии с иерархией. Например, вызов функции запроса через исходящий транспорт осуществляется следующим образом: SYS.Transport.Sockets.out_testModBus.messIO(Special.FLibSYS.strEnc2Bin("15 01 00 00 00 06 01 03 00 00 00 05"));.
Приведём несколько примеров программ на Java-подобном языке:
//Модель хода исполнительного механизма шарового крана
if(!(st_close && !com) && !(st_open && com))
{
tmp_up = (pos>0&&pos<100) ? 0 : (tmp_up>0&&lst_com==com) ? tmp_up-1/frq : t_up;
pos += (tmp_up>0) ? 0 : (100*(com?1:-1))/(t_full*frq);
pos = (pos>100) ? 100 : (pos<0) ? 0 : pos;
st_open = (pos>=100) ? true : false;
st_close = (pos<=0) ? true : false;
lst_com = com;
}
//Модель клапана
Qr = Q0 + Q0*Kpr*(Pi-1) + 0.01;
Sr = (S_kl1*l_kl1+S_kl2*l_kl2)/100;
Ftmp = (Pi>2*Po) ? Pi*pow(Q0*0.75/Ti,0.5) : (Po>2*Pi) ? Po*pow(Q0*0.75/To,0.5) : pow(abs(Q0*(pow(Pi,2)-pow(Po,2))/Ti),0.5);
Fi -= (Fi-7260*Sr*sign(Pi-Po)*Ftmp)/(0.01*lo*frq);
Po += 0.27*(Fi-Fo)/(So*lo*Q0*frq);
Po = (Po<0) ? 0 : (Po>100) ? 100 : Po;
To += (abs(Fi)*(Ti*pow(Po/Pi,0.02)-To)+(Fwind+1)*(Twind-To)/Riz)/(Ct*So*lo*Qr*frq);
Объект контроллера этого модуля, для обеспечения непосредственных вычислений, связывается с функциями из библиотек, построенных с его помощью, или с шаблоном сбора данных. В случае с шаблоном сбора данных, добавляется возможность внешнего связывания, с другими параметрами и атрибутами подсистемы "Сбор данных". Для предоставления вычисленных данных в OpenSCADA, в объекте контроллера могут создаваться параметры. Пример вкладки конфигурации объекта контроллера данного типа изображен на рисунке 2.
From this tab you can set:
Вкладка "Вычисления" объекта контроллера (Рис. 3) содержит параметры и текст программы, непосредственно выполняемой контроллером. Модулем предусмотрена обработка ряда специальных параметров, доступных в программе контроллера:
The module provides only one the "Standard (std)" type of the parameters with the parameters table name "JavaLikePrm_{CntrId}".
Параметр объекта контроллера данного модуля выполняет функцию предоставления доступа к результатам вычисления контроллера в OpenSCADA, посредством атрибутов параметров. Из специфических полей, вкладка конфигурации параметра контроллера содержит только поле перечисления параметров вычисляемой функции, которые необходимо отразить.
Модуль предоставляет механизм для создания библиотек пользовательских функций на Java-подобном языке. Пример вкладки конфигурации библиотеки изображен на рисунке 4. Вкладка содержит базовые поля: доступность, адрес таблицы БД библиотеки (с отслеживанием наличия данных в различных хранилищах и предоставлением последовательного удаления дубликатов), дата и время модификации, идентификатор, имя и описание.
Функция, также как и библиотека, содержит базовую вкладку конфигурации, вкладку формирования программы и параметров функции (Рис.1), а также вкладку исполнения созданной функции.
Некоторые объекты модуля предоставляют функции пользовательского программирования.
Объект "Библиотека функций" (SYS.DAQ.JavaLikeCalc["lib_Lfunc"])
Объект "Пользовательская функция" (SYS.DAQ.JavaLikeCalc["lib_Lfunc"]["func"])
Service functions are an interface for accessing OpenSCADA from external systems through the Control Interface. This mechanism is the basis of all exchange within OpenSCADA, implemented through weak links and OpenSCADA's own exchange protocol.
Getting for values of the function IO of the controller object
REQ: <get path="/DAQ/JavaLikeCalc/{CNTR}/%2fserv%2ffncAttr" />
RESP: <get path="/DAQ/JavaLikeCalc/{CNTR}/%2fserv%2ffncAttr" rez="0">{IOs}</get>
<get path="/DAQ/JavaLikeCalc/testCalc/%2fserv%2ffncAttr" rez="0" user="roman">
<a id="f_frq">0.1</a>
<a id="f_start">0</a>
<a id="f_stop">0</a>
<a id="this"><TCntrNodeObj path="/sub_DAQ/mod_JavaLikeCalc/cntr_testCalc/"/></a>
<a id="offset">100</a>
<a id="out">50</a>
<a id="test">1</a>
<a id="rez" />
<a id="inFarg">3</a>
</get>
Setting for values of the function IO of the controller object
REQ[root-DAQ]: <set path="/DAQ/JavaLikeCalc/{CNTR}/%2fserv%2ffncAttr">{IOs}</set>
<set path="/DAQ/JavaLikeCalc/testCalc/%2fserv%2ffncAttr">
<a id="out">50</a>
<a id="test">1</a>
</set>
Исходный текст процедур на языке этого модуля компилируются в байт-код, который впоследствии вычисляется виртуальной машиной. Байт-код это не машинный код и достичь производительности самой аппаратной архитектуры в виртуальной машине его исполняющего — теоретически нереально, если конечно код этой виртуальной машины не исполняет сам процессор. Т.е. производительность выполнения байт-кода примерно на порядок ниже аппаратной производительности за счёт накладных расходов команд виртуальной машины, разделения многопоточного доступа к данным, прозрачного приведения типов и отсутствия жёсткой типизации, а так-же динамической природы языка и наличия сложных типов "Строка" и "Объект".
28.01.2006:
Description: Initial estimation of the productivity of the OpenSCADA virtual machine in example of the expression y=x1+x2, where all the variables are global and in the float-point type.
Stage | Action | K7_1G-0, us |
---|---|---|
1 | The registers list initialization | 2.3 |
2 | Entry to the function exec() | 3 |
3 | The commands coming | 4.4 |
4 | Reading | 9 |
5 | Full time | 10.2 |
17.07.2013:
Description: Justification of the current performance evaluation and optimization. The measurements were made by sampling the minimum time from five calls to 1000 executions of the formula a -= b*(a-c) and its abbreviations in each call. All the variables are global and in the float-point type.
Formula | Time on AMDGeode-500 (the operation time), us | Notes |
---|---|---|
a -= b*(a-c) | 4.52 (0.74) | |
a -= b*c | 3.78 (0.72) | |
a -= b | 3.06 (0.56)
|
|
a = b | 2.5 (1.21)
|
Writing to the function IO is longer then reading from the local register for other context call and additional checking for NAN and real modification. |
Empty | 1.29 | the infrastructure and measurement method utilization time. |
24.04.2016:
Reason: Estimate performance of accessing to the low level IO lines on Raspberry Pi GPIO in different ways of the JavaLikeCalc language of OpenSCADA.
Conditions: Raspberry Pi 3, GPIO40, DAQ.GPIO (based on the library bcm2835)
Operation | Result, us |
---|---|
Sleep. Lag on sleep in 1ms measuring, which mostly limited by the realtime reaction about 100us. | |
SYS.sleep(); | +110 |
Special.FLibSYS.fnc_tmSleep(); | +70 |
Sleep. Lag on sleep in 100us measuring, which performs in the measuring cycle. | |
SYS.sleep(); | +17 |
Special.FLibSYS.fnc_tmSleep(); | +2 |
Get a level of the GPIO pin | |
From an attribute res = GPIO.io.pi.gpio17; | 5.4 |
By the static accessing function res = DAQ.GPIO.io.pi.fnc_get(17); | 1.6 |
By the static accessing function with the link preparation function get = "DAQ.GPIO.io.pi.fnc_get"; for(i = 0; i < 10000; i++) res = get(17); | 1.7 |
By the dynamic accessing function res = SYS.DAQ.GPIO.io.pi.fnc_get.call(17); | 80 |
By the dynamic accessing function with the end object preparation tO = SYS.DAQ.GPIO.io.pi.fnc_get; for(i = 0; i < 1000; i++) res = tO.call(17); | 14.3 |
Put a level to the GPIO pin | |
To an attribute GPIO.io.pi.gpio18 = true; | 2.1 |
By the static accessing function DAQ.GPIO.io.pi.fnc_put(18, true); | 1.4 |
By the static accessing function with the link preparation function put = "DAQ.GPIO.io.pi.fnc_put"; for(i = 0; i < 10000; i++) put(17, false); | 1.5 |
By the dynamic accessing function SYS.DAQ.GPIO.io.pi.fnc_put.call(18, true); | 79 |
By the dynamic accessing function with the end object preparation tO = SYS.DAQ.GPIO.io.pi.fnc_put; for(i = 0; i < 1000; i++) tO.call(18, true); | 14.3 |
Modules/JavaLikeCalc/ru - GFDL | November 2024 | OpenSCADA 1+r2996 |