Скрипты и формулы
В приложение Сервер встроен механизм исполнения пользовательских скриптов. Скрипты применяются для расчёта значений и статусов каналов, а также для расчёта значений команд управления.
Скрипты записываются в таблицу Скрипты базы конфигурации. Созданный по шаблону проект уже содержит начальный набор скриптов, который удобно использовать в качестве примеров для разработки собственных скриптов. Переменные и функции, реализованные в таблице Скрипты, затем вызываются в столбцах Входная формула и Выходная формула таблицы Каналы. Чтобы расчёт по формуле для какого-либо канала выполнялся, необходимо установить для него галочку в столбце Формула вкл.
Правила создания скриптов
Общие правила написания и использования скриптов:
- Скрипты записываются согласно синтаксису языка C#. Доступны различные классы фреймворка .NET, например, Math, DateTime, File.
- В таблицу Скрипты добавляются новые константы, поля, свойства и методы, которые становятся доступны в формулах каналов.
- Если хотя бы один скрипт или формула содержит ошибку, работа Сервера невозможна. Информация об ошибках в скриптах выводится в журнал приложения.
Правила вычисления формул каналов:
- Расчёт входной формулы для каналов типа Входной и Входной/выходной выполняется только при получении Сервером новых данных по этим каналам. Используйте эти типы каналов, если данные приходят от устройств.
- Расчёт входной формулы для каналов типа Расчётный и Расчётный/выходной выполняется постоянно на каждом шаге основного цикла Сервера. Последовательность расчёта – от меньших номеров каналов к большим. Расчётные типы каналов используются, если значение и статус канала вычисляются на основе данных других каналов.
- Расчёт выходной формулы для каналов типа Входной/выходной, Расчётный/выходной и Выходной выполняется при отправке команды управления на соответствующий канал.
- Статус канала после вычисления входной формулы для каналов типа Входной и Входной/выходной равен статусу переданных Серверу данных, если расчёт статуса не задан в формуле явно.
- Для каналов типа Расчётный и Расчётный/выходной устанавливается статус Определён, если расчёт статуса не задан в формуле явно.
- Если входная формула канала содержит символ ";", то она разбивается на две части: первая часть рассчитывает значение канала, вторая часть – статус канала.
- Если для канала заданы границы, то статус канала пересчитывается с учётом границ после вычисления входной формулы канала.
- Формулы должны возвращать значения определённых типов данных, которые описаны ниже.
Входные формулы каналов возвращают данные следующих типов:
Тип данных | Описание |
---|---|
double | Значение канала |
CnlData | Значение и статус канала |
long | 64-битное целое значение канала, для которого в таблице Каналы установлен тип данных Integer |
string | Строковое значение канала, для которого в таблице Каналы установлен тип данных ASCII string или Unicode string |
Если входная формула канала возвращает другой тип данных, не указанный таблице, возвращаемое формулой значение приводится к соответствующему типу в зависимости от типа данных канала. Часть входной формулы канала, которая рассчитывает статус канала, должна возвращать целое число типа int
.
Выходные формулы каналов возвращают данные следующих типов:
Тип данных | Описание |
---|---|
double | Значение команды. Чтобы отменить команду, формула должна вернуть double.NaN |
CnlData | Значение команды. Чтобы отменить команду, формула должна вернуть CnlData.Empty |
byte[] | Бинарные данные команды. Чтобы отменить команду, формула должна вернуть null |
string | Строковые данные команды. Преобразуются Сервером в массив байт |
Доступные переменные
Механизм скриптов предоставляет следующие встроенные переменные:
Переменная | Тип данных | Описание |
---|---|---|
Timestamp | DateTime | Метка времени обрабатываемых данных (UTC) |
IsCurrent | bool | Обрабатываемые данные являются текущими данными |
CnlNum | int | Номер канала, формула которого вычисляется |
Channel | Cnl | Свойства канала, формула которого вычисляется |
ArrIdx | int | Индекс обрабатываемого элемента массива |
Cnl, CnlVal | double | Переданное Серверу значение канала до расчёта |
CnlStat | int | Переданный Серверу статус канала до расчёта |
CnlData | CnlData | Переданные Серверу данные канала до расчёта |
Cmd, CmdVal | double | Переданное Серверу значение команды управления |
CmdData | byte[] | Переданные Серверу данные команды управления |
CmdDataStr | string | Данные команды управления в строковом виде |
Доступные функции
Механизм скриптов предоставляет следующие встроенные функции:
Функция | Тип данных | Описание |
---|---|---|
N(n) | int | Возвращает номер канала n. Используется при клонировании каналов |
Val() | double | Текущее значение канала вычисляемой формулы |
Val(n) | double | Текущее значение канала n |
SetVal(n, val) | double | Устанавливает текущее значение канала n |
Stat() | int | Текущий статус канала вычисляемой формулы |
Stat(n) | int | Текущий статус канала n |
SetStat(n, stat) | int | Устанавливает текущий статус канала n |
Data() | CnlData | Текущие данные канала вычисляемой формулы |
Data(n) | CnlData | Текущие данные канала n |
SetData(n, val, stat) | double | Устанавливает текущее значение и статус канала n |
SetData(n, cnlData) | double | Устанавливает текущие данные канала n |
NewData(val, stat) | CnlData | Создает новый экземпляр данных канала |
PrevVal() | double | Предыдущее значение канала вычисляемой формулы |
PrevVal(n) | double | Предыдущее значение канала n |
PrevStat() | int | Предыдущий статус канала вычисляемой формулы |
PrevStat(n) | int | Предыдущий статус канала n |
PrevData() | CnlData | Предыдущие данные канала вычисляемой формулы |
PrevData(n) | CnlData | Предыдущие данные канала n |
Time() | DateTime | Метка времени канала вычисляемой формулы |
Time(n) | DateTime | Метка времени канала n |
PrevTime() | DateTime | Предыдущая метка времени канала вычисляемой формулы |
PrevTime(n) | DateTime | Предыдущая метка времени канала n |
Deriv(n) | double | Производная значения канала n по времени |
Функции из шаблона проекта
В проекте, который был создан на основе стандартного шаблона, таблица Скрипты содержит следующие функции:
Функция | Тип данных | Описание |
---|---|---|
GetBit(val, n) | double | Бит n значения val |
GetBit(cnlData, val) | CnlData | Данные канала, состоящие из бита n значения и статуса канала |
SetBit(val, n, isOn) | double | Устанавливает бит n значения val |
SetBit(cnlData, n, isOn) | CnlData | Устанавливает бит n значения канала, сохраняя статус канала неизменным |
GetByte(val, n) | double | Байт n значения val |
DataRel(offset) | CnlData | Данные канала относительно текущего канала |
SetData() | double | Устанавливает текущие данные канала в соответствии со значением команды |
Now() | double | Текущая дата и время в виде числа с плавающей точкой. Часовой пояс сервера |
UtcNow() | double | Текущая дата и время в виде числа с плавающей точкой. Универсальный часовой пояс (UTC) |
UnixTime() | long | Текущее время Unix в секундах |
EncodeDate( |
double | Преобразует указанные дату и время в значение канала типа double |
DecodeDate(val) | DateTime | Преобразует значение канала в дату и время |
EncodeAscii(s) | double | Преобразует строку в кодировке ASCII, длиной до 8 символов, в число с плавающей точкой |
EncodeUnicode(s) | double | Преобразует строку в кодировке Unicode, длиной до 4 символов, в число с плавающей точкой |
DecodeAscii(val) | string | Преобразует заданное значение в строку ASCII длиной до 8 символов |
DecodeUnicode( |
string | Преобразует заданное значение в строку Unicode длиной до 4 символов |
Substring(s, startIndex, length) | string | Извлекает подстроку из строки с проверкой границ |
SplitAscii( |
string | Разделяет строку ASCII для хранения несколькими каналами |
SplitUnicode( |
string | Разделяет строку Unicode для хранения несколькими каналами |
EverySec( |
CnlData | Выполняет указанную функцию каждую секунду |
EveryMin( |
CnlData | Выполняет указанную функцию каждую минуту |
EveryHour( |
CnlData | Выполняет указанную функцию каждый час |
CountPulse( |
double | Считает импульс указанного канала |
HourStarted() | bool | Указывает, что начался новый час. Результат равен true один раз для каждого канала |
DayStarted() | bool | Указывает, что начался новый день. Результат равен true один раз для каждого канала |
MonthStarted() | bool | Указывает, что начался новый месяц. Результат равен true один раз для каждого канала |
Дополнительные скрипты, в том числе для вычисления средних значений, доступны на GitHub.
Отладка скриптов
При разработке собственных скриптов необходимо соблюдать правила синтаксиса и проверять правильность работы скриптов. Если службе Сервера при запуске не удалось скомпилировать скрипты, информация об ошибке выводится в журнал работы Сервера ScadaServer.log
, а компилируемый исходный код доступен в файле CalcEngine.cs
, который расположен в директории журналов Сервера. Для разработки сложных формул рекомендуется использовать Microsoft Visual Studio или Visual Studio Code.
Примеры формул
Пример 1. Линейное преобразование значения канала, получаемого от устройства. Формула используется для канала типа Входной.
10 * Cnl + 1
Пример 2. Сумма значений каналов 101 и 102. Статус извлекается из канала 101. Формула используется для канала типа Расчётный.
Val(101) + Val(102); Stat(101)
Пример 3. Формула, используемая для канала расчётного типа, извлекает 0-й бит из данных канала 105.
GetBit(Data(105), 0)
Пример 4. Формула увеличивает счётчик на единицу каждую минуту, обнуляя счётчик в начале часа.
EveryMin(() => HourStarted() ? 0 : Val() + 1)