Движок правил wb-rules 1.7/en: различия между версиями
(Новая страница: «<syntaxhighlight lang="js"> defineVirtualDevice("vdev", { ... cells: { ... mycell: { type: "value", value: "10",…») |
(Новая страница: «= Scripts =») |
||
(не показано 37 промежуточных версий этого же участника) | |||
Строка 5: | Строка 5: | ||
== | == Изоляция сценариев == | ||
Начиная с версии wb-rules 1.7, локальные переменные и функции, обьявленные в файле сценария не видны в других сценариях. | |||
Таким образом, каждый сценарий может определять свои функции и переменные без риска изменить поведение других сценариев. | |||
=== | === Пример === | ||
В качестве примера приведём два сценария, одновременно запускаемых в движке правил. | |||
Каждый сценарий определяет переменные и функции. | |||
В предыдущих версиях wb-rules обращение к переменной, изменяемой в нескольких | |||
файлах сценариев, может привести к неопределённому поведению. В версиях, начиная с 1.7, | |||
поведение строго определено и такое же, как будто сценарий единственный в системе. | |||
В комментариях указан вывод команд log для ранних версий и для актуальной версии. Обратите внимание на var перед переменными. | |||
'''rules1.js''' | '''Сценарий 1 (rules1.js)''' | ||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
var test1 = 42; | var test1 = 42; | ||
Строка 33: | Строка 33: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
''' rules2.js''' | '''Сценарий 2 (rules2.js)''' | ||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
test1 = 84; | var test1 = 84; | ||
test2 = "Hello"; | var test2 = "Hello"; | ||
setInterval(function myFuncTwo() { | setInterval(function myFuncTwo() { | ||
Строка 45: | Строка 45: | ||
Было: | |||
<pre> | <pre> | ||
2019-01-05 17:29:50myFuncTwo called | 2019-01-05 17:29:50myFuncTwo called | ||
Строка 56: | Строка 56: | ||
Стало: | |||
<pre> | <pre> | ||
2019-01-05 17:28:42myFuncTwo called | 2019-01-05 17:28:42myFuncTwo called | ||
Строка 70: | Строка 70: | ||
=== | === Примечание === | ||
В предыдущих версиях wb-rules для изоляции правил рекомендовалось использовать замыкание, т.е. | |||
оборачивание кода сценария в конструкцию: | |||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
(function() { | (function() { | ||
// | // код сценария идёт здесь | ||
})(); | })(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Начиная с версии 1.7, в подобной конструкции обычно нет необходимости. Тем не менее, старые сценарии, | |||
использующие эту конструкцию, продолжат работу без изменений в поведении. | |||
=== | === Обходные пути === | ||
Если в вашей системе использовалось общее глобальное пространство для хранения общих данных и функций, | |||
есть несколько способов реализации такого поведения: | |||
==== | ==== Постоянное хранилище ==== | ||
Для обмена данными также можно использовать глобальные постоянные хранилища (PersistentStorage). | |||
''' | '''Внимание:''' при использовании глобальных постоянных хранилищ может произойти совпадение имён, в | ||
этом случае возможно труднообнаруживаемое нарушение поведения. | |||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
Строка 102: | Строка 102: | ||
/// ... | /// ... | ||
ps.myvar = "value"; // | ps.myvar = "value"; // это значение доступно для всех пользователей хранилища с именем "my-global-storage" | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== | ==== Прототип глобального объекта ==== | ||
''' | '''ВНИМАНИЕ:''' метод считается "грязным", т.к. все переменные и функции, опубликованные таким образом, | ||
становятся доступными всем сценариям в системе. Старайтесь избегать этого способа. За неопределённое | |||
поведение при использовании этого метода несёт ответственность сам программист. | |||
Глобальные объекты всех сценариев имеют общий объект-''прототип'', в котором определены стандартные функции | |||
wb-rules ( | wb-rules (такие, как defineRule, setTimeout и т.д.). Через него можно передавать переменные или функции | ||
в общую область видимости. | |||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
global.__proto__.myVar = 42; // | global.__proto__.myVar = 42; // теперь myVar - общая переменная для всех сценариев | ||
// | // из других сценариев к переменной можно обращаться так | ||
log("shared myVar: {}", myVar); | log("shared myVar: {}", myVar); | ||
// | // или вот так, что чуть более аккуратно, т.к. однозначно показывает, где определена переменная | ||
log("shared myVar: {}", global.__proto__.myVar); | log("shared myVar: {}", global.__proto__.myVar); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Правило поиска переменной в первом случае будет выглядеть так: | |||
# | # Проверяем, есть ли myVar среди локальных переменных (определённой как var myVar = ...). | ||
# | # Если нет, проверяем, есть ли myVar в глобальном объекте (определённой как myVar = ...). | ||
# | # Если нет, проверяем, есть ли myVar в ''прототипе'' глобального объекта (определённой как global.__proto__.myVar). | ||
Поиск останавливается, как только переменная найдена. | |||
Таким образом, первый способ обращения будет работать только в том случае, если myVar не определена в верхних областях видимости. | |||
== Постоянное хранилище данных == | |||
В wb-rules 1.7 добавлена поддержка постоянных хранилищ. По сути, это объекты, значения в которых будут сохраняться | |||
даже при потере питания контроллера. Такие хранилища удобно использовать для хранения состояний или конфигурации. | |||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
Строка 149: | Строка 150: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Поддерживаются только глобальные хранилища, т.е. видимые по одному и тому же имени из всех файлов сценариев. | |||
== | == Виртуальные устройства == | ||
В предыдущих версиях wb-rules значения контролов виртуальных устройств хранились только в MQTT retained, что не очень надёжно (в случае | |||
потери питания данные могли быть легко утеряны). Начиная с версии 2.0, эти значения сохраняются также в специальное хранилище в постоянной | |||
памяти и восстанавливаются при загрузке сценария. | |||
Если необходимо каждый раз при перезагрузке скрипта восстанавливать строго определённое значение (т.е. не восстанавливать предыдущее сохранённое), | |||
можно добавить в описание контрола поле forceDefault: | |||
<syntaxhighlight lang="js"> | <syntaxhighlight lang="js"> | ||
Строка 169: | Строка 170: | ||
type: "value", | type: "value", | ||
value: "10", | value: "10", | ||
forceDefault: true // | forceDefault: true // при каждой загрузке сценария поле mycell будет получать значение 10 | ||
} | } | ||
} | } | ||
}); | }); | ||
</syntaxhighlight> | </syntaxhighlight> |
Версия 17:57, 6 июня 2019
The updated wb-rules engine contains a number of important innovations regarding the logic of scripting.
Scripts
Изоляция сценариев
Начиная с версии 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);
Правило поиска переменной в первом случае будет выглядеть так:
- Проверяем, есть ли myVar среди локальных переменных (определённой как var myVar = ...).
- Если нет, проверяем, есть ли myVar в глобальном объекте (определённой как myVar = ...).
- Если нет, проверяем, есть ли 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
}
}
});