Совместимость скриптов при обновлении wb-rules

Материал из Wiren Board
(перенаправлено с «Совместимость скриптов при обновлении wb-rules»)

Это утверждённая версия страницы. Она же — наиболее свежая версия.

Обновление до версии 2.2

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

  • Раньше для установки признака включено/выключено для switch и состояний pushbutton можно было присваивать значение 1 или 0 — теперь только true/false.
  • Раньше контролу типа text можно было присвоить число и оно автоматически конвертировалось текст — теперь только текст. Если нужно записать число, то используйте (val).toString() для конвертации.
  • Раньше для передачи в контролы типа value и его подтипов (Temperature, Power, Voltage и прочие) значений допускалось указывать значения в кавычках — теперь нужно указывать только число.

Если в коде есть ошибка, то выполнение скрипта останавливается и в логи добавляются записи вида:

ERROR: control wb-mr3_30/K1 SetValue() error: can't convert control value '1' (type float64) to datatype 'switch'
ERROR: control system/Reboot SetValue() error: can't convert control value '1' (type float64) to datatype 'pushbutton'
ERROR: control status/someStatus SetValue() error: can't convert control value '-1.47' (type float64) to datatype 'text'

Раньше писали так:

dev["wb-mr3_30"]["K1"] = 1 // включение
dev["status"]["someStatus"] = -1.47 // float
dev["temperature"]["value"] = "36.6" // text

Теперь только так:

dev["wb-mr3_30"]["K1"] = true // включение
dev["status"]["someStatus"] = (-1.47).toString() // text
dev["temperature"]["value"] = 36.6 // float

Обновление до версии 1.7

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

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

Сценарий 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:50 myFuncTwo called
2019-01-05 17:29:50 test1: 84, test2: Hello
2019-01-05 17:29:50 myFuncOne called
2019-01-05 17:29:50 test1: 84
2019-01-05 17:29:50 test2: Hello

Стало:

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

Возможные способы решения проблемы:

Способ 1

Самый простой способ: перенести всю логику, которая использует доступ к переменной в другм файле в тот же файл, где определена переменная. Т.е. на примере приведённых выше правил нужно перенести всё в один файл rules1.js

var test1 = 42;
var test2 = "Hello";

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

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

Способ 2

Следующий способ заключается в использовании глобального постоянного хранилища (PersistentStorage) ====

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

Вышеприведённый пример можно исправить следующим образом:

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

var test1 = 42;

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

ps.test1 = test1;

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

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

var test2 = "Hello";

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

ps.test2 = test2;

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


Способ 3

"Грязный" способ, который использует прототип глобального объекта - использовать этот способ не рекомендуется из-за возможного замусоривания глобального пространства пользовательскими данными.

Исправленная версия:

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

global.__proto__.test1 = 42;

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

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

global.__proto__.test2 = "Hello";

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