Движок правил wb-rules 1.7/en: различия между версиями

Материал из Wiren Board
(Новая страница: «==== A prototype of the global object ====»)
(Новая страница: «'''WARNING:''' method is considered "dirty" because all variables and functions published in this way, become available to all scenarios in the system. Try to avo…»)
Строка 108: Строка 108:
==== A prototype of the global object ====
==== A prototype of the global object ====


'''ВНИМАНИЕ:''' метод считается "грязным", т.к. все переменные и функции, опубликованные таким образом,
'''WARNING:''' method is considered "dirty" because all variables and functions published in this way,
становятся доступными всем сценариям в системе. Старайтесь избегать этого способа. За неопределённое
become available to all scenarios in the system. Try to avoid this method. The programmer is solely responsible for the undefined behavior of the device when using this method
поведение при использовании этого метода несёт ответственность сам программист.


Глобальные объекты всех сценариев имеют общий объект-''прототип'', в котором определены стандартные функции
Глобальные объекты всех сценариев имеют общий объект-''прототип'', в котором определены стандартные функции

Версия 19:06, 6 июня 2019

Другие языки:

The updated wb-rules engine contains a number of important innovations regarding the logic of scripting.

Scripts

Isolation of scripts

Starting with wb-rules v.1.7, local variables and functions declared in the script file are not visible in other scripts. Thus, each script can define its own functions and variables without the risk of changing the behavior of other scripts.


Example

As an example, here are two scenarios that run simultaneously in the rules engine. Each scenario defines variables and functions.

In previous versions of wb-rules, refering to a global variable, that is modified in several script files, may cause undefined behavior. Starting from v.1.7, the behavior is strictly defined and is the same as if the script is the only one in the system.

The comments indicate the output of the log commands for earlier versions and for the current version. Note the 'var' before the variables.

rules1.js

var test1 = 42;

setInterval(function myFuncOne() {
    log("myFuncOne called");
    log("test1: {}", test1);
    log("test2: {}", test2);
}, 5000);

rules2.js

test1 = 84;
test2 = "Hello";

setInterval(function myFuncTwo() {
    log("myFuncTwo called");
    log("test1: {}, test2: {}", test1, test2);
}, 5000);


Before:

2019-01-05 17:29:50myFuncTwo called
2019-01-05 17:29:50test1: 84, test2: Hello
2019-01-05 17:29:50myFuncOne called
2019-01-05 17:29:50test1: 84
2019-01-05 17:29:50test2: Hello


Now:

2019-01-05 17:28:42myFuncTwo called
2019-01-05 17:28:42test1: 84, test2: Hello
2019-01-05 17:28:42myFuncOne called
2019-01-05 17:28:42test1: 42
2019-01-05 17:28:42ECMAScript error: ReferenceError: identifier 'test2' undefined
duk_js_var.c:1232
myFuncOne /etc/wb-rules/rules1.js:6 preventsyield



Note

In previous versions of wb-rules, it was recommended to use loopback to isolate rules, i.e. to isolate rules. wrapping the script code into a construct:

(function() {
    // script code goes here
})();

Starting from version 1.7, such a construction is usually not necessary. However, the old scripts, using this construction, will continue to work without changes in behavior.


workarounds

If your system used a shared global space to store shared data and functions, there are several ways to implement this behavior:


Permanent repository

You can also use global persistent storages to exchange data.

Warning: when using global persistent stores, a name match may occur, in this case, a hard-to-detect behavior violation is possible.

var ps = new PersistentStorage("my-global-storage", {global: true});

/// ...

ps.myvar = "value"; // this value is available for users of "my-global-storage"


A prototype of the global object

WARNING: method is considered "dirty" because all variables and functions published in this way, become available to all scenarios in the system. Try to avoid this method. The programmer is solely responsible for the undefined behavior of the device when using this method

Глобальные объекты всех сценариев имеют общий объект-прототип, в котором определены стандартные функции wb-rules (такие, как defineRule, setTimeout и т.д.). Через него можно передавать переменные или функции в общую область видимости.

global.__proto__.myVar = 42; // теперь myVar - общая переменная для всех сценариев

// из других сценариев к переменной можно обращаться так
log("shared myVar: {}", myVar);

// или вот так, что чуть более аккуратно, т.к. однозначно показывает, где определена переменная
log("shared myVar: {}", global.__proto__.myVar);

Правило поиска переменной в первом случае будет выглядеть так:

  1. Проверяем, есть ли myVar среди локальных переменных (определённой как var myVar = ...).
  2. Если нет, проверяем, есть ли myVar в глобальном объекте (определённой как myVar = ...).
  3. Если нет, проверяем, есть ли myVar в прототипе глобального объекта (определённой как global.__proto__.myVar).

Поиск останавливается, как только переменная найдена.

Таким образом, первый способ обращения будет работать только в том случае, если myVar не определена в верхних областях видимости.


Постоянное хранилище данных

В wb-rules 1.7 добавлена поддержка постоянных хранилищ. По сути, это объекты, значения в которых будут сохраняться даже при потере питания контроллера. Такие хранилища удобно использовать для хранения состояний или конфигурации.

var ps = new PersistentStorage("my-storage", { global: true });

ps.key = "Hello World";
log(ps.key);

Поддерживаются только глобальные хранилища, т.е. видимые по одному и тому же имени из всех файлов сценариев.


Виртуальные устройства

В предыдущих версиях wb-rules значения контролов виртуальных устройств хранились только в MQTT retained, что не очень надёжно (в случае потери питания данные могли быть легко утеряны). Начиная с версии 2.0, эти значения сохраняются также в специальное хранилище в постоянной памяти и восстанавливаются при загрузке сценария.

Если необходимо каждый раз при перезагрузке скрипта восстанавливать строго определённое значение (т.е. не восстанавливать предыдущее сохранённое), можно добавить в описание контрола поле forceDefault:

defineVirtualDevice("vdev", {
    ...
    cells: {
        ...
        mycell: {
            type: "value",
            value: "10",
            forceDefault: true // при каждой загрузке сценария поле mycell будет получать значение 10
        }
    }
});