Scripts

Материал из WebHMI Wiki
Перейти к: навигация, поиск
Эта страница — перевод страницы Скрипты. Перевод выполнен на 100%.

Другие языки:
English • ‎русский
Другие языки:
English • ‎русский

About Lua scripts

WebHMI since version 1.10.0.3393 allows you to create custom programs (scripts) in Lua 5.1 [1].

The scripts are managed in the Setup --> Scripts menu item:
Lua-scripts-1.png

Scripts edit.png

Each script should contain a function named main. It will be called at the right time (depending on the settings of the script - in each cycle, when you act on the dashboard or when changing the value of the register). Also, the entire script will be executed once when the daemon is started. This is necessary to compile the Lua script and validate it. All the code that is in the global scope (that is, outside the main function, the main function will not be called) will be executed. This can be useful for initializing some variables and the like.

The main function will receive one parameter - userId - the user ID that executed this script. If the script is executed in each loop or when the register is changed, userId will be zero.

WebHMI imposes the following restrictions on standard functions and Lua libraries:

  • such functions and tables are not available: require, print, loadfile, dofile, os.execute, os.getenv, os.remove, os.rename, os.tmpname, package , debug.debug, debug.getfenv, debug.getregistry, arg
  • libraries are not available: io

In addition to the standard Lua functions, WebHMI also defines such functions:

  • GetReg
  • SetReg
  • WriteReg
  • AddInfoMessage
  • AddWarningMessage
  • AddAlertMessage
  • SendSMS
  • SendEmailMessage
  • SendTelegramMessage
  • SendViberMessage
  • ERROR
  • INFO
  • DEBUG
  • TRACE
  • ApplyRecipe
  • GetCurrentAlerts
  • GetCurrentWeather
  • GetForecastWeather
  • GetSunriseTime
  • GetSunsetTime
  • GetTodayDayType
  • GetTomorrowDayType
  • GetHolidaysStats

For the convenience of working with registers from Lua programs, each register can be assigned a symbolic name and run from Lua already with this name. This name is specified in the register settings in the Variable name field:
Lua-scripts-3.png

Lua-scripts-4.png

An example of a simple script that shows the calls of all WebHMI functions:

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

Despite the fact that the built-in editor checks the syntax of the program, it is not able to detect all the errors that can occur during the execution of the script. In the event that any errors occur during its execution, they will be displayed in the Communication Log.

For example, if this script is executed, a bug will occur because variable random is not declared.

function main (userId)
    
  local v1 = GetReg("Drying_Humidity1_Value");
  v1 = v1+random; -- error is here, without random everything is ok
  
  SetReg("Drying_Humidity1_Value", v1);
  WriteReg("Drying_Humidity1_Value", v1); 
  
  AddInfoMessage(v1, userId);
end

There will be such records in 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')

Here we see that an error occurred while trying to perform an arithmetic operation with an undefined variable (nil). The error occurred in script number 1, its name is 'Calculate humidity', the error occurred in line number 4. The variable that caused the error is called 'random'.

Thus, using this log you can find all the errors in the execution of scripts.

GetReg(variable_name[, connection_name])

The GetReg function returns the current value of the register with the variable name variable_name where variable_name is the value of Srcipt alias from the register settings. Instead of variable_name you can specify a number - the register ID. However, the register ID is a less obvious way and it complicates the reading of the program.

The optional parameter connection_name' can be specified if in several different connections there are registers with the same variable_name' . In this case, connection_name indicates from which particular connection it is necessary to read the register named variable_name. Also, instead of the string connection_name, you can specify the connection ID. However, again, constants complicate the reading of the code and it is preferable to use named lines.

If the register is not found or was not read, then the value nil is returned

For the convenience of selecting registers near the script editor, there are three additional buttons.
Help-buttons.png

When you click on any of them, a pop-up window appears with a list of registers.
Regs-select.png

After clicking on the register of interest, the editor will insert the code with the identifier of the selected register (variable_name if exists or ID) and a comment with the register name, its address and the connection name.
Reg-selected.png

SetReg(variable_name[,connection_name] , new_value)

The SetReg function sets the current value of the register with the name of the variable variable_name to new_value for the current scan. Recording to external devices does not occur and when polling this register in subsequent cycles, the old value will be read. Returns 1 if an error occurred and 0 if successful.

The parameters variable_name and connection_name work just like in the GetReg function.

WriteReg(variable_name[, connection_name], new_value)

The WriteReg function sets the current value of the register with the name of the variable variable_name (optionally you can specify the connection connection_name) in new_value for the current scan and writes this value to the external device in beginning of the next cycle. When polling this register in subsequent scans, a new value will be read (if it does not change by the device itself). Returns 1 if an error occurred and 0 if successful.

The parameters variable_name and connection_name work just like in the GetReg function.

SendSMS(phone_number, message)

The SendSMS function sends a request to send an SMS to the number phone_number with the text message . Returns false if an error occurred and true if successful. Success does not mean delivering a message, but only success when creating a query. This function requires an Internet connection, an account in Level2 system and a positive balance in it. The service is paid.

Format phone_number - only digits with the country code, without spaces, brackets, plus sign. For example, 380123456789.

SendEmailMessage(emailAddress, subject, message)

The SendEmailMessage function sends a request to send an e-mail to emailAddress with the subject of the letter subject and the text message. Returns false if an error occurred and true if successful. Success does not mean delivering a message, but only success when creating a query. This function requires an Internet connection, an account in Level2 system and a positive balance in it. The service is paid.

The field message can contain the html-code.

Пример:

function main (userId)
    SendEmailMessage("address@company.com", 
               "Cooler malfunction. Low oil level.", 
                "<p style='color: red;'>Cooler error #12 has occurred.</p><p>Низкий уровень масла.</p>");
end

The function is available since WebHMI 2.7.4710 version.

SendTelegramMessage(chatId, message)

The SendTelegramMessage function sends a message with the text message to the chat with Id = chatId . Returns 1 if an error occurred and 0 if successful. Success does not mean delivering a message, but only success when creating a query. To use this function, you must have an Internet connection, an account in the Level2 system. The service is free.

To get ChatId, go to http://telegram.me/webhmibot on your mobile phone (by pre-installing Telegram) and start the dialog with bot webhmibot using the / start command. In response you will receive a message with a unique chatId.

Example:

Chatid.png


SendViberMessage(chatId, message)

The SendViberMessage function sends a message with the text 'message' to the chat with Id = chatId. Returns 1 if an error occurred and 0 if successful.
Success does not mean delivering a message, but only success when creating a query. To use this function, you must have a working Internet connection, an account in the system Level2 and a positive balance in this system. The service is chargeable. This feature is available starting with firmware version 2.4.4227.

To get ChatId, go to http://viber.com/webhmi on your mobile phone (having previously installed Viber), click 'Have a look' button.
Viber-pa.png

You will be taken to the public WebHMI account. Go to the personal messages of the public account by clicking the button at the top right.
Viber-private-message.png

Write any text in the chat. In response you will receive a message with a unique chatId chat ID.
Viber-chat-id.png

AddInfoMessage(message, userId)

The AddInfoMessage function adds the message message with the Info level to the Messages log. userId is the user ID on whose behalf the message should be added. Returns 1 if an error occurred and 0 if successful.

AddWarningMessage(message, userId)

The AddWarningMessage function adds the message message with the Warning level to the Messages log. userId is the user ID on whose behalf the message should be added. Returns 1 if an error occurred and 0 if successful.

AddAlertMessage(message, userId)

The AddAlertMessage function adds the message message with the Alert level to the Messages log. userId is the user ID on whose behalf the message should be added. Returns 1 if an error occurred and 0 if successful.

ERROR(message)

The ERROR function adds the message message of the ERROR level to the Communication Log (Maintenance -> Communication Log). Returns 1 if an error occurred and 0 if successful.

INFO(message)

The INFO function adds the message message of the INFO level to the Communication Log (Maintenance -> Communication Log). Returns 1 if an error occurred and 0 if successful.

DEBUG(message)

The DEBUG function adds a message message to the DEBUG level in the Communication Log (Maintenance -> Communication Log). Returns 1 if an error occurred and 0 if successful.

TRACE(message)

The TRACE function adds a message message to the TRACE level in the Communication Log (Maintenance -> Communication Log). Returns 1 if an error occurred and 0 if successful.

ApplyRecipe(recipeId, userId)

The ApplyRecipe function applies the recipe with the number recipeId on behalf of the user with id = userId . If this user does not have access rights to this recipe, then the recipe will not be applied. The application of the recipe is to write to the registers that are specified in the recipe, the corresponding values.


GetCurrentAlerts()

The GetCurrentAlerts function returns a list of current alerts. The return value is the table. The key is the alarm number, the value is the table with the alert properties. Alert properties:

СвойствоОписаниеData type
startTimeAlert trigger timeNumber, UnixTime
regIdThe number of the register in which the flag of the alert led to this alert Number
regAliasVariable name for programsString
bitNumber of the bit with the alarm flagNumber
titleAlert nameString
connectionTitleConnection nameString
connectionIdConnection IDNumber
connectionAliasThe name of the connection variable for programsString
acknowledgedByUser name, who confirmed the alert. If the alert is not confirmed - empty string. String
acknowledgedTime Time, when the alert was confiremed. If the alert is not confirmed - 0.Number, UnixTime
canBeAcknowledged The flag indicates the alert can be confirmed.Boolean

Using this structure, it is possible to implement, for example, various notification scenarios about unconfirmed accidents.

An example of this program:

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; -- notify about alerts that are not confirmed for more than 5 minutes
            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", "The alert is not confirmed: " .. alert['title']);
                        if (notificationsSent[regId] == nil) then
                            notificationsSent[regId] = {};
                        end
                        notificationsSent[regId][bit] = startTime;
                    end
                end
            end
        end
    end
end


GetCurrentWeather()

The GetCurrentWeather function returns the current weather conditions at the WebHMI installation location. The data is updated approximately every two hours. The service requires an Internet connection, an account in Level2 and a subscription to the weather forecast. The function is available since version 2.5.2400.

If for some reason the data has not been received, the value nil will be returned.

the function returns a table with the following fields:
time — the time for which the weather data was received
text — a short textual description of the current weather
temperature — air temperature in degrees Celsius
pressure — atmospheric pressure in hPa
humidity — relative air humidity in %
windDirection — the direction of the wind origin, metrological degrees (north— 0 degrees)
windSpeed — wind speed in m/s
cloudness — covering the sky with clouds as a percentage
rain — Precipitation (rain), in mm
snow — Precipitation (snow), in mm

GetForecastWeather(interval)

The GetForecastWeather function returns the weather forecast at the WebHMI installation location to the specified interval. The data is updated approximately every two hours. The service requires an Internet connection, an account in Level2 and a subscription to the weather forecast. The function is available since version 2.5.2400.

If for some reason the data has not been received, the value nil will be returned.

The interval is a three-hour time interval in the future. From zero to 6. Total six intervals. Zero interval is the forecast in about 3 hours. The first interval is the forecast in six hours. Etc. The format of the returned data is identical to the function GetCurrentWeather.

An example of a simple program that turns on and off the warm floor before entering the store, depending on the weather conditions.

function main (userId)
    local current = GetCurrentWeather();
    local nextForecast = GetForecastWeather(0);
    
    if (nextForecast.temperature > 6 and current.temperature > 6) then -- positive temperature
        WriteReg(91, 0); -- turn anti-ice off 
    end

    if (current.snow < 1 and nextForecast.snow < 1) then -- снег не идет
        WriteReg(91, 0); -- turn anti-ice off
    end

    if (current.cloudness < 20 and nextForecast.cloudness < 20) then -- clear
        WriteReg(91, 0); -- turn anti-ice
    end

    if (current.snow > 2 or nextForecast.snow > 2) then -- it's snowing
        WriteReg(91, 1); -- turn anti-ice on
    end
end

GetSunriseTime(interval)

The function GetSunriseTime returns the time in the format Unixtime of the sunrise in the current day.

The service requires an Internet connection, an account in Level2 and a subscription to the weather forecast. The function is available since version 2.5.2400.

If for some reason the data has not been received, the value nil will be returned.


GetSunsetTime(interval)

The function GetSunsetTime returns the time in Unixtime format of sunset in the current day.

The service requires an Internet connection, an account in Level2 and a subscription to the weather forecast. The function is available since version 2.5.2400.

If for some reason the data has not been received, the value nil will be returned.


GetMeterConsumption(variable_name[, connection_name])

The GetMeterConsumption function returns the resource consumption for the specified counter. Parameters variable_name' and connection_name' work in the same way as in the function GetReg - they determine the register with the resource counter.

Returned consumption of resources is the consumption of the meter for each hour from the beginning of the month to the current moment. Data is taken from Level2 times per hour. Why not from the local database? Because a) the data storage period on the SD card can be less than 1 month and b) because the SD card may fail and if it is replaced in the middle of the month, the data will be unreliable.

The service requires an Internet connection, an account in Level2, and an active data storage service. The function is available since version 2.9.

If for some reason the data has not been received, the value nil will be returned.


GetResourceLimit(resource_name)

The GetResourceLimit function returns the limit of the specified resource for the current month. Limits are set in the Level2 system.

Possible resources names are: "ELECTRICITY", "GAS", "HEAT", "WATER", "HOTWATER", "CUSTOM"

Data is taken from Level2. Data on WebHMI is usually updated no later than 1 hour after they are updated in Level2. If there is no connection, the system will return the limits from the cache. The cache stores the limits for the current and the next two months.

The service requires an Internet connection, an account in Level2. The function is available since version 2.9.

If for some reason the data has not been received, the value nil will be returned.


GetTodayDayType()

The GetTodayDayType function returns the type of the current day: work, holiday, holiday. The types of days are set in the Level2 system.

Three types are valid: "Working", "Weekend", "Holiday".

Data is taken from Level2. Data on WebHMI is usually updated no later than 1 hour after they are updated in Level2. If there is no connection, the system will return the limits from the cache. The cache stores the limits for the current and the next two months.

The service requires an Internet connection, an account in Level2. The function is available since version 2.9.

If for some reason the data has not been received, the value nil will be returned.

GetTomorrowDayType()

The GetTomorrowDayType function is completely analogous to GetTodayDayType with the only difference that it returns data about the future.

GetHolidaysStats()

GetHolidaysStats returns a table with the following fields:

  • currentMonthDays – number of days in the current month
  • nextMonthDays – number of days in the next month
  • currentMonthWeekends – the number of dayoffs (according to the settings in the Level2 system) in the current month
  • nextMonthWeekends – the number of dayoffs (according to the settings in the Level2 system) in the next month
  • currentMonthHolidays – number of holidays (according to the settings in the Level2 system) in the current month
  • nextMonthHolidays – number of holidays (according to the settings in the Level2 system) in the next month

Data is taken from Level2. Data on WebHMI is usually updated no later than 1 hour after they are updated in Level2. If there is no connection, the system will return the limits from the cache. The cache stores the limits for the current and the next two months.

The service requires an Internet connection, an account in Level2. The function is available since version 2.9.

If for some reason the data has not been received, the value nil will be returned.

GetConnectionAddress (connection_name)

The GetConnectionAddress function returns the current address of the device with which it is communicating. The function is available since version 3.0.

The connection_name parameter specifies which connection to take the address from. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

If no connection is found, nil is returned.

For connections connected via RS-485, the address is a number. GetConnectionAddress will return values ​​of type number.

For TCP and UDP connections, the address is its IP address or host name. GetConnectionAddress will return values ​​of type string.

SetConnectionAddress(connection_name, new_address)

The SetConnectionAddress function replaces the current address of the device being communicated with a new one. The function is available since version 3.0. SetConnectionAddress is designed to work in projects with hot backup controllers and allows you to read registers from a particular (active, working) PLC.

The connection_name parameter specifies for which connection the address should be replaced. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

If no connection is found, nil is returned. If the address was changed successfully, the function returns true. If you try to replace the address in internal register connection, the function returns false.

For connections connected via RS-485, the address is a number. SetConnectionAddress expects the value of new_address to be of type number.

For TCP and UDP connections, the address is its IP address or host name. SetConnectionAddress expects the values ​​of new_address to be of type string.

EnableConnection (connection_name)

EnableConnection turns on polling registers "on-the-fly" in the specified connection. The function is available since version 3.0. In the project configuration, the checkbox 'Disconnect' for the specified connection does not change.

The connection_name parameter specifies for which connection the polling should be enabled. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

DisableConnection(connection_name)

DisableConnection disables polling registers "on the fly" in the specified connection. The function is available since version 3.0. In the project configuration, the checkbox 'Disconnect' for the specified connection does not change.

The connection_name parameter specifies for which connection the poll should be enabled. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

IsConnectionEnabled(connection_name)

The IsConnectionEnabled function tells whether the polling of registers in the specified connection is enabled. The function is available since version 3.0.

The connection_name parameter specifies for which connection the poll should be enabled. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

GetConnectionErrors(connection_name)

The GetConnectionErrors function reports whether there were errors while reading the registers in the last scan in the specified connection. The function is available since version 3.0.

If there were no errors, then the function returns zero. If there were errors, then the ID of the last register is returned, which was not read.

The connection_name parameter specifies for which connection the poll should be enabled. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

GetConnectionScanTime(connection_name)

The GetConnectionScanTime function returns the time taken to poll the registers in the last scan in the specified connection. The function is available since version 3.0.

The function returns the time in milliseconds.

The connection_name parameter specifies for which connection the poll should be enabled. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.

GetConnectionErrorScans(connection_name)

The GetConnectionErrorScans function returns the number of consecutive scans in which there were errors reading the registers in the specified connection. The function is available since version 3.0.

The function returns the number of scans in which read errors occurred. If in any scan there were no read errors in this connection, the counter is reset to zero.

The connection_name parameter specifies for which connection the poll should be enabled. In connection_name, you can specify either the connection ID or its variable name. However, constants complicate the reading of the code and it is preferable to use named lines.