Модули/FLibSYS - OpenSCADAWiki

# Модули/FLibSYS

This page is a translated version of the page Modules/FLibSYS and the translation is 100% complete.

English • ‎mRussian • ‎Українська

Модуль | Имя | Версия | Лицензия | Источник | Языки | Платформы | Тип | Автор | Описание
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
FLibSYS | Системные функции | 1.8 | GPL2 | spec_FLibSYS.so | en,uk,ru,de | x86,x86_64,ARM | Специальные | Роман Савоченко | Предоставляет библиотеку системных функций среды пользовательского программирования.

Модуль предоставляет в OpenSCADA статичную библиотеку функций для использования в среде пользовательского программирования и организации неординарных алгоритмов взаимодействия.

Для адресации к функциям этой библиотеки можно использовать статический адрес вызова Special.FLibSYS.{Func}() или динамический SYS.Special.FLibSYS["{Func}"].call(), SYS.Special.FLibSYS.{Func}(). Где {Func} — идентификатор функции в библиотеке.

## 1 Общесистемные функции

### 1.1 SYS: Вызов (sysCall)

Описание: Осуществляет вызовы консольных команд ОС. Функция открывает широкие возможности пользователю OpenSCADA, путём вызова любых системных программ, утилит и скриптов, а также получения посредством них доступа к огромному объёму системных данных. Например, команда "ls -l" вернёт детализированное содержимое рабочей директории.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
com | Команда | Строка | Вход |

Пример:

    using Special.FLibSYS;
    test=sysCall("ls -l");
    messPut("Example",0,"Example: "+test);

### 1.2 SYS: Ввод/Вывод (IO)

Описание: Ввод/Вывод в: файл, строковый поток.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Объект(IO) | Возврат |
name | Имя файла или данные (для строкового потока) | Строка | Вход |
accs | Доступ к файлу (""-строковый поток;"r[+]"-чтение;"w[+]"-запись с нуля;"a[+]"-добавление;...) | Строка | Вход |
mFormat | Формат данных машины ('n'-сист. порядок;'b'-BigEndian;'l'-LittleEndian) | Целое | Вход | "n"
enc | Кодировка строки в файле/потоке | Логический | Вход |

#### 1.2.1 IO объект

Значения:

- valType — типы значений:
  - "uchar", "unsigned char", "char", "schar", "signed char";
  - "uint8";
  - "uint16", "ushort";
  - "uint32", "uint", "ulong";
  - "uint64";
  - "char", "int8", "schar", "signed char", "integer*1";
  - "int16", "integer*2";
  - "int32", "int", "long", "integer*4", "integer";
  - "int64", "integer*8";
  - "float", "float32", "single", "real*4";
  - "double", "float64", "real*8", "real";
- accs — режим доступа и тип:
  - — строковый поток;
  - 'r[+]' — чтение;
  - 'w[+]' — запись с нуля;
  - 'a[+]' — добавление.
- mFormat — формат данных машины:
  - 'n' — родной;
  - 'l' — LittleEndian;
  - 'b' — BigEndian.

Свойства:

- length — длина потока;
- pos (w) — позиция чтения/записи потока.
- string (w) — полное значение строкового потока;
- mFormat (w) — формат данных машины;
- stringEncode (w) — кодировка подстрок файла/потока.

Функции:

- bool open(string name = "", string accs = "", string mFormat = "n", string enc = ""); — открыть новый поток для строки или файла name.
- bool close(); — закрыть поток.
- {string|int|real|Array[int|real]} read(string valType = "char", int cnt = -1, string mFormatEnc = "n|NoEnc"); — читать значение типа valType в количестве cnt для формата машины или входной кодировки строки mFormatEnc.
- {int|IO} {write|wr}({string|int|real|Array} vals, string valType = "char", string mFormatEnc = "n|NoEnc", int cnt = 1); — записать значение(ия) vals для типа valType для формата машины или входной кодировки строки mFormatEnc, и повтора скалярных значений в количестве cnt.

### 1.3 DB: SQL запрос (dbReqSQL)

Описание: Формирование SQL-запроса req к БД addr, внутри (trans=true), вне (trans=false) или безразлично (trans=EVAL) к транзакции. В случае ошибки её значение помещается в свойство "err" результата.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Объект(Массив) | Возврат |
addr | Адрес БД, "{TypeDB}.{DB}" | Строка | Вход |
req | SQL-запрос | Строка | Вход |
trans | Транзакция | Логический | Вход | EVAL

### 1.4 XML: Узел (xmlNode)

Описание: Создание объекта узла XML.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Объект(XMLNodeObj) | Возврат |
name | Имя | Строка | Вход |

Пример:

    using Special.FLibSYS;
    //Создание объекта "get" узла XML.
    req = xmlNode("get");
    //Создание объекта "get" узла XML с созданием атрибутов.
    //sub_DAQ/mod_ModBus/cntr_1/prm_1 — путь согласно структуре проекта
    req = xmlNode("get").setAttr("path","/sub_DAQ/mod_ModBus/cntr_1/prm_1/%2fprm%2fst%2fen");

### 1.5 XML: Запрос интерфейса управления (xmlCntrReq)

Описание: Запрос на предмет контроля программы, по XML. Обычный запрос записывается в виде "<get path="/OPath/%2felem"/>". При определении станции осуществляется запрос к указанной внешней станции.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
req | Запрос | Объект(XMLNodeObj) | Выход |
stat | Станция | Строка | Вход |

Пример (больше примеров смотрите для аналогичной внутренней функции SYS.cntrReq()):

    using Special.FLibSYS;
    //Получение признака "Включен/Выключен" параметра "1" контроллера "1" модуля "ModBus".
    //sub_DAQ/mod_ModBus/cntr_1/prm_1 — путь согласно структуре проекта
    req = xmlNode("get").setAttr("path","/sub_DAQ/mod_ModBus/cntr_1/prm_1/%2fprm%2fst%2fen");
    rez = xmlCntrReq(req);
    messPut("test",0,"Значение: "+req.text());

    //Установка признака "Включен" параметра "1" контроллера "1" модуля "ModBus".
    req = xmlNode("set").setAttr("path","/sub_DAQ/mod_ModBus/cntr_1/prm_1/%2fprm%2fst%2fen").setText(1);
    rez = xmlCntrReq(req);

    //Установка признака "Выключен" параметра "1" контроллера "1" модуля "ModBus".
    req = xmlNode("set").setAttr("path","/sub_DAQ/mod_ModBus/cntr_1/prm_1/%2fprm%2fst%2fen").setText(0);
    rez = xmlCntrReq(req);

## 2 Функции работы со временем

### 2.1 Time: Дата (tmTime)

Описание: Возвращает абсолютное время в секундах от эпохи 1.1.1970 и микросекунды в usec, если указана такая переменная и её значение негативно.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
sec | Секунды | Целый | Возврат | 0
usec | Микросекунды | Целый | Выход | -1

### 2.2 Time: Дата и время (tmDate)

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

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
fullsec | Полные секунды | Целый | Вход | 0
sec | Секунды [0...59] | Целый | Выход | 0
min | Минуты [0...59] | Целый | Выход | 0
hour | Часы [0...23] | Целый | Выход | 0
mday | День месяца [1...31] | Целый | Выход | 0
month | Месяц [0...11] | Целый | Выход | 0
year | Год, от 1900 | Целый | Выход | 0
wday | День недели [0...6] | Целый | Выход | 0
yday | День в году [0...365] | Целый | Выход | 0
isdst | Летнее время | Целый | Выход | 0

Пример:

    using Special.FLibSYS;
    curMin=curHour=curDay=curMonth=curYear=0;
    tmDate(tmTime(),0,curMin,curHour,curDay,curMonth,curYear);
    messPut("test",0,"Текущая минута: "+curMin);
    messPut("test",0,"Текущий час : "+curHour);
    messPut("test",0,"Текущий день: "+curDay);
    messPut("test",0,"Текущий месяц: "+curMonth);
    messPut("test",0,"Текущий год: "+curYear);

### 2.3 Time: Строка времени (tmFStr)

Описание: Получение времени как форматируемая строка. Запись формата соответствует POSIX-функции "strftime()".
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
val | Строка даты и времени | Строка | Возврат |
sec | Секунды | Целый | Вход | 0
form | Формат даты и времени | Строка | Вход | %Y-%m-%d %H:%M:%S

Пример:

    using Special.FLibSYS;
    test = tmFStr(SYS.time(),"%d %m %Y");
    messPut("Пример",0,"tmFStr(): "+test);

### 2.4 Time: Строку во время (tmStrPTime)

Описание: Преобразование строковой даты и времени, для формата form, во время в секундах от 01.01.1970. Например, шаблону "%Y-%m-%d %H:%M:%S" соответствует время "2006-08-08 11:21:55". Описание формата шаблона можно получить из документации на POSIX-функцию "strptime()".
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
sec | Секунды | Целый | Возврат | 0
str | Строка даты и времени | Строка | Вход |
form | Формат даты и времени | Строка | Вход | %Y-%m-%d %H:%M:%S

Пример:

    using Special.FLibSYS;
    curMin=curHour=curDay=curMonth=curYear=0;
    tmDate(tmTime(),0,curMin,curHour,curDay,curMonth,curYear);
    test = tmStrPTime(""+curYear+"-"+(curMonth+1)+"-"+curDay+" 9:0:0","%Y-%m-%d %H:%M:%S");
    messPut("Пример",0,"tmStrPTime(): "+test);</pre>

    === {{Anch|tmCron|Time: Планировать время по CRON (tmCron)}} ===
    ''Описание:'' Планирование времени по стандарту CRON, возвращая спланированное время начиная от базового времени ''base'' или от текущего если базовое не указано.<br/>
    ''Параметры:''
    {| class="wikitable"
    |-
    ! Идентификатор !! Параметр !! Тип !! Режим !! По умолчанию
    |-
    | res || Результат, секунды || Целый || Возврат || 0
    |-
    | str || CRON || Строка || Вход || * * * * *
    |-
    | base || Базовое время, секунды || Целый || Вход || 0
    |}

    === {{Anch|tmSleep|Time: сон (tmSleep)}} ===
    ''Описание:'' Короткий сон от наносекунд и до STD_INTERF_TM (5 секунд).<br/>
    ''Параметры:''
    {| class="wikitable"
    |-
    ! Идентификатор !! Параметр !! Тип !! Режим !! По умолчанию
    |-
    | res || Результат || Целый || Возврат || 0
    |-
    | tm || Время, секунды || Вещественный || Вход || 0
    |}

    == Функции работы со строками ==
    === {{Anch|strSize|Строка: Получить размер (strSize)}} ===
    ''Описание:'' Получение размера строки.<br/>
    ''Параметры:''
    {| class="wikitable"
    |-
    ! Идентификатор !! Параметр !! Тип !! Режим !! По умолчанию
    |-
    | rez || Результат || Целый || Возврат ||
    |-
    | str || Строка || Строка || Вход ||
    |}
    ''Пример:''
    <syntaxhighlight lang="JS">
    using Special.FLibSYS;
    messPut("Пример",1,"ReturnString: "+strSize("Пример"));

### 2.5 Строка: Получить подстроку (strSubstr)

Описание: Получение подстроки.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
str | Строка | Строка | Вход |
pos | Позиция | Целый | Вход | 0
n | Количество | Целый | Вход | -1

Пример:

    using Special.FLibSYS;
    test = strSubstr("Пример", 0, strSize("Пример")-1);
    messPut("Пример", 1, "ReturnString: "+test);

### 2.6 Строка: Вставить строку в другую строку (strInsert)

Описание: Вставка строки в другую строку.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
str | Строка | Строка | Выход |
pos | Позиция | Целый | Вход | 0
ins | Вставляемая строка | Строка | Вход |

### 2.7 Строка: Заменить часть строки другой строкой (strReplace)

Описание: Заменяет часть строки другой строкой.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
str | Строка | Строка | Выход |
pos | Позиция | Целый | Вход | 0
n | Количество | Целый | Вход | -1
repl | Строка для замены | Строка | Вход |

### 2.8 Строка: Разобрать по разделителю (strParse)

Описание: Разбор строки по разделителю.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
str | Строка | Строка | Вход |
lev | Уровень | Целый | Вход |
sep | Разделитель | Строка | Вход | "."
off | Смещение | Целый | Выход |

Пример:

    using Special.FLibSYS;
    ExapleString = "Пример:123";
    test = strParse(ExapleString, 1, ":");
    messPut("Пример", 0, "strParse(): "+test);

### 2.9 Строка: Разобрать путь (strParsePath)

Описание: Разбор пути на элементы.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
path | Путь | Строка | Вход |
lev | Уровень | Целый | Вход |
off | Смещение | Целый | Выход |

Пример:

    using Special.FLibSYS;
    test = strParsePath(path, 0, "/");
    messPut("Пример", 1, "strParsePath(): "+test);

### 2.10 Строка: Путь в строку с разделителем (strPath2Sep)

Описание: Преобразование пути в строку с разделителем.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
src | Источник | Строка | Вход |
sep | Разделитель | Строка | Вход | "."

Пример:

    //Преобразуем значение "/ses_AGLKS/pg_so" атрибута "path" в значение "ses_AGLKS.pg_so"
    using Special.FLibSYS;
    test = strPath2Sep(path);
    messPut("Пример", 0, "path: "+path);
    messPut("Пример", 0, "strPath2Sep(): "+test);

### 2.11 Строка: Кодировать строку в HTML (strEnc2HTML)

Описание: Кодирование строки для использования в исходнике HTML.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
src | Источник | Строка | Вход |

### 2.12 Строка: Кодировать текст в бинарный вид (strEnc2Bin)

Описание: Кодирование текста в бинарный вид, для формата текста <00 A0 FA DE>.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
src | Источник | Строка | Вход |

### 2.13 Строка: Декодировать текст из бинарного вида (strDec4Bin)

Описание: Декодирование текста из бинарного вида, для формата текста <00 A0 FA DE>.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
src | Источник | Строка | Вход |

### 2.14 Строка: Вещественное в строку (real2str)

Описание: Преобразование вещественного значения в строковый формат.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
val | Значение | Вещественное | Вход |
prc | Точность | Целое | Вход | 4
tp | Тип | Строка | Вход | "f"

### 2.15 Строка: Целое в строку (int2str)

Описание: Преобразование целого значения в строковый формат.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
val | Значение | Целое | Вход |
base | База (8, 10, 16) | Целое | Вход | 10

### 2.16 Строка: Строка в вещественное (str2real)

Описание: Преобразование значения строки в формат вещественного.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Вещественное | Возврат |
val | Значение | Строка | Вход |

### 2.17 Строка: Строку в целое (str2int)

Описание: Преобразование значения строки в формат целого.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Целое | Возврат |
val | Значение | Строка | Вход |
base | Основа (2...36) | Целый | Вход | 0

### 2.18 Циклический Избыточный Код (CRC)

Описание: Унифицированная и полная реализация Циклического Избыточного Кода для длин 1-64 бит с нормальным полиномом, начальным CRC, входной и выходной последовательностью, и XOR для выхода.
Также поддерживается предыдущая нотация этой функции, где реверсивний полином размещается в позиции ширины width!
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Целый | Возврат |
data | Данные | Строка | Вход |
width | Ширина | Целый | Вход | 16
poly | Полином | Целый | Вход | 32773 (0x8005)
init | Нач.значение | Целый | Вход | -1 (0xFFFFFFFFFFFFFFFF)
RefIn | Последовательность входа | Логический | Вход | 1
RefOut | Последовательность выхода | Логический | Вход | 1
XorOut | XOR для выхода | Целый | Вход | 0

### 2.19 Слепок сообщения 5 (MD5)

Описание: Расчёт слепка сообщения 5 для данных data.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Строка | Возврат |
data | Данные | Строка | Вход |

## 3 Функции работы с вещественным

### 3.1 Float: Разделить на слова (floatSplitWord)

Описание: Разделение значения float (4 байта) на два слова (2 байта).
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
val | Значение | Вещественное | Вход |
w1 | Слово 1 | Целый | Выход |
w2 | Слово 2 | Целый | Выход |

### 3.2 Float: Объединить слова (floatMergeWord)

Описание: Объединение значения float (4 байта) из двух слов (2 байта).
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Вещественное | Возврат |
w1 | Слово 1 | Целый | Вход |
w2 | Слово 2 | Целый | Вход |

### 3.3 Float: Извлечь (floatExtract)

Описание: Извлечение мантиссы и экспоненты из вещественного значения.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
magn | Размер | Вещественный | Возврат |
val | Значение | Вещественный | Вход |
exp | Экспонента | Целый | Выход |

## 4 Функции архивов

### 4.1 Mess: Получить (messGet)

Описание: Получить сообщения программы.
Параметры:

Ідентифікатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Объект(Массив) | Возврат |
btm | Время начала, секунды | Целое | Вход |
etm | Время конца, секунды | Целое | Вход |
cat | Категория | Строка | Вход |
lev | Уровень | Целый | Вход |
arch | Архиватор | Строка | Вход |

### 4.2 Mess: Поместить (messPut)

Описание: Поместить сообщение в программу.
Параметры:

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
cat | Категория | Строка | Вход |
lev | Уровень | Целый | Вход |
mess | Сообщение | Строка | Вход |

Пример:

    rnd_sq_gr11_lineClr="red";
    Special.FLibSYS.messPut("Пример",1,"Event: "+rnd_sq_gr12_leniClr);

### 4.3 Val: Архив (vArh)

Описание: Получение объекта архива значений (VArchObj) через подключения к архиву по его адресу.
Параметры:

Идентификатор | Имя | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Объект(VArchObj) | Возврат |
name | Имя — адрес к атрибуту параметра с архивом (DAQ.{Module}.{Cntr}.{Prm}.{Attr}) или непосредственно к архиву значений (Archive.va_{ValArchive}). | Строка | Вход |

#### 4.3.1 Объект VArchObj

Функции:

- bool isNull() — объект не инициирован.
- int begin( int usec = 0, string archiver = "" ) — время начала архива, через возврата секунд и микросекунд usec для архиватора archiver.
- int end( int usec = 0, string archiver = "" ) — время окончания архива, через возврат секунд и микросекунд usec для архиватора archiver.
- int period( int usec = 0, string archiver = "" ) — период архива, через возврат секунд и микросекунд usec для архиватора archiver.
- ValObj get( int sec, int usec, bool upOrd = false, string archiver = "" ) — значение из архива на время sec:usec с привязкой к верху upOrd и для архиватора archiver. Реальное время полученного значения устанавливается в sec:usec.
- bool set( ValObj val, int sec, int usec, string archiver = "" ) [доступ к подсистеме Архивы-История для операций с архивом] — записать значение val в архив на время sec:usec для архиватора archiver.
- bool copy( VArchObj src, int begSec, int begUSec, int endSec, int endUSec, string archiver = "" ) [доступ к подсистеме Архивы-История для операций с архивом] — копировать часть исходного src архива, или его буфера, в текущий, начиная с begSec:begUSec и заканчивая endSec:endUSec для архиватора archiver.
- ArrayObj FFT( int tm, real size, string archiver = "", int tm_usec = 0 ) — разложить в ряд Фурье с помощью FFT алгоритма. Возвращается массив амплитуд частот для окна значений из архива с временем начала tm:tm_usec (секунды:микросекунды), глубиной в историю архива size (секунд) и для архиватора archiver.

Пример:

    using Special.FLibSYS;
    val = vArh(strPath2Sep(addr)).get(time,uTime,0,archiver);
    return val.isEval() ? "Пусто" : real2str(val,prec);

### 4.4 Val: Буфер архива (vArhBuf)

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

Идентификатор | Параметр | Тип | Режим | По умолчанию
--- | --- | --- | --- | ---
rez | Результат | Объект(VArchObj) | Возврат |
tp | Тип значений архива (0-Логический, 1-Целый, 4-Вещественный, 5-Строка) | Целый | Вход | 1
sz | Максимальный размер буфера | Целый | Вход | 100
per | Период значений буфера, микросекунды | Целый | Вход | 1000000
hgrd | Жесткая сетка времени | Логический | Вход | 0
hres | Высокое разрешения времени (микросекунды) | Логический | Вход | 0

## 5 API пользовательского программирования

Модуль, как объект, предоставляют функции пользовательского программирования.

Объект "Библиотека функций" (SYS.Special.FLibSYS)

- ElTp {funcID}(ElTp prm1, ...) — вызов функции библиотеки {funcID}. Возвращает результат вызываемой функции.

Объект "Пользовательская функция" (SYS.Special.FLibSYS["funcID"])

- ElTp call(ElTp prm1, ...) — вызов данной функции с параметрами <prm{N}>. Возвращает результат вызываемой функции.

Modules/FLibSYS/ru - GFDL | May 2026 | OpenSCADA 0.9.9