Скрипты и формулы

В приложение Сервер встроен механизм исполнения пользовательских скриптов. Скрипты применяются для расчёта значений и статусов каналов, а также для расчёта значений команд управления.

Скрипты записываются в таблицу Скрипты базы конфигурации. Созданный по шаблону проект уже содержит начальный набор скриптов, который удобно использовать в качестве примеров для разработки собственных скриптов. Переменные и функции, реализованные в таблице Скрипты, затем вызываются в столбцах Входная формула и Выходная формула таблицы Каналы. Чтобы расчёт по формуле для какого-либо канала выполнялся, необходимо установить для него галочку в столбце Формула вкл.

Правила создания скриптов

Общие правила написания и использования скриптов:

  1. Скрипты записываются согласно синтаксису языка C#. Доступны различные классы фреймворка .NET, например, Math, DateTime, File.
  2. В таблицу Скрипты добавляются новые константы, поля, свойства и методы, которые становятся доступны в формулах каналов.
  3. Если хотя бы один скрипт или формула содержит ошибку, работа Сервера невозможна. Информация об ошибках в скриптах выводится в журнал приложения.

Правила вычисления формул каналов:

  1. Расчёт входной формулы для каналов типа Входной и Входной/выходной выполняется только при получении Сервером новых данных по этим каналам. Используйте эти типы каналов, если данные приходят от устройств.
  2. Расчёт входной формулы для каналов типа Расчётный и Расчётный/выходной выполняется постоянно на каждом шаге основного цикла Сервера. Последовательность расчёта – от меньших номеров каналов к большим. Расчётные типы каналов используются, если значение и статус канала вычисляются на основе данных других каналов.
  3. Расчёт выходной формулы для каналов типа Входной/выходной, Расчётный/выходной и Выходной выполняется при отправке команды управления на соответствующий канал.
  4. Статус канала после вычисления входной формулы для каналов типа Входной и Входной/выходной равен статусу переданных Серверу данных, если расчёт статуса не задан в формуле явно.
  5. Для каналов типа Расчётный и Расчётный/выходной устанавливается статус Определён, если расчёт статуса не задан в формуле явно.
  6. Если входная формула канала содержит символ ";", то она разбивается на две части: первая часть рассчитывает значение канала, вторая часть – статус канала.
  7. Если для канала заданы границы, то статус канала пересчитывается с учётом границ после вычисления входной формулы канала.
  8. Формулы должны возвращать значения определённых типов данных, которые описаны ниже.

Входные формулы каналов возвращают данные следующих типов:

Тип данных Описание
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(dateTime) double Преобразует указанные дату и время в значение канала типа double
DecodeDate(val) DateTime Преобразует значение канала в дату и время
EncodeAscii(s) double Преобразует строку в кодировке ASCII, длиной до 8 символов, в число с плавающей точкой
EncodeUnicode(s) double Преобразует строку в кодировке Unicode, длиной до 4 символов, в число с плавающей точкой
DecodeAscii(val) string Преобразует заданное значение в строку ASCII длиной до 8 символов
DecodeUnicode(val) string Преобразует заданное значение в строку Unicode длиной до 4 символов
Substring(s, startIndex, length) string Извлекает подстроку из строки с проверкой границ
SplitAscii(getStringFunc) string Разделяет строку ASCII для хранения несколькими каналами
SplitUnicode(getStringFunc) string Разделяет строку Unicode для хранения несколькими каналами
EverySec(getDataFunc) CnlData Выполняет указанную функцию каждую секунду
EveryMin(getDataFunc) CnlData Выполняет указанную функцию каждую минуту
EveryHour(getDataFunc) CnlData Выполняет указанную функцию каждый час
CountPulse(cnlNum) 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)