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

Материал из Wiren Board
(Подготовка страницы к переводу)
(Отметить эту версию для перевода)
Строка 1: Строка 1:
<languages/>
<languages/>
<translate>
<translate>
<!--T:1-->
В обновлённом движке правил wb-rules присутствует ряд важных нововведений, касающихся логики написания сценариев.
В обновлённом движке правил wb-rules присутствует ряд важных нововведений, касающихся логики написания сценариев.


= Сценарии =
= Сценарии = <!--T:2-->




== Изоляция сценариев ==
== Изоляция сценариев == <!--T:3-->


<!--T:4-->
Начиная с версии wb-rules 1.7, локальные переменные и функции, обьявленные в файле сценария не видны в других сценариях.
Начиная с версии wb-rules 1.7, локальные переменные и функции, обьявленные в файле сценария не видны в других сценариях.
Таким образом, каждый сценарий может определять свои функции и переменные без риска изменить поведение других сценариев.
Таким образом, каждый сценарий может определять свои функции и переменные без риска изменить поведение других сценариев.




=== Пример ===
=== Пример === <!--T:5-->


<!--T:6-->
В качестве примера приведём два сценария, одновременно запускаемых в движке правил.
В качестве примера приведём два сценария, одновременно запускаемых в движке правил.
Каждый сценарий определяет переменные и функции.
Каждый сценарий определяет переменные и функции.


<!--T:7-->
В предыдущих версиях wb-rules обращение к переменной, изменяемой в нескольких
В предыдущих версиях wb-rules обращение к переменной, изменяемой в нескольких
файлах сценариев, может привести к неопределённому поведению. В версиях, начиная с 1.7,
файлах сценариев, может привести к неопределённому поведению. В версиях, начиная с 1.7,
поведение строго определено и такое же, как будто сценарий единственный в системе.
поведение строго определено и такое же, как будто сценарий единственный в системе.


<!--T:8-->
В комментариях указан вывод команд log для ранних версий и для актуальной версии. Обратите внимание на var перед переменными.
В комментариях указан вывод команд log для ранних версий и для актуальной версии. Обратите внимание на var перед переменными.


<!--T:9-->
'''Сценарий 1 (rules1.js)'''
'''Сценарий 1 (rules1.js)'''
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
var test1 = 42;
var test1 = 42;


<!--T:10-->
setInterval(function myFuncOne() {
setInterval(function myFuncOne() {
     log("myFuncOne called");
     log("myFuncOne called");
Строка 34: Строка 41:
</syntaxhighlight>
</syntaxhighlight>


<!--T:11-->
'''Сценарий 2 (rules2.js)'''
'''Сценарий 2 (rules2.js)'''
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
Строка 39: Строка 47:
var test2 = "Hello";
var test2 = "Hello";


<!--T:12-->
setInterval(function myFuncTwo() {
setInterval(function myFuncTwo() {
     log("myFuncTwo called");
     log("myFuncTwo called");
Строка 46: Строка 55:




<!--T:13-->
Было:
Было:
<pre>
<pre>
Строка 54: Строка 64:
2019-01-05 17:29:50test2: Hello
2019-01-05 17:29:50test2: Hello


<!--T:14-->
</pre>
</pre>




<!--T:15-->
Стало:
Стало:
<pre>
<pre>
Строка 71: Строка 83:




=== Примечание ===
=== Примечание === <!--T:16-->


<!--T:17-->
В предыдущих версиях wb-rules для изоляции правил рекомендовалось использовать замыкание, т.е.  
В предыдущих версиях wb-rules для изоляции правил рекомендовалось использовать замыкание, т.е.  
оборачивание кода сценария в конструкцию:
оборачивание кода сценария в конструкцию:
Строка 81: Строка 94:
</syntaxhighlight>
</syntaxhighlight>


<!--T:18-->
Начиная с версии 1.7, в подобной конструкции обычно нет необходимости. Тем не менее, старые сценарии,
Начиная с версии 1.7, в подобной конструкции обычно нет необходимости. Тем не менее, старые сценарии,
использующие эту конструкцию, продолжат работу без изменений в поведении.
использующие эту конструкцию, продолжат работу без изменений в поведении.




=== Обходные пути ===
=== Обходные пути === <!--T:19-->


<!--T:20-->
Если в вашей системе использовалось общее глобальное пространство для хранения общих данных и функций,
Если в вашей системе использовалось общее глобальное пространство для хранения общих данных и функций,
есть несколько способов реализации такого поведения:
есть несколько способов реализации такого поведения:




==== Постоянное хранилище ====
==== Постоянное хранилище ==== <!--T:21-->


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


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


<!--T:24-->
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
var ps = new PersistentStorage("my-global-storage", {global: true});
var ps = new PersistentStorage("my-global-storage", {global: true});


<!--T:25-->
/// ...
/// ...


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




==== Прототип глобального объекта ====
==== Прототип глобального объекта ==== <!--T:27-->


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


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


<!--T:30-->
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
global.__proto__.myVar = 42; // теперь myVar - общая переменная для всех сценариев
global.__proto__.myVar = 42; // теперь myVar - общая переменная для всех сценариев


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


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


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


<!--T:34-->
# Проверяем, есть ли myVar среди локальных переменных (определённой как var myVar = ...).
# Проверяем, есть ли myVar среди локальных переменных (определённой как var myVar = ...).
# Если нет, проверяем, есть ли myVar в глобальном объекте (определённой как myVar = ...).
# Если нет, проверяем, есть ли myVar в глобальном объекте (определённой как myVar = ...).
# Если нет, проверяем, есть ли myVar в ''прототипе'' глобального объекта (определённой как global.__proto__.myVar).
# Если нет, проверяем, есть ли myVar в ''прототипе'' глобального объекта (определённой как global.__proto__.myVar).


<!--T:35-->
Поиск останавливается, как только переменная найдена.
Поиск останавливается, как только переменная найдена.


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






== Постоянное хранилище данных ==
== Постоянное хранилище данных == <!--T:37-->


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


<!--T:39-->
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
var ps = new PersistentStorage("my-storage", { global: true });
var ps = new PersistentStorage("my-storage", { global: true });


<!--T:40-->
ps.key = "Hello World";
ps.key = "Hello World";
log(ps.key);
log(ps.key);
</syntaxhighlight>
</syntaxhighlight>


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




== Виртуальные устройства ==
== Виртуальные устройства == <!--T:42-->


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


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


<!--T:45-->
<syntaxhighlight lang="js">
<syntaxhighlight lang="js">
defineVirtualDevice("vdev", {
defineVirtualDevice("vdev", {

Версия 17:38, 6 июня 2019

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

В обновлённом движке правил wb-rules присутствует ряд важных нововведений, касающихся логики написания сценариев.

Сценарии

Изоляция сценариев

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


Пример

В качестве примера приведём два сценария, одновременно запускаемых в движке правил. Каждый сценарий определяет переменные и функции.

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

В комментариях указан вывод команд log для ранних версий и для актуальной версии. Обратите внимание на var перед переменными.

Сценарий 1 (rules1.js)

var test1 = 42;

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

Сценарий 2 (rules2.js)

var test1 = 84;
var test2 = "Hello";

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


Было:

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


Стало:

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



Примечание

В предыдущих версиях wb-rules для изоляции правил рекомендовалось использовать замыкание, т.е. оборачивание кода сценария в конструкцию:

(function() {
    // код сценария идёт здесь
})();

Начиная с версии 1.7, в подобной конструкции обычно нет необходимости. Тем не менее, старые сценарии, использующие эту конструкцию, продолжат работу без изменений в поведении.


Обходные пути

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


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

Для обмена данными также можно использовать глобальные постоянные хранилища (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
        }
    }
});