Скрипты
Содержание
- 1 О скриптах Lua
- 2 GetReg(variable_name[, connection_name])
- 3 SetReg(variable_name[,connection_name] , new_value)
- 4 WriteReg(variable_name, new_value)
- 5 SendSMS(phone_number, message)
- 6 SendTelegramMessage(chatId, message)
- 7 SendViberMessage(chatId, message)
- 8 AddInfoMessage(message, userId)
- 9 AddWarningMessage(message, userId)
- 10 AddAlertMessage(message, userId)
- 11 ERROR(message)
- 12 INFO(message)
- 13 DEBUG(message)
- 14 TRACE(message)
- 15 ApplyRecipe(recipeId, userId)
- 16 GetCurrentAlerts()
- 17 GetCurrentWeather()
- 18 GetForecastWeather(int interval)
О скриптах Lua
WebHMI начиная с версии 1.10.0.3393 позволяет создавать пользовательские скрипты на языке Lua 5.1 [1].
Управление скриптами происходит в пункте меню Setup --> Scripts:
Каждый скрипт должен содержать в себе функцию с именем main. Именно она будет вызвана в необходимый момент (в зависимости от настроек скрипта - в каждом цикле, при действии на dashboard или при изменении значения регистра). Также, весь скрипт будет выполнен один раз при запуске демона. Это необходимо для компиляции сценария Lua и его валидации. Выполнен будет весь код, который находится в глобальной области видимости (т.е. вне функции main, функция main не будет вызвана). Это может быть полезно для инициализации некоторых переменный и т.п.
В функцию main будет передан один параметр — userId — ID пользователя, который выполнил этот скрипт. Если скрипт выполняется в каждом цикле или при изменении регистра то userId будет равен нулю.
WebHMI накладывает следующие ограничения на стандартные функции и библиотеки языка Lua:
- не доступны такие функции и таблицы: require, print, loadfile, dofile, os.execute, os.getenv, os.remove, os.rename, os.tmpname, package, debug.debug, debug.getfenv, debug.getregistry, arg
- не доступны библиотеки: io
Помимо стандартных функций Lua, в WebHMI определены еще такие функции:
- GetReg
- SetReg
- WriteReg
- AddInfoMessage
- AddWarningMessage
- AddAlertMessage
- SendSMS
- SendTelegramMessage
- SendViberMessage
- ERROR
- INFO
- DEBUG
- TRACE
- ApplyRecipe
- GetCurrentAlerts
- GetCurrentWeather
- GetForecastWeather
Для удобства работы с регистрами из программ Lua, каждому регистру можно присвоить символическое имя и работать из Lua уже с этим именем. Это имя указывается в настройках регистра в поле Variable name:
Пример простейшего скрипта, который показывает вызовы всех WebHMI функций:
function main (userId)
local randomVal = GetReg("random_val");
SetReg("random_val_copy", randomVal);
WriteReg("result", randomVal + 2);
AddInfoMessage("This is a Lua script", userId);
AddWarningMessage("Warning. This is a Lua script", userId);
AddAlertMessage("Alert! This is a Lua script", userId);
SendSMS("380501234567", "This is a test SMS");
ERROR("This message will be added to communication log with ERROR level");
INFO("This message will be added to communication log with INFO level");
DEBUG("This message will be added to communication log with DEBUG level");
TRACE("This message will be added to communication log with TRACE level");
end
Несмотря на то, что встроенный редактор проверяет синтаксис программы, он не в состоянии выявить все ошибки, которые могу возникнуть в процессе выполнения скрипта. В случаях, если во время его выполнения возникнут какие-либо ошибки, они будут выведены в Communication Log.
Например, при выполнении этого скрипта произойдет ошибка т.к. переменная random не объявлена.
function main (userId)
local v1 = GetReg("Drying_Humidity1_Value");
v1 = v1+random; -- ошибка здесь, если убрать random, всё становится ок
SetReg("Drying_Humidity1_Value", v1);
WriteReg("Drying_Humidity1_Value", v1);
AddInfoMessage(v1, userId);
end
В Communication Log можно будет увидеть такие записи:
Jan 23 12:09:27.047: ERROR: LUA scripts: Can't execute LUA script #1. Error #2: [string "Calculate humidity"]:4: attempt to perform arithmetic on a nil value (global 'random') Jan 23 12:09:27.551: ERROR: LUA scripts: Can't execute LUA script #1. Error #2: [string "Calculate humidity"]:4: attempt to perform arithmetic on a nil value (global 'random') Jan 23 12:09:28.051: ERROR: LUA scripts: Can't execute LUA script #1. Error #2: [string "Calculate humidity"]:4: attempt to perform arithmetic on a nil value (global 'random')
Здесь видно, что произошла ошибка при попытке выполнения арифметической операции с неопределенной переменной (nil). Ошибка произошла в скрипте номер 1, его название "Calculate humidity", ошибка произошла в строке номер 4. Переменная, которая вызвала ошибку называется 'random'.
Таким образом, с помощью этого лога можно найти все ошибки выполнения сценариев.
GetReg(variable_name[, connection_name])
Функция GetReg возвращает текущее значение регистра с именем переменной variable_name где variable_name — это значение Srcipt alias из настроек регистра. Вместо variable_name можно указывать число — ID регистра. Однако, ID регистра — это менее наглядный способ и он усложняет чтение программы.
Опциональный параметр connection_name можно указать если в нескольких разных соединениях есть регистры с одинаковым variable_name. В этом случае connection_name указывает из какого конкретно соединения необходимо читать регистр с именем variable_name. Также, вместо строки connection_name можно указывать ID соединения. Однако, опять же, константы усложняют чтение кода и предпочтительней использовать именованные строки.
Если регистр не найден или не был прочитан то возвращается значение nil.
Для удобства выбора регистров возле редактора скриптов есть три дополнительные кнопки.
При нажатии на любую из них появляется всплывающее окно со списком регистров.
После щелчка по интересуещему регистру в редактор будет вставлен код с идентификатором выбранного регистра (variable_name если есть или ID) и комментарием с именем регистра, его адресом и именем соединения.
SetReg(variable_name[,connection_name] , new_value)
Функция SetReg устанавливает текущее значение регистра с именем переменной variable_name в new_value для текущего цикла. Запись во внешние устройства не происходит и при опросе этого регистра в последующих циклах будет прочитано старое значение. Возвращает 1 если произошла ошибка и 0 в случае успеха.
Параметры variable_name и connection_name работают так же как и в функции GetReg.
WriteReg(variable_name, new_value)
Функция WriteReg устанавливает текущее значение регистра с именем переменной variable_name в new_value для текущего цикла цикле и записывает это значение во внешнее устройство в начале следующего цикла. При опросе этого регистра в последующих циклах будет прочитано новое значение (если оно не изменится самим устройством). Возвращает 1 если произошла ошибка и 0 в случае успеха.
Параметры variable_name и connection_name работают так же как и в функции GetReg.
SendSMS(phone_number, message)
Функция SendSMS отправляет запрос на отправку SMS на номер phone_number с текстом message. Возвращает false если произошла ошибка и true в случае успеха. Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие интернет-подключения и предоплаты за услуги SMS.
Формат phone_number — только цифры с указанием кода страны, без пробелов, скобок, знака плюс. Например, 380123456789.
SendTelegramMessage(chatId, message)
Функция SendTelegramMessage отправляет сообщение с текстом message в чат с Id = chatId . Возвращает 1 если произошла ошибка и 0 в случае успеха.
Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие работающего интернет-подключения и доступа к серверу webhmicloud.com.
Для получения ChatId перейдите по ссылке http://telegram.me/webhmibot на мобильном телефоне (предварительно установив Telegram) и начните диалог с ботом webhmibot командой /start. В ответ вы получите сообщение с уникальным идентификатором чата ChatId.
Пример:
SendViberMessage(chatId, message)
Функция SendViberMessage отправляет сообщение с текстом message в чат с Id = chatId. Возвращает 1 если произошла ошибка и 0 в случае успеха.
Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие работающего интернет-подключения, учетной записи в системе Level2 и положительного баланса в этой системе. Отправка сообщений в Viber — платная услуга. Эта возможность доступна начиная с версии прошивки 2.4.4227.
Для получения ChatId перейдите по ссылке http://viber.com/webhmi на мобильном телефоне (предварительно установив Viber), нажмите кнопку Have a look.
Вы попадете в публичный аккаунт WebHMI. Перейдите в личные сообщения публичного аккаунта нажав кнопку справа вверху.
Напишите любой текст в чате. В ответ вы получите сообщение с уникальным идентификатором чата ChatId.
AddInfoMessage(message, userId)
Функция AddInfoMessage добавляет сообщение message с уровнем Info в журнал Messages. userId — это ID пользователя, от имени которого должно быть добавлено сообщение. Возвращает 1 если произошла ошибка и 0 в случае успеха.
AddWarningMessage(message, userId)
Функция AddWarningMessage добавляет сообщение message с уровнем Warning в журнал Messages. userId — это ID пользователя, от имени которого должно быть добавлено сообщение. Возвращает 1 если произошла ошибка и 0 в случае успеха.
AddAlertMessage(message, userId)
Функция AddAlertMessage добавляет сообщение message с уровнем Alert в журнал Messages. userId — это ID пользователя, от имени которого должно быть добавлено сообщение. Возвращает 1 если произошла ошибка и 0 в случае успеха.
ERROR(message)
Функция ERROR добавляет сообщение message уровня ERROR в Communication Log (Maintenance --> Communication Log). Возвращает 1 если произошла ошибка и 0 в случае успеха.
INFO(message)
Функция INFO добавляет сообщение message уровня INFO в Communication Log (Maintenance --> Communication Log). Возвращает 1 если произошла ошибка и 0 в случае успеха.
DEBUG(message)
Функция DEBUG добавляет сообщение message уровня DEBUG в Communication Log (Maintenance --> Communication Log). Возвращает 1 если произошла ошибка и 0 в случае успеха.
TRACE(message)
Функция TRACE добавляет сообщение message уровня TRACE в Communication Log (Maintenance --> Communication Log). Возвращает 1 если произошла ошибка и 0 в случае успеха.
ApplyRecipe(recipeId, userId)
Функция ApplyRecipe применяет рецепт с номером recipeId от имени пользователя с id = userId. Если у данного пользователя нет прав доступа к этому рецепту то рецепт применен не будет. Применение рецепта заключается в записи в регистры, которые указаны в рецепте, соответствующих значений.
GetCurrentAlerts()
Функция GetCurrentAlerts возвращает список текущих аварий. Возвращаемое значение — таблица. Ключем является номер аварии, значением — таблица со свойствами аварии. Свойства аварии:
Свойство | Описание | Тип данных |
startTime | Время возникновения аварии | Number, UnixTime |
regId | Номер регистра, в котором флаг аварии привел к этой аварии | Number |
regAlias | Имя переменной регистра для программ | String |
bit | Номер бита с флагом аварии | Number |
title | Название аварии | String |
connectionTitle | Название соединения | String |
connectionId | ID соединения | Number |
connectionAlias | Имя переменной соединения для программ | String |
acknowledgedBy | Имя пользователя, который подтвердил аварию. Если авария не подтверждена — пустая строка. | String |
acknowledgedTime | Время, когда авария был подтверждена. Если авария не подтверждена — 0. | Number, UnixTime |
canBeAcknowledged | Флаг, обозначающий что данную аварию можно подтверждать. | Boolean |
Используя данную структру, можно реализовать, например, различные сценарии оповещения о неподтвержденных авариях.
Пример такой программы:
local notificationsSent = {};
function main (userId)
local alerts = GetCurrentAlerts();
if (#alerts > 0) then
for num,alert in pairs(alerts) do
local now = os.time();
local canBeAcknowledged = alert['canBeAcknowledged'];
local acknowledgedTime = alert['acknowledgedTime'];
local startTime = alert['startTime'];
local waitSecondsBeforeEscalate = 300; -- оповещать об авариях, которые неподтвержены более 5 минут
local regId = alert['regId'];
local bit = alert['bit'];
if (canBeAcknowledged and acknowledgedTime == 0) then
if (now - startTime > waitSecondsBeforeEscalate) then
if (notificationsSent[regId] == nil or notificationsSent[regId][bit] ~= startTime) then
--ERROR("Alert" .. alert['title'] .. " was not acknowledged!");
SendSMS("380501234567", "Не подтведжена авария: " .. alert['title']);
if (notificationsSent[regId] == nil) then
notificationsSent[regId] = {};
end
notificationsSent[regId][bit] = startTime;
end
end
end
end
end
end
GetCurrentWeather()
Функция GetCurrentWeather возвращает текущие метеоусловия в месте установки WebHMI. Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.
Функция возвращает таблицу с такими полями:
time — время, для которого был получены метеоданные
text — краткое текстовое описание текущей погоды
temperature — температура воздуха в градусах Цельсия
pressure — атмосферное давление в hPa
humidity — относительная влажность воздуха в %
windDirection — навравление, откуда дует ветер. метрологические градусы (сервер — 0 градусов)
windSpeed — скокрость ветра в м/c
cloudness — покрытие неба облаками в процентах
rain — количество осадков (дождь), в мм
snow — количество осадков (снег), в мм
GetForecastWeather(int interval)
Функция GetForecastWeather возвращает прогноз погоды в месте установки WebHMI на указанный интервал. Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.
Интервал — это трехчасовой промежуток времени в будущем. От нуля до 6. Всего шесть интервалов. Нулевой интервал — это прогноз примерно через 3 часа. Первый интервал — прогноз через шесть часов. И т.д. Формат возвращаемых данных идентичен функции GetCurrentWeather.
Пример простой программы, которая включает и выключает теплый пол перед входом в магазин в зависимости от погодных условий.
function main (userId)
local current = GetCurrentWeather();
local nextForecast = GetForecastWeather(0);
if (nextForecast.temperature > 6 and current.temperature > 6) then -- положительная температура
WriteReg(91, 0); -- выключить антилед
end
if (current.snow < 1 and nextForecast.snow < 1) then -- снег не идет
WriteReg(91, 0); -- выключить антилед
end
if (current.cloudness < 20 and nextForecast.cloudness < 20) then -- ясно
WriteReg(91, 0); -- выключить антилед
end
if (current.snow > 2 or nextForecast.snow > 2) then -- идет снег
WriteReg(91, 1); -- включить антилед
end
end