Скрипты

Материал из WebHMI Wiki
Перейти к: навигация, поиск
Другие языки:
English • ‎русский

О скриптах Lua

WebHMI начиная с версии 1.10.0.3393 позволяет создавать пользовательские программы (скрипты) на языке Lua 5.1 [1].

Управление скриптами происходит в пункте меню Setup --> Scripts:
Lua-scripts-1.png

Scripts edit.png

Каждый скрипт должен содержать в себе функцию с именем 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
  • SendEmailMessage
  • SendTelegramMessage
  • SendViberMessage
  • ERROR
  • INFO
  • DEBUG
  • TRACE
  • ApplyRecipe
  • GetCurrentAlerts
  • GetCurrentWeather
  • GetForecastWeather
  • GetSunriseTime
  • GetSunsetTime
  • GetTodayDayType
  • GetTomorrowDayType
  • GetHolidaysStats

Для удобства работы с регистрами из программ Lua, каждому регистру можно присвоить символическое имя и работать из Lua уже с этим именем. Это имя указывается в настройках регистра в поле Имя переменной (Variable name):
Lua-scripts-3.png

Lua-scripts-4.png

Пример простейшего скрипта, который показывает вызовы всех 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.

Для удобства выбора регистров возле редактора скриптов есть три дополнительные кнопки.
Help-buttons.png

При нажатии на любую из них появляется всплывающее окно со списком регистров.
Regs-select.png

После щелчка по интересуещему регистру в редактор будет вставлен код с идентификатором выбранного регистра (variable_name если есть или ID) и комментарием с именем регистра, его адресом и именем соединения.
Reg-selected.png

SetReg(variable_name[,connection_name] , new_value)

Функция SetReg устанавливает текущее значение регистра с именем переменной variable_name в new_value для текущего цикла. Запись во внешние устройства не происходит и при опросе этого регистра в последующих циклах будет прочитано старое значение. Возвращает 1 если произошла ошибка и 0 в случае успеха.

Параметры variable_name и connection_name работают так же как и в функции GetReg.

WriteReg(variable_name[, connection_name], new_value)

Функция WriteReg устанавливает текущее значение регистра с именем переменной variable_name (опционально можно указать и соединение connection_name) в new_value для текущего цикла цикле и записывает это значение во внешнее устройство в начале следующего цикла. При опросе этого регистра в последующих циклах будет прочитано новое значение (если оно не изменится самим устройством). Возвращает 1 если произошла ошибка и 0 в случае успеха.

Параметры variable_name и connection_name работают так же как и в функции GetReg.

SendSMS(phone_number, message)

Функция SendSMS отправляет запрос на отправку SMS на номер phone_number с текстом message. Возвращает false если произошла ошибка и true в случае успеха. Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие интернет-подключения, учетная запись в системе Level2 и положительного баланса в ней. Услуга платная.
Формат phone_number — только цифры с указанием кода страны, без пробелов, скобок, знака плюс. Например, 380123456789.

SendEmailMessage(emailAddress, subject, message)

Функция SendEmailMessage отправляет запрос на отправку e-mail по адресу emailAddress с темой письма subject и текстом message. Возвращает false если произошла ошибка и true в случае успеха. Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие интернет-подключения, учетная запись в системе Level2 и положительного баланса в ней. Услуга платная.
Поле message может содержать html-код.

Пример:

function main (userId)
    SendEmailMessage("address@company.com", 
               "Авария охладителя. Низкий уровень масла.", 
                "<p style='color: red;'>Произошла ошибка охладителя #12</p><p>Низкий уровень масла.</p>");
end

Функция доступна начиная с версии WebHMI 2.7.4710

SendTelegramMessage(chatId, message)

Функция SendTelegramMessage отправляет сообщение с текстом message в чат с Id = chatId . Возвращает 1 если произошла ошибка и 0 в случае успеха. Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие интернет-подключения, учетная запись в системе Level2. Услуга бесплатная.

Для получения ChatId перейдите по ссылке http://telegram.me/webhmibot на мобильном телефоне (предварительно установив Telegram) и начните диалог с ботом webhmibot командой /start. В ответ вы получите сообщение с уникальным идентификатором чата ChatId.

Пример:

Chatid.png


SendViberMessage(chatId, message)

Функция SendViberMessage отправляет сообщение с текстом message в чат с Id = chatId. Возвращает 1 если произошла ошибка и 0 в случае успеха.
Успех не означает доставку сообщения, а только успех при создании запроса. Для работы этой функции необходимо наличие работающего интернет-подключения, учетной записи в системе Level2 и положительного баланса в этой системе. Услуга платная. Эта возможность доступна начиная с версии прошивки 2.4.4227.

Для получения ChatId перейдите по ссылке http://viber.com/webhmi на мобильном телефоне (предварительно установив Viber), нажмите кнопку Have a look.
Viber-pa.png

Вы попадете в публичный аккаунт WebHMI. Перейдите в личные сообщения публичного аккаунта нажав кнопку справа вверху.
Viber-private-message.png

Напишите любой текст в чате. В ответ вы получите сообщение с уникальным идентификатором чата ChatId.
Viber-chat-id.png

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
connectionIdID соединения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. Данные обновляются примерно каждые два часа. Для работы сервиса требуется интернет-подключение, учетная запись в Level2 и подписка на метеопрогноз. Функция доступна начиная с версии 2.5.2400.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.

Функция возвращает таблицу с такими полями:
time — время, для которого был получены метеоданные
text — краткое текстовое описание текущей погоды
temperature — температура воздуха в градусах Цельсия
pressure — атмосферное давление в hPa
humidity — относительная влажность воздуха в %
windDirection — навравление, откуда дует ветер. метрологические градусы (сервер — 0 градусов)
windSpeed — скокрость ветра в м/c
cloudness — покрытие неба облаками в процентах
rain — количество осадков (дождь), в мм
snow — количество осадков (снег), в мм

GetForecastWeather(interval)

Функция GetForecastWeather возвращает прогноз погоды в месте установки WebHMI на указанный интервал. Данные обновляются примерно каждые два часа. Для работы сервиса требуется интернет-подключение, учетная запись в Level2 и подписка на метеопрогноз. Функция доступна начиная с версии 2.5.2400.

Если по каким-либо причинам данные не были получены, то будет возвращено значение 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

GetSunriseTime(interval)

Функция GetSunriseTime возвращает время в формате Unixtime восхода Солнца в текущих сутках.

Для работы сервиса требуется интернет-подключение, учетная запись в Level2 и подписка на метеопрогноз. Функция доступна начиная с версии 2.5.2400.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.


GetSunsetTime(interval)

Функция GetSunsetTime возвращает время в формате Unixtime заката Солнца в текущих сутках.

Для работы сервиса требуется интернет-подключение, учетная запись в Level2 и подписка на метеопрогноз. Функция доступна начиная с версии 2.5.2400.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.


GetMeterConsumption(variable_name[, connection_name])

Функция GetMeterConsumption возвращает расход ресурсов для указанного счетчика. Параметры variable_name и connection_name работают так же как и в функции GetReg – они опеределяют регистр со счетчиком ресурсов.

Возвращаемый расход ресурсов – это расход счетчика за каждый час с начала месяца по текущий момент. Данные берутся с Level2 раз в час. Почему не из локальной БД? Потому что а) срок хранения данных на SD-карте может быть меньше 1 месяца и б) потому что возможны сбои SD-карты и если ее заменить посреди месяца, то данные будут недостоверны.

Для работы сервиса требуется интернет-подключение, учетная запись в Level2 и активная услуга хранения данных. Функция доступна начиная с версии 2.9.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.


GetResourceLimit(resource_name)

Функция GetResourceLimit возвращает лимит указанного ресурса на текущий месяц. Лимиты задаются в системе Level2.

Допустимые имена ресурсов: "ELECTRICITY", "GAS", "HEAT", "WATER", "HOTWATER", "CUSTOM"

Данные берутся с Level2. Данные на WebHMI обновляются обычно не позже чем через 1 час после обновления их в Level2. При отсутствии связи, система будет возвращать лимиты из кеша. В кеше хранятся лимиты за текущий и два последующих месяца.

Для работы сервиса требуется интернет-подключение, учетная запись в Level2. Функция доступна начиная с версии 2.9.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.


GetTodayDayType()

Функция GetTodayDayType возвращает тип текущего дня: рабочий, выходной, праздник. Типы дней задаются в системе Level2.

Доступны три типа: "Working", "Weekend", "Holiday".

Данные берутся с Level2. Данные на WebHMI обновляются обычно не позже чем через 1 час после обновления их в Level2. При отсутствии связи, система будет возвращать данные из кеша. В кеше хранятся типы дней за текущий и два последующих месяца.

Для работы сервиса требуется интернет-подключение, учетная запись в Level2. Функция доступна начиная с версии 2.9.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.

GetTomorrowDayType()

Функция GetTomorrowDayType полностью аналогична GetTodayDayType с той лишь разницей, что возвращает данные о завтрашнем дне.

GetHolidaysStats()

Функция GetHolidaysStats возвращает таблицу с такими полями:

  • currentMonthDays – количество дней в текущем месяце
  • nextMonthDays – количество дней в следующем месяце
  • currentMonthWeekends – количество выходных дней (согласно настроек в системе Level2) в текущем месяце
  • nextMonthWeekends – количество выходных дней (согласно настроек в системе Level2) в следующем месяце
  • currentMonthHolidays – количество праздничных дней (согласно настроек в системе Level2) в текущем месяце
  • nextMonthHolidays – количество праздничных дней (согласно настроек в системе Level2) в следующем месяце

Данные берутся с Level2. Данные на WebHMI обновляются обычно не позже чем через 1 час после обновления их в Level2. При отсутствии связи, система будет возвращать данные из кеша. В кеше хранятся типы дней за текущий и два последующих месяца.

Для работы сервиса требуется интернет-подключение, учетная запись в Level2. Функция доступна начиная с версии 2.9.

Если по каким-либо причинам данные не были получены, то будет возвращено значение nil.

GetConnectionAddress(connection_name)

Функция GetConnectionAddress возвращает текущий адрес устройства, с которым идет обмен. Функция доступна начиная с версии 3.0.

Параметр connection_name указывает для какого соединения необходимо взять адрес. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

Если соединение не найдено то возвращается значение nil.

Для соединений, подключенных по RS-485, адресом является число. GetConnectionAddress вернет значения типа number.

Для TCP и UDP соединений адресом является его IP-адрес или имя хоста. GetConnectionAddress вернет значения типа string.

SetConnectionAddress(connection_name, new_address)

Функция SetConnectionAddress заменяет текущий адрес устройства, с которым идет обмен, на новый. Функция доступна начиная с версии 3.0. SetConnectionAddress предназначена для работы в проектах с горячим резервированием контролеров и позволяет читать регистры из конкретного (активного, работающего) ПЛК.

Параметр connection_name указывает для какого соединения необходимо заменить адрес. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

Если соединение не найдено то возвращается значение nil. Если адрес был заменен успешно, функция вернет true. При попытке замены адреса вj dyenhtyyb[ htubcnhf[, функция вернет false.

Для соединений, подключенных по RS-485, адресом является число. SetConnectionAddress ожидает что значение new_address будет типа number.

Для TCP и UDP соединений адресом является его IP-адрес или имя хоста. SetConnectionAddress ожидает что значения new_address будет типа string.

EnableConnection(connection_name)

Функция EnableConnection на лету включает опрос регистров в указанном соединении. Функция доступна начиная с версии 3.0. При этом в конфигурации проекта галочка "Отключить" для указанного соединения не изменяется.

Параметр connection_name указывает для какого соединения необходимо включить опрос. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

DisableConnection(connection_name)

Функция DisableConnection на лету отключает опрос регистров в указанном соединении. Функция доступна начиная с версии 3.0. При этом в конфигурации проекта галочка "Отключить" для указанного соединения не изменяется.

Параметр connection_name указывает для какого соединения необходимо включить опрос. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

IsConnectionEnabled(connection_name)

Функция IsConnectionEnabled сообщает включен ли опрос регистров в указанном соединении. Функция доступна начиная с версии 3.0.

Параметр connection_name указывает для какого соединения необходимо включить опрос. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

GetConnectionErrors(connection_name)

Функция GetConnectionErrors сообщает были ли ошибки при чтении регистров в последнем скане в указанном соединении. Функция доступна начиная с версии 3.0.

Если ошибок не было, то функция возвращает ноль. Если ошибки были, то возвращается ID последнего регистра, который не был прочитан.

Параметр connection_name указывает для какого соединения необходимо включить опрос. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

GetConnectionScanTime(connection_name)

Функция GetConnectionScanTime возвращает время, затраченное на опрос регистров в последнем скане в указанном соединении. Функция доступна начиная с версии 3.0.

Функция возвращает время в миллисекундах.

Параметр connection_name указывает для какого соединения необходимо включить опрос. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.

GetConnectionErrorScans(connection_name)

Функция GetConnectionErrorScans возвращает количество последовательных сканов, в которых были ошибки чтения регистров в указанном соединении. Функция доступна начиная с версии 3.0.

Функция возвращает количество сканов, в которых были ошибки чтения. Если в каком-либо скане не было ошибок чтения в данном соединении, то счетчик сбрасывается в ноль.

Параметр connection_name указывает для какого соединения необходимо включить опрос. В connection_name можно указывать или ID соединения или его имя переменной. Однако, константы усложняют чтение кода и предпочтительней использовать именованные строки.