Wb-rules 1.7 rules engine

Материал из Wiren Board
Версия от 18:21, 6 июня 2019; RomanKulibaba (обсуждение | вклад) (Новая страница: «==== permanent repository ====»)

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

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

Для обмена данными также можно использовать глобальные постоянные хранилища (PersistentStorage).

Внимание: при использовании глобальных постоянных хранилищ может произойти совпадение имён, в этом случае возможно труднообнаруживаемое нарушение поведения.

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

/// ...

ps.myvar = "value"; // это значение доступно для всех пользователей хранилища с именем "my-global-storage"


Прототип глобального объекта

ВНИМАНИЕ: метод считается "грязным", т.к. все переменные и функции, опубликованные таким образом, становятся доступными всем сценариям в системе. Старайтесь избегать этого способа. За неопределённое поведение при использовании этого метода несёт ответственность сам программист.

Глобальные объекты всех сценариев имеют общий объект-прототип, в котором определены стандартные функции 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
        }
    }
});