|
|
Строка 10: |
Строка 10: |
|
| |
|
|
| |
|
| == Совместимость скриптов при обновлении wb-rules ==
| |
|
| |
| Предполагается, что при обновлении с предыдущей на следующую версию wb-rules и при соблюдении гайдлайнов при написании скриптов - все сценарии продолжат работать без каких-либо изменений.
| |
| Далее перечислены возможные проблемы в связи с изменением логики обработки скриптов новыми версиями движка.
| |
|
| |
|
| === Обновление до версии 2.2 ===
| |
|
| |
| С версии 2.2 стали более строго проверяться типы устанавливаемых значений для контролов:
| |
|
| |
| если для установления признака включено/выключено для контролов типа switch, клики по pushbutton или присваивание значений контролам с типом text применялась недокументировання возможность
| |
| использования для этой цели числовые значения (1, 0 и т.д.) в версии движка 2.2 операция присваивания не выполнится и завершится с ошибкой.
| |
| Корректный способ — устанавливать булевы значений (true/false) для switch/pushbutton и строковые значения для типа text
| |
|
| |
| При возникновении подобной проблемы в логах можно видеть подобные записи:
| |
| <pre>
| |
| 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'
| |
| </pre>
| |
|
| |
| Вместо:
| |
| <syntaxhighlight lang="ecmascript">
| |
| dev["wb-mr3_30"]["K1"] = 1 // включение
| |
| dev["status"]["someStatus"] = -1.47 // float
| |
| </syntaxhighlight>
| |
|
| |
| Нужно:
| |
| <syntaxhighlight lang="ecmascript">
| |
| dev["wb-mr3_30"]["K1"] = true // включение
| |
| dev["status"]["someStatus"] = (-1.47).toString() // text
| |
| </syntaxhighlight>
| |
|
| |
| === Обновление до версии 1.7 ===
| |
|
| |
| Начиная с версии wb-rules 1.7, локальные переменные и функции, объявленные в файле сценария не видны в других сценариях.
| |
| Таким образом, каждый сценарий может определять свои функции и переменные без риска изменить поведение других сценариев.
| |
|
| |
| В качестве примера приведём два сценария, одновременно запускаемых в движке правил. Каждый сценарий определяет переменные и функции.
| |
| В предыдущих версиях wb-rules обращение к переменной, изменяемой в нескольких файлах сценариев, может привести к неопределённому поведению.
| |
| В версиях, начиная с 1.7, поведение строго определено и такое же, как будто сценарий единственный в системе.
| |
|
| |
| Сценарий 1 (rules1.js):
| |
| <syntaxhighlight lang="ecmascript">
| |
| var test1 = 42;
| |
|
| |
| setInterval(function myFuncOne() {
| |
| log("myFuncOne called");
| |
| log("test1: {}", test1);
| |
| log("test2: {}", test2);
| |
| }, 5000);
| |
| </syntaxhighlight>
| |
|
| |
| Сценарий 2 (rules2.js):
| |
| <syntaxhighlight lang="ecmascript">
| |
| var test1 = 84;
| |
| var test2 = "Hello";
| |
|
| |
| setInterval(function myFuncTwo() {
| |
| log("myFuncTwo called");
| |
| log("test1: {}, test2: {}", test1, test2);
| |
| }, 5000);
| |
| </syntaxhighlight>
| |
|
| |
| Было:
| |
| <pre>
| |
| 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
| |
| </pre>
| |
|
| |
| Стало:
| |
| <pre>
| |
| 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
| |
| </pre>
| |
|
| |
| Возможные способы решения проблемы:
| |
|
| |
| '''Способ 1'''
| |
|
| |
| Самый простой способ: перенести всю логику, которая использует доступ к переменной в другм файле в тот же файл, где определена переменная. Т.е. на примере приведённых выше правил нужно перенести всё в один файл rules1.js
| |
| <syntaxhighlight lang="ecmascript">
| |
| 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);
| |
| </syntaxhighlight>
| |
|
| |
| '''Способ 2'''
| |
|
| |
| Следующий способ заключается в использовании глобального постоянного хранилища (PersistentStorage) ====
| |
|
| |
| '''Внимание:''' при использовании глобальных постоянных хранилищ может произойти совпадение имён, в этом случае возможно труднообнаруживаемое нарушение поведения.
| |
|
| |
| Вышеприведённый пример можно исправить следующим образом:
| |
|
| |
| Сценарий 1 (rules1.js):
| |
| <syntaxhighlight lang="ecmascript">
| |
| 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);
| |
| </syntaxhighlight>
| |
|
| |
| Сценарий 2 (rules2.js):
| |
| <syntaxhighlight lang="ecmascript">
| |
| 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);
| |
| </syntaxhighlight>
| |
|
| |
|
| |
| '''Способ 3'''
| |
|
| |
| "Грязный" способ, который использует прототип глобального объекта - использовать этот способ не рекомендуется из-за возможного замусоривания глобального пространства пользовательскими данными.
| |
|
| |
| Исправленная версия:
| |
|
| |
| Сценарий 1 (rules1.js):
| |
| <syntaxhighlight lang="ecmascript">
| |
| global.__proto__.test1 = 42;
| |
|
| |
| setInterval(function myFuncOne() {
| |
| log("myFuncOne called");
| |
| log("test1: {}", global.__proto__.test1);
| |
| log("test2: {}", global.__proto__.test2);
| |
| }, 5000);
| |
| </syntaxhighlight>
| |
|
| |
| Сценарий 2 (rules2.js):
| |
| <syntaxhighlight lang="ecmascript">
| |
| global.__proto__.test2 = "Hello";
| |
|
| |
| setInterval(function myFuncTwo() {
| |
| log("myFuncTwo called");
| |
| log("test1: {}, test2: {}", global.__proto__.test1, global.__proto__.test2);
| |
| }, 5000);
| |
| </syntaxhighlight>
| |
|
| |
|
| == Как создавать и редактировать правила == <!--T:3--> | | == Как создавать и редактировать правила == <!--T:3--> |
Строка 261: |
Строка 94: |
|
| |
|
|
| |
|
| | | == Виртуальное устройство defineVirtualDevice == <!--T:17--> |
| === Пишем сложные правила === <!--T:17--> | |
| | |
| <!--T:18-->
| |
| Чтобы начать писать сложные правила, нужно посмотреть примеры правил и полную документацию по движку правил:
| |
| #Примеры правил:
| |
| #* на этой же странице ниже;
| |
| #* в [http://forums.contactless.ru/t/dvizhok-pravil-primery-koda/483 специальной теме на нашем форуме ].
| |
| #[https://github.com/contactless/wb-rules Полное описание движка правил].
| |
|
| |
|
|
| |
|
|
| |
|
| == Примеры правил == <!--T:19-->
| | Виртуальные устройства - это появляющиеся в веб-интерфейсе новые элементы управления - например, кнопка-выключатель, которая на самом деле выключает два устройства одновременно. Она не привязана напрямую ни к какому физическому устройству, а действия при её нажатии определяются написанным вами скриптом. |
| | При написании скрипта вы можете создать виртуальное устройство для включения/выключения тех или иных управляющих алгоритмов и установки их параметров. |
| | <!--T:20--> |
| | <syntaxhighlight lang="ecmascript"> |
| | defineVirtualDevice("wb-1", { |
| | title: "Отопление", |
| | cells: { |
| | "Температура": { |
| | type: "range", |
| | max: 24, |
| | value: 20, |
| | }, |
| | "Вкл.": { |
| | type: "switch", |
| | value: false, |
| | } |
| | } |
| | }); |
|
| |
|
| | </syntaxhighlight> |
|
| |
|
| === Слежение за контролом === <!--T:20--> | | === Типы устройств (type): === |
|
| |
|
| | '''switch''' — переключатель. Может принимать значения true или false. По-умолчанию доступен для изменения пользователем. |
| <!--T:21--> | | <!--T:21--> |
| Это простейшее правило следит за контролом и устанавливает другой контрол в такое же состояние.
| |
|
| |
|
| | '''wo-switch''' — переключатель, аналог switch. Может принимать значения true или false. По-умолчанию не доступен для изменения пользователем. |
| <!--T:22--> | | <!--T:22--> |
| Например правило может включать сирену и лампу, если датчик движения заметил движение.
| |
|
| |
|
| | '''pushbutton''' — кнопка. Может принимать значения true. По-умолчанию доступна для нажатия. |
| | range — ползунок. Может принимать значения от 0 до max. По-умолчанию доступен для изменения пользователем. |
| <!--T:23--> | | <!--T:23--> |
| В примере датчик движения подключен к входу "сухой контакт", контрол типа "switch". Сирена подключена к встроеному реле Wiren Board, а лампа - через релейный блок по Modbus. Когда вход типа "сухой контакт" (выход датчика движения) замкнут, то на лампу и реле подаётся "1", когда выключен - "0".
| |
|
| |
|
| |
|
| | '''rgb''' — специальный тип для задания цвета. Кодируется 3 числами от 0 до 255, разделенными точкой с запятой. Например „255;0;0“ задает красный цвет. По-умолчанию доступен для изменения пользователем. |
| <!--T:24--> | | <!--T:24--> |
| Правило срабатывает каждый раз при изменении значения контрола "D1_IN" у устройства "wb-gpio". В код правила передаётся новое значение этого контрола в виде переменной newValue.
| |
|
| |
|
| | '''alarm''' — индикатор. Может принимать значения true или false. |
| <!--T:25--> | | <!--T:25--> |
| <syntaxhighlight lang="ecmascript">
| |
|
| |
|
| | '''text''' — текстовое поле. По-умолчанию не доступно для редактирования пользователем. |
| <!--T:26--> | | <!--T:26--> |
| defineRule("motion_detector", {
| |
| whenChanged: "wb-gpio/D1_IN",
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-gpio"]["Relay_2"] = newValue;
| |
| dev["wb-mrm2_6"]["Relay 1"] = newValue;
| |
|
| |
|
| <!--T:27-->
| | '''value''' — значение с плавающей точкой. По-умолчанию не доступно для редактирования пользователем. |
| }
| | <!--T:27--> |
| });
| |
|
| |
|
| | Так же существуют еще 14 специальных типов. Все они аналогичны value, но имеют соответствующие подписи в интерфейсе. |
| <!--T:28--> | | <!--T:28--> |
| </syntaxhighlight>
| |
|
| |
|
| |
|
| | {| class="wikitable" |
| | ! Type |
| | ! meta/type |
| | ! units |
| | !value format |
| | |- |
| | | Temperature |
| | | temperature |
| | | °C |
| | | float |
| | |- |
| | | Relative humidity |
| | | rel_humidity |
| | | %, RH |
| | | float, 0 - 100 |
| | |- |
| | | Atmospheric pressure |
| | | atmospheric_pressure |
| | | millibar (100 Pa) |
| | | float |
| | |- |
| | | Precipitation rate (rainfall rate) |
| | | rainfall |
| | | mm per hour |
| | | float |
| | |- |
| | | Wind speed |
| | | wind_speed |
| | | m/s |
| | | float |
| | |- |
| | | Power |
| | | power |
| | | watt |
| | | float |
| | |- |
| | | Power consumption |
| | | power_consumption |
| | | kWh |
| | | float |
| | |- |
| | | Voltage |
| | | voltage |
| | | volts |
| | | float |
| | |- |
| | | Water flow |
| | | water_flow |
| | | m^3 / hour |
| | | float |
| | |- |
| | | Water total consumption |
| | | water_consumption |
| | | m^3 |
| | | float |
| | |- |
| | | Resistance |
| | | resistance |
| | | resistance |
| | | float |
| | |- |
| | | Gas concentration |
| | | concentration |
| | | ppm |
| | | float (unsigned) |
| | |- |
| | | Heat power |
| | | heat_power |
| | | Gcal / hour |
| | | float |
| | |- |
| | | Heat energy |
| | | heat_energy |
| | | Gcal |
| | | float |
| | |} |
| <!--T:29--> | | <!--T:29--> |
| То же самое, но с виртуальным девайсом в качестве источника событий. Пример использования: сценарная кнопка, которая включает/выключает сирену и лампочку.
| |
|
| |
| <!--T:30-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("simple_test", {
| |
| title: "Simple switch",
| |
| cells: {
| |
| enabled: {
| |
| type: "switch",
| |
| value: false
| |
| },
| |
| }
| |
| });
| |
|
| |
|
| |
|
| | === value: === |
| | Обязательное поле. При создании устройства в первый раз его значение будет установлено в значение по-умолчанию. В дальнейшем значения сохраняются в специальное хранилище в постоянной памяти и восстанавливаются при загрузке сценария. |
| <!--T:31--> | | <!--T:31--> |
| defineRule("simple_switch", {
| |
| whenChanged: "simple_test/enabled",
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-gpio"]["Relay_2"] = newValue;
| |
| dev["wb-mrm2_6"]["Relay 1"] = newValue;
| |
|
| |
| <!--T:32-->
| |
| }
| |
| });
| |
|
| |
|
| | === forceDefault: === |
| | Если необходимо каждый раз при перезагрузке скрипта восстанавливать строго определённое значение (т.е. не восстанавливать предыдущее сохранённое), нужно добавить в описание контрола поле '''forceDefault: true''' |
| <!--T:33--> | | <!--T:33--> |
| </syntaxhighlight>
| |
|
| |
|
| |
|
| |
| === Детектор движения c таймаутом === <!--T:34-->
| |
|
| |
|
| | === max: === |
| | Для параметра типа '''range''' может задавать его максимально допустимое значение. |
| <!--T:35--> | | <!--T:35--> |
| На вход D2 подключен детектор движения с выходом типа "сухой контакт", который замыкает D2 и GND при обнаружении движения.
| |
| При этом, на канале "wb-gpio/D2_IN" появляется статус "1".
| |
|
| |
|
| <!--T:36-->
| | === readonly: === |
| Правило включает свет при обнаружении движения и выключает свет, спустя 30 секунд после пропадания сигнала с датчика движения.
| | Когда задано истинное значение — устройство становится не доступным для редактирования пользователем. Если надо предоставить пользователю возможность редактировать значение, следует добавить в описание '''readonly: false''' |
|
| |
|
| <!--T:37-->
| | |
| Освещение подключено через встроенное реле, канал wb-gpio/Relay_1.
| | == Типы правил defineRule == |
| | |
| <!--T:38-->
| |
| <syntaxhighlight lang="ecmascript">
| |
|
| |
|
| <!--T:39-->
| |
| var motion_timer_1_timeout_ms = 30 * 1000;
| |
| var motion_timer_1_id = null;
| |
|
| |
|
| | === whenChanged === |
| | Правило срабатывает при любых изменениях значений параметров или функций |
| | В примере кнопка подключена ко входу А1 контроллера. При нажатии на кнопку срабатывает реле 1 устройства MRM2-mini, при отжатии реле возвращается в исходное состояние. |
| <!--T:40--> | | <!--T:40--> |
| defineRule("motion_detector_1", {
| |
| whenChanged: "wb-gpio/D2_IN",
| |
| then: function (newValue, devName, cellName) {
| |
| if (newValue) {
| |
| dev["wb-gpio"]["Relay_1"] = 1;
| |
|
| |
| <!--T:41-->
| |
| if (motion_timer_1_id) {
| |
| clearTimeout(motion_timer_1_id);
| |
| }
| |
|
| |
| motion_timer_1_id = setTimeout(function () {
| |
| dev["wb-gpio"]["Relay_1"] = 0;
| |
| motion_timer_1_id = null;
| |
| }, motion_timer_1_timeout_ms);
| |
| }
| |
| }
| |
| });
| |
|
| |
| <!--T:42-->
| |
| </syntaxhighlight>
| |
|
| |
|
| |
|
| |
| === Создание однотипных правил === <!--T:43-->
| |
|
| |
| <!--T:44-->
| |
| Если таких детекторов движения нужно несколько, то, чтобы не копировать код, можно обернуть создание правила и переменных в функцию:
| |
|
| |
| <!--T:45-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| function makeMotionDetector(name, timeout_ms, detector_control, relay_control) {
| |
| var motion_timer_id = null;
| |
| defineRule(name, {
| |
| whenChanged: "wb-gpio/" + detector_control,
| |
| then: function(newValue, devName, cellName) {
| |
| if (!newValue) {
| |
| dev["wb-gpio"][relay_control] = 1;
| |
| if (motion_timer_id) {
| |
| clearTimeout(motion_timer_id);
| |
| }
| |
|
| |
| <!--T:46-->
| |
| motion_timer_id = setTimeout(function() {
| |
| dev["wb-gpio"][relay_control] = 0;
| |
| motion_timer_id = null;
| |
| }, timeout_ms);
| |
| }
| |
| }
| |
| });
| |
| }
| |
|
| |
| <!--T:47-->
| |
| makeMotionDetector("motion_detector_1", 20000, "EXT1_DR1", "EXT2_R3A1");
| |
| makeMotionDetector("motion_detector_2", 10000, "EXT1_DR2", "EXT2_R3A2");
| |
| makeMotionDetector("motion_detector_3", 10000, "EXT1_DR3", "EXT2_R3A3");
| |
| </syntaxhighlight>
| |
|
| |
|
| |
|
| |
| === Активация правила только в определённое время === <!--T:48-->
| |
|
| |
| <!--T:49-->
| |
| Правило как в предыдущем разделе, но выполняется только с 9:30 до 17:10 по UTC.
| |
|
| |
|
| <!--T:50-->
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| var motion_timer_1_timeout_ms = 5 * 1000;
| | defineRule("test_rule", { //имя правила test_rule |
| var motion_timer_1_id = null;
| |
|
| |
| defineRule("motion_detector_1", { | |
| whenChanged: "wb-gpio/A1_IN", | | whenChanged: "wb-gpio/A1_IN", |
| then: function (newValue, devName, cellName) { | | then: function (newValue, devName, cellName) { |
| var date = new Date(); | | dev["wb-mrm2-mini_2"]["Relay 1"] = newValue; |
| | | } |
| <!--T:51-->
| |
| // time point marking the beginning of the interval
| |
| // i.e. "today, at HH:MM". All dates are in UTC!
| |
| var date_start = new Date(date);
| |
| date_start.setHours(9);
| |
| date_start.setMinutes(30);
| |
| | |
| <!--T:52-->
| |
| // time point marking the end of the interval
| |
| var date_end = new Date(date);
| |
| date_end.setHours(17);
| |
| date_end.setMinutes(10);
| |
|
| |
| // if time is between 09:30 and 17:10 UTC
| |
| if ((date > date_start) && (date < date_end)) {
| |
| if (newValue) {
| |
| dev["wb-gpio"]["EXT1_R3A1"] = 1;
| |
|
| |
| if (motion_timer_1_id) {
| |
| clearTimeout(motion_timer_1_id);
| |
| }
| |
|
| |
| motion_timer_1_id = setTimeout(function () {
| |
| dev["wb-gpio"]["EXT1_R3A1"] = 0;
| |
| motion_timer_1_id = null;
| |
| }, motion_timer_1_timeout_ms);
| |
| }
| |
| }
| |
| }
| |
| }); | | }); |
| </syntaxhighlight>
| |
|
| |
|
| |
|
| |
| === Роллеты === <!--T:53-->
| |
|
| |
| <!--T:54-->
| |
| Одно реле включает двигатель, поднимающий шторы, второе реле - включает двигатель, опускающий шторы.
| |
| Правило следит за тем, чтобы оба реле не были включены одновременно.
| |
|
| |
| <!--T:55-->
| |
| Кроме этого, правило отключает двигатели спустя заданное время после включения.
| |
|
| |
| <!--T:56-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| (function() { //don't touch this line
| |
|
| |
| var suffix = "1"; // must be different in different JS files
| |
|
| |
| var relay_up_device = "lc103_4";
| |
| var relay_up_control = "Relay 1";
| |
|
| |
| <!--T:57-->
| |
| var relay_down_device = "lc103_4";
| |
| var relay_down_control = "Relay 2";
| |
|
| |
| <!--T:58-->
| |
| var timeout_s = 15;
| |
|
| |
| // End of settings
| |
|
| |
|
| |
| var relay_up_timer_id = null;
| |
| var relay_down_timer_id = null;
| |
|
| |
| defineRule( "roller_shutter_up_on" + suffix, {
| |
| asSoonAs: function() {
| |
| return dev[relay_up_device][relay_up_control];
| |
| },
| |
| then: function () {
| |
| if (relay_up_timer_id) {
| |
| relay_up_timer_id = clearTimeout(relay_up_timer_id);
| |
| };
| |
|
| |
| <!--T:59-->
| |
| relay_up_timer_id = setTimeout(function() {
| |
| return dev[relay_up_device][relay_up_control] = 0;
| |
| }, timeout_s * 1000);
| |
| }
| |
| });
| |
|
| |
| <!--T:60-->
| |
| defineRule("roller_shutter_down_on" + suffix, {
| |
| asSoonAs: function() {
| |
| return dev[relay_down_device][relay_down_control];
| |
| },
| |
| then: function () {
| |
| if (relay_down_timer_id) {
| |
| relay_down_timer_id = clearTimeout(relay_down_timer_id);
| |
| };
| |
|
| |
| relay_down_timer_id = setTimeout(function() {
| |
| dev[relay_down_device][relay_down_control] = 0;
| |
| }, timeout_s * 1000);
| |
| }
| |
| });
| |
|
| |
| <!--T:61-->
| |
| defineRule("roller_shutter_both_on" + suffix, {
| |
| asSoonAs: function() {
| |
| return dev[relay_up_device][relay_up_control] && dev[relay_down_device][relay_down_control];
| |
| },
| |
| then: function () {
| |
| if (relay_up_timer_id) {
| |
| relay_up_timer_id = clearTimeout(relay_up_timer_id);
| |
| };
| |
|
| |
|
| <!--T:62-->
| |
| if (relay_down_timer_id) {
| |
| relay_down_timer_id = clearTimeout(relay_down_timer_id);
| |
| };
| |
|
| |
|
| |
| <!--T:63-->
| |
| dev[relay_up_device][relay_up_control] = 0;
| |
| dev[relay_down_device][relay_down_control] = 0;
| |
| log("Both roller shutter relays on, switching them off");
| |
| }
| |
| });
| |
| })();
| |
| </syntaxhighlight> | | </syntaxhighlight> |
| | <!--T:41--> |
|
| |
|
| | | === asSoonAs === |
| | | Правила, задаваемые при помощи asSoonAs, срабатывают в случае, когда значение, возвращаемое функцией, заданной в asSoonAs, становится истинным при том, что при предыдущем просмотре данного правила оно было ложным. |
| <!--T:64--> | | <!--T:43--> |
| Более старая версия того же сценария демонстрирует использование alias-ов:
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| | defineRule({ |
| | asSoonAs: function() { |
| | return dev["wb-gpio"]["A2_IN"]; |
| | }, |
| | then: function (newValue, devName, cellName) { |
| | dev["wb-mrm2-mini_2"]["Relay 2"] = true; |
| | } |
| | }); |
| | defineRule({ |
| | asSoonAs: function() { |
| | return dev["wb-gpio"]["A3_IN"]; |
| | }, |
| | then: function (newValue, devName, cellName) { |
| | dev["wb-mrm2-mini_2"]["Relay 2"] = false; |
| | } |
| | }); |
|
| |
|
| <!--T:65-->
| |
| (function() {
| |
| defineAlias("relay_up_1", "lc103_4/Relay 1");
| |
| defineAlias("relay_down_1", "lc103_4/Relay 2");
| |
| var timeout_s = 15;
| |
|
| |
| <!--T:66-->
| |
| defineRule("roller_shutter_1_up_on", {
| |
| asSoonAs: function() {
| |
| return relay_up_1;
| |
| },
| |
| then: function () {
| |
| setTimeout(function() {
| |
| relay_up_1 = 0;
| |
| }, timeout_s * 1000);
| |
| }
| |
| });
| |
|
| |
| <!--T:67-->
| |
| defineRule("roller_shutter_1_down_on", {
| |
| asSoonAs: function() {
| |
| return relay_down_1;
| |
| },
| |
| then: function () {
| |
| setTimeout(function() {
| |
| relay_down_1 = 0;
| |
| }, timeout_s * 1000);
| |
| }
| |
| });
| |
|
| |
| <!--T:68-->
| |
| defineRule("roller_shutter_1_both_on", {
| |
| asSoonAs: function() {
| |
| return relay_up_1 && relay_down_1;
| |
| },
| |
| then: function () {
| |
| relay_up_1 = 0;
| |
| relay_down_1 = 0;
| |
| log("Both roller shutter relays on, switching them off");
| |
| }
| |
| });
| |
| })();
| |
|
| |
| <!--T:69-->
| |
| </syntaxhighlight> | | </syntaxhighlight> |
|
| |
|
| | При нажатии на кнопку, подключенную к входу А2, включаем реле, а при нажатии на кнопку, подключенную к входу А3 — выключаем. |
|
| |
|
|
| |
|
| === Системные правила === <!--T:70--> | | === when === |
| | |
| <!--T:71-->
| |
| Некоторые правила поставляются с системой правил по умолчанию в пакете wb-rules-system.
| |
| | |
| <!--T:72-->
| |
| Полный список правил [https://github.com/contactless/wb-rules-system/tree/master/rules в репозитории].
| |
| | |
| <!--T:73-->
| |
| Некоторые примеры:
| |
| | |
| | |
| | |
| ==== Правило для пищалки ==== <!--T:74-->
| |
|
| |
|
| <!--T:75-->
| | Правила, задаваемые при помощи when срабатывают при каждом просмотре, при котором функция, заданная в when, возвращает истинное значение. При срабатывании правила выполняется функция, заданная в свойстве when. |
| [https://github.com/contactless/wb-rules-system/blob/master/rules/buzzer.js Правило] создаёт виртуальное устройство buzzer с ползунками для регулировки громкости и частоты, а также кнопкой включения звука.
| |
|
| |
|
|
| |
| <!--T:76-->
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| defineVirtualDevice("buzzer", { | | defineVirtualDevice("test_button2", { |
| title: "Buzzer", // | | title: "test_relay2", |
| | | cells: { |
| <!--T:77--> | | "switch_on": { |
| cells: { | | type: "pushbutton", |
| frequency : { | | value: false, |
| type : "range",
| |
| value : 3000,
| |
| max : 7000,
| |
| }, | |
| volume : {
| |
| type : "range",
| |
| value : 10,
| |
| max : 100,
| |
| }, | |
| enabled : {
| |
| type : "switch",
| |
| value : false,
| |
| }, | | }, |
| | "switch_off": { |
| | type: "pushbutton", |
| | value: false, |
| | } |
| } | | } |
| }); | | }); |
|
| |
|
| | | defineRule({ |
| <!--T:78-->
| | when: function() { |
| // setup pwm2
| | return dev["test_button2"]["switch_on"]; |
| runShellCommand("echo 2 > /sys/class/pwm/pwmchip0/export");
| | }, |
| | | then: function (newValue, devName, cellName) { |
| | | dev["wb-mrm2-mini_2"]["Relay 2"] = true; |
| | |
| <!--T:79-->
| |
| function _buzzer_set_params() { | |
| var period = parseInt(1.0 / dev.buzzer.frequency * 1E9);
| |
| var duty_cycle = parseInt(dev.buzzer.volume * 1.0 / 100 * period * 0.5);
| |
| | |
| | |
| <!--T:80-->
| |
| runShellCommand("echo " + period + " > /sys/class/pwm/pwmchip0/pwm2/period");
| |
| runShellCommand("echo " + duty_cycle + " > /sys/class/pwm/pwmchip0/pwm2/duty_cycle");
| |
| }; | |
| | |
| | |
| <!--T:81-->
| |
| defineRule("_system_buzzer_params", {
| |
| whenChanged: [
| |
| "buzzer/frequency",
| |
| "buzzer/volume",
| |
| ],
| |
| | |
| <!--T:82--> | |
| then: function (newValue, devName, cellName) { | |
| if ( dev.buzzer.enabled) { | |
| _buzzer_set_params();
| |
| }
| |
| } | | } |
| }); | | }); |
|
| |
|
| | | defineRule({ |
| <!--T:83-->
| | when: function() { |
| defineRule("_system_buzzer_onof", { | | return dev["test_button2"]["switch_off"]; |
| whenChanged: "buzzer/enabled", | | }, |
| then: function (newValue, devName, cellName) { | | then: function (newValue, devName, cellName) { |
| if ( dev.buzzer.enabled) { | | dev["wb-mrm2-mini_2"]["Relay 2"] = false; |
| _buzzer_set_params();
| | } |
| runShellCommand("echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable");
| |
| } else {
| |
| runShellCommand("echo 0 > /sys/class/pwm/pwmchip0/pwm2/enable");
| |
| }
| |
| }
| |
| }); | | }); |
|
| |
|
| |
|
| |
| <!--T:84-->
| |
| </syntaxhighlight> | | </syntaxhighlight> |
|
| |
|
| | При нажатии на кнопку switch_on, включаем реле, а при нажатии на кнопку,switch_off — выключаем. |
| | <!--T:49--> |
|
| |
|
| | === cron-правила === |
|
| |
|
| ==== Правило для статуса питания ==== <!--T:85-->
| | Отдельный тип правил - cron-правила. Такие правила задаются следующим образом: |
|
| |
|
| <!--T:86--> | | <syntaxhighlight lang="ecmascript"> |
| [https://github.com/contactless/wb-rules-system/blob/master/rules/power_status.js Правило] создаёт виртуальное устройство, которое сообщает текущий статус питания. В качестве входных данных используется два канала АЦП: измерение напряжения на аккумуляторе и измерение входного напряжения.
| | defineRule("crontest_hourly", { |
| | when: cron("@hourly"), |
| | then: function () { |
| | log("@hourly rule fired"); |
| | } |
| | }) |
| | </syntaxhighlight> |
|
| |
|
| <!--T:87-->
| | Вместо @hourly здесь можно задать любое выражение, допустимое в стандартном crontab, например, 0 20 * * * (выполнять правило каждый день в 20:00). Помимо стандартных выражений допускается использование ряда расширений, см. описание формата выражений используемой cron-библиотеки. |
| Реализована следующая логика:
| |
|
| |
|
| <!--T:88-->
| |
| 1. Если входное напряжение меньше напряжение на аккумуляторе, то значит плата питается от аккумулятора. В этом случае, также отображается 0V в качестве входного напряжения.
| |
|
| |
|
| <!--T:89-->
| | == Таймеры == |
| 2. Если входное напряжение больше напряжения на аккумуляторе, то плата работает от внешнего источника питания. В качестве входонго напряжения отображается измерение с канала Vin.
| |
|
| |
|
| | === setTimeout(); === |
|
| |
|
| <!--T:90-->
| | С помощью данного таймера можно отсрочить выполнение правила на определенное время. В примере после нажатия кнопки включается пищалка и затем выключается спустя 2 секунды. |
| Для иллюстрации правила используют два разных способа срабатывания: по изменению значения контрола (правило _system_track_vin) и по изменению значения выражения (два других).
| |
|
| |
|
| <!--T:91-->
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| | | defineVirtualDevice("test_buzzer", { |
| <!--T:92-->
| | title: "Test Buzzer", |
| defineVirtualDevice("power_status", { | | cells: { |
| title: "Power status", //
| | enabled: { |
| | | type: "pushbutton", |
| <!--T:93--> | | value: false |
| cells: { | |
| 'working on battery' : { | |
| type : "switch",
| |
| value : false,
| |
| readonly : true
| |
| },
| |
| 'Vin' : {
| |
| type : "voltage",
| |
| value : 0
| |
| }
| |
| | |
| | |
| <!--T:94-->
| |
| }
| |
| });
| |
| | |
| | |
| | |
| <!--T:95-->
| |
| defineRule("_system_track_vin", {
| |
| whenChanged: "wb-adc/Vin",
| |
| then: function() {
| |
| if (dev["wb-adc"]["Vin"] < dev["wb-adc"]["BAT"] ) {
| |
| dev["power_status"]["Vin"] = 0;
| |
| } else {
| |
| dev["power_status"]["Vin"] = dev["wb-adc"]["Vin"] ;
| |
| }
| |
| } | | } |
| });
| |
|
| |
|
| |
|
| |
| <!--T:96-->
| |
| defineRule("_system_dc_on", {
| |
| asSoonAs: function () {
| |
| return dev["wb-adc"]["Vin"] > dev["wb-adc"]["BAT"];
| |
| },
| |
| then: function () {
| |
| dev["power_status"]["working on battery"] = false;
| |
| } | | } |
| }); | | }); |
|
| |
|
| <!--T:97-->
| | defineRule({ |
| defineRule("_system_dc_off", { | | whenChanged: "test_buzzer/enabled", |
| asSoonAs: function () {
| | then: function (newValue, devName, cellName) { |
| return dev["wb-adc"]["Vin"] <= dev["wb-adc"]["BAT"]; | | dev["buzzer"]["enabled"] = true; |
| },
| | setTimeout(function () { |
| then: function () {
| | dev["buzzer"]["enabled"] = false; |
| dev["power_status"]["working on battery"] = true;
| | }, 2000); |
| } | | } |
| }); | | }); |
|
| |
| <!--T:98-->
| |
| </syntaxhighlight> | | </syntaxhighlight> |
| | <!--T:59--> |
|
| |
|
| | === setInterval(); ===<!--T:60--> |
|
| |
|
|
| |
| === Отправка команд по RS-485 === <!--T:99-->
| |
|
| |
| <!--T:100-->
| |
| Для примера отправим команду устройству на порт ''/dev/ttyNSC0'' (соответствует аппаратному порту RS-485-ISO на [[Special:MyLanguage/Wiren Board 4|Wiren Board 4]]).
| |
| Для этого будем использовать движок правил и возможность выполнения произвольных shell-команд. Подробнее см. [https://github.com/contactless/wb-rules#%D0%94%D1%80%D1%83%D0%B3%D0%B8%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%91%D0%BD%D0%BD%D1%8B%D0%B5-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8-%D0%B8-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5 в документации].
| |
|
| |
| <!--T:101-->
| |
| С помощью движка правил создадим виртуальное устройство с контролом типа switch (переключатель).
| |
|
| |
| <!--T:102-->
| |
| При включении переключателя будем отправлять команду (уст. Яркость кан. 00=0xff) для Uniel UCH-M141:
| |
| <pre>
| |
| FF FF 0A 01 FF 00 00 0A
| |
| </pre>
| |
|
| |
|
| |
| <!--T:103-->
| |
| При выключении переключателя будем отправлять команду (уст. Яркость кан. 00=0x00) для Uniel UCH-M141:
| |
| <pre>
| |
| FF FF 0A 01 00 00 00 0B
| |
| </pre>
| |
|
| |
|
| |
|
| |
|
| |
|
| |
| <!--T:104-->
| |
| 1. Настройка порта
| |
|
| |
| <!--T:105-->
| |
| Для настройки порта /dev/ttyNSC0 на скорость 9600 надо выполнить следующую команду
| |
|
| |
| <!--T:106-->
| |
| <pre>
| |
| stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8
| |
| </pre>
| |
|
| |
| <!--T:107-->
| |
| 2. Отправка команды
| |
|
| |
| <!--T:108-->
| |
| Отправка данных делается следующей шелл-командой:
| |
|
| |
| <!--T:109-->
| |
| <pre>
| |
| /usr/bin/printf '\xFF\xFF\x0A\x01\xD1\x06\x00\xE2' >/dev/ttyNSC0
| |
| </pre>
| |
| где "\xFF\xFF\x0A\x01\xD1\x06\x00\xE2" - это запись команды "FF FF 0A 01 D1 06 00 E2".
| |
|
| |
|
| |
| <!--T:110-->
| |
| 3. Создадим в движке правил новый файл с правилами <code>/etc/wb-rules/rs485_cmd.js</code>
| |
|
| |
| <!--T:111-->
| |
| Файл можно редактировать с помощью vim, nano или mcedit в сеансе ssh на устройстве, либо залить его с помощью SCP.
| |
|
| |
| <!--T:112-->
| |
| <pre>
| |
| root@wirenboard:~# mcedit /etc/wb-rules/rs485_cmd.js
| |
| </pre>
| |
|
| |
|
| |
| <!--T:113-->
| |
| 4. Описываем в файле виртуальный девайс
| |
|
| |
| <!--T:114-->
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| defineVirtualDevice("rs485_cmd", { | | defineVirtualDevice("test_buzzer", { |
| title: "Send custom command to RS-485 port",
| | title: "Test Buzzer", |
| cells: {
| | cells: { |
| enabled: {
| | enabled: { |
| type: "switch",
| | type: "pushbutton", |
| value: false
| | value: false |
| },
| |
| } | | } |
| | } |
| }); | | }); |
| </syntaxhighlight>
| |
|
| |
|
|
| |
|
| <!--T:115-->
| | var test_interval = null; |
| 5. Перезапускаем wb-rules и проверяем работу
| |
|
| |
|
| <!--T:116-->
| | defineRule({ |
| <pre>
| | whenChanged: "test_buzzer/enabled", |
| root@wirenboard:~# /etc/init.d/wb-rules restart
| | then: function (newValue, devName, cellName) { |
| root@wirenboard:~# tail -f /var/log/messages
| | var n = 0; |
| </pre>
| | if (dev["test_buzzer"]["enabled"]){ |
| | | test_interval = setInterval(function () { |
| <!--T:117-->
| | dev["buzzer"]["enabled"] = !dev["buzzer"]["enabled"]; |
| В логе не должно быть сообщений об ошибке (выход через control-c)
| | n = n+1; |
| | | if (n >= 10){ |
| | | clearInterval(test_interval); |
| <!--T:118-->
| | } |
| В веб-интерфейсе в разделе Devices должно появиться новое устройство "Send custom command to RS-485 port".
| | }, 500); |
| | | } |
| | | } |
| <!--T:119-->
| | }); |
| 6. Добавим функцию для конфигурирования порта.
| |
| | |
| | |
| <!--T:120-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| function setup_port() {
| |
| runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8");
| |
| } | |
| | |
| <!--T:121-->
| |
| </syntaxhighlight> | | </syntaxhighlight> |
| | <!--T:61--> |
|
| |
|
| | | === startTimer(); === |
| <!--T:122--> | | Запускает однократный таймер. При срабатывании таймера происходит просмотр правил, при этом timers.<name>.firing для этого таймера становится истинным на время этого просмотра. |
| 7. Опишем правила на включение и выключение переключателя
| | <!--T:63--> |
| | |
| <!--T:123--> | |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| defineRule("_rs485_switch_on", {
| | defineVirtualDevice("test_buzzer", { |
| asSoonAs: function () { | | title: "Test Buzzer", |
| return dev.rs485_cmd.enabled; | | cells: { |
| },
| | enabled: { |
| then: function() {
| | type: "switch", |
| runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\xff\\x00\\x00\\x0a' > /dev/ttyNSC0"); | | value: false |
| | } |
| } | | } |
| }); | | }); |
|
| |
|
| <!--T:124-->
| | defineRule("1",{ |
| defineRule("_rs485_switch_off", { | |
| asSoonAs: function () { | | asSoonAs: function () { |
| return !dev.rs485_cmd.enabled; | | return dev["test_buzzer"]["enabled"] ; |
| }, | | }, |
| then: function() { | | then: function () { |
| runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\x00\\x00\\x00\\x0b' >/dev/ttyNSC0"); | | startTimer("one_second", 1000); |
| | dev["buzzer"]["enabled"] = true;//включаем пищалку |
| } | | } |
| }); | | }); |
|
| |
|
| <!--T:125-->
| | defineRule("2",{ |
| | when: function () { return timers.one_second.firing; }, |
| | then: function () { |
| | dev["buzzer"]["enabled"] = false;//выключаем пищалку |
| | dev["test_buzzer"]["enabled"] = false; |
| | } |
| | }); |
| </syntaxhighlight> | | </syntaxhighlight> |
| | <!--T:64--> |
|
| |
|
| | | === startTicker(); === |
| <!--T:126-->
| | Запускает периодический таймер с указанным интервалом. В примере правило 1 запускает таймер с интервалом 1 сек. Вызывая срабатывание правила 2. Метод stop() приводит к остановке таймера. |
| Обратите внимание на двойное экранирование.
| | <!--T:66--> |
| | |
| | |
| | |
| | |
| <!--T:127-->
| |
| 7. Собираем всё вместе
| |
| | |
| <!--T:128-->
| |
| Полное содержимое файла с правилами:
| |
| | |
| <!--T:129--> | |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| defineVirtualDevice("rs485_cmd", { | | defineVirtualDevice("test_buzzer", { |
| title: "Send custom command to RS-485 port",
| | title: "Test Buzzer", |
| cells: {
| | cells: { |
| enabled: {
| | enabled: { |
| type: "switch",
| | type: "switch", |
| value: false
| | value: false |
| },
| |
| } | | } |
| | } |
| }); | | }); |
|
| |
|
| | | defineRule("1",{ |
| <!--T:130-->
| |
| function setup_port() {
| |
| runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8");
| |
| }
| |
| | |
| <!--T:131-->
| |
| defineRule("_rs485_switch_on", { | |
| asSoonAs: function () { | | asSoonAs: function () { |
| return dev.rs485_cmd.enabled; | | return dev["test_buzzer"]["enabled"] ; |
| }, | | }, |
| then: function() { | | then: function () { |
| runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\xff\\x00\\x00\\x0a' > /dev/ttyNSC0"); | | startTicker("one_second", 1000); |
| } | | } |
| }); | | }); |
| | | defineRule("2",{ |
| <!--T:132-->
| | when: function () { return timers.one_second.firing; }, |
| defineRule("_rs485_switch_off", { | | then: function () { |
| asSoonAs: function () { | | dev["buzzer"]["enabled"] = !dev["buzzer"]["enabled"]; |
| return !dev.rs485_cmd.enabled;
| | if (dev["test_buzzer"]["enabled"] == false){ |
| },
| | timers.one_second.stop(); |
| then: function() { | | } |
| runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\x00\\x00\\x00\\x0b' >/dev/ttyNSC0"); | |
| } | | } |
| }); | | }); |
|
| |
| <!--T:133-->
| |
| setTimeout(setup_port, 1000); // запланировать выполнение setup_port() через 1 секунду после старта правил.
| |
|
| |
| <!--T:134-->
| |
| </syntaxhighlight> | | </syntaxhighlight> |
| | <!--T:67--> |
|
| |
|
| | | == Сообщения в лог == |
| | | В зависимости от функции сообщение классифицируется как отладочное (debug), информационное (info), предупреждение (warning) или сообщение об ошибке (error). |
| === Пользовательские поля в интерфейсе === <!--T:135--> | | <!--T:69--> |
| | |
| <!--T:136-->
| |
| Чтобы дать пользователю возможность вводить точные значения параметров (уставки) из интерфейса, можно воспользоваться [https://wirenboard.com/wiki/index.php/%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D1%83%D0%B5%D0%BC%D1%8B%D1%85_%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D0%BA%D0%B8%D1%85_%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9_%D0%B2_%D0%B2%D0%B5%D0%B1-%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81%D0%B5 инструкцией].
| |
| | |
| <!--T:137--> | |
| Более подробно и с примером - в [https://support.wirenboard.com/t/kak-na-wb5-wb6-sozdat-pole-dlya-vvoda-ustavok-i-peredat-znachenie-v-pravila/2180 теме на портале техподдержки].
| |
| | |
| | |
| === Импульсные счетчики ===
| |
| | |
| Импульсный счетчик подключен к WB-MCM8. Выдает 1 импульс на 10 литров воды. При подключении на счетчике были показания 123.120 м³, что равно 123120 литрам воды. У WB-MCM8 при подключении было насчитано 7 импульсов.
| |
| | |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| var meterCorrection = 123120 // Корректировочное значение счетчика в литрах
| | defineVirtualDevice("test_buzzer", { |
| var counterCorrection = 7 // Корректировочное значение WB-MCM8 в импульсах
| | title: "Test Buzzer", |
| var inpulseValue = 10 // Количество литров на один импульс
| | cells: { |
| | | enabled: { |
| defineVirtualDevice("water_meters", { // Создаем виртуальный девайс для отображения в веб интерфейсе. | | type: "pushbutton", |
| title: "Счетчики воды",
| | value: false |
| cells: {
| |
| water_meter_1: {
| |
| type: "value",
| |
| value: 0
| |
| },
| |
| } | | } |
| | } |
| }); | | }); |
|
| |
|
| defineRule("water_meter_1", { | | defineRule({ |
| whenChanged: "wb-mcm8_29/Input 1 counter",
| | whenChanged: "test_buzzer/enabled", |
| then: function(newValue, devName, cellName) {
| | then: function (newValue, devName, cellName) { |
| if(newValue){
| | log.info('info'); |
| dev["water_meters"]["water_meter_1"] = ((parseInt(newValue) - counterCorrection) * inpulseValue) + meterCorrection; // Умножаем значение счетчика на количество литров/импульс и прибавляем корректировочное значение.
| | log.debug('debug'); |
| }
| | log.error('error'); |
| }
| | log.warning('warning'); |
| | log('log!'); //сокращение для log.info(); |
| | debug('deb!'); //сокращение для log.debug(); |
| | } |
| }); | | }); |
| </syntaxhighlight> | | </syntaxhighlight> |
| | <!--T:70--> |
| | == Выполнение произвольной команды runShellCommand(); == |
|
| |
|
| === Сложные правила с расписаниями === <!--T:138-->
| |
|
| |
| <!--T:139-->
| |
| Объект - продуктовый магазин. Различные системы магазина управляются по обратной связи от датчиков температуры и с учётом расписания работы магазина.
| |
|
| |
| <!--T:140-->
| |
| Для расписаний используются не cron-правила, а обёртка над ними. Обёртка включает и выключает правила, которые, в отличие от cron-правил, выполняются постоянно, будучи включенными.
| |
|
| |
| <!--T:141-->
| |
| Например, мы хотим, чтобы освещение было включено с 10 до 17ч. Обёртка (libschedule) будет выполнять правило "включить освещение" раз в минуту с 10 утра до 17 вечера.
| |
|
| |
| <!--T:142-->
| |
| Это значит, что даже если контроллер работает с перерывами и пропустил время перехода между расписаниями (10 утра), то контроллер всё равно включит освещение при первой возможности.
| |
|
| |
|
| |
| <!--T:143-->
| |
| lib_schedules.js:
| |
|
| |
| <!--T:144-->
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| global.__proto__.Schedules = {};
| | defineVirtualDevice("test_button", { |
| | | title: "Test Button", |
| <!--T:145-->
| | cells: { |
| (function(Schedules) { // замыкание | | enabled: { |
| | | type: "pushbutton", |
| <!--T:146--> | | value: false |
| function todayAt(now, hours, minutes) {
| |
| var date = new Date(now);
| |
| // i.e. "today, at HH:MM". All dates are in UTC!
| |
| date.setHours(hours);
| |
| date.setMinutes(minutes);
| |
| return date;
| |
| } | |
| | |
| | |
| <!--T:147-->
| |
| function checkScheduleInterval(now, start_time, end_time) {
| |
| var start_date = todayAt(now, start_time[0], start_time[1]);
| |
| var end_date = todayAt(now, end_time[0], end_time[1]);
| |
| log("checkScheduleInterval {} {} {}".format(now, start_date, end_date));
| |
| | |
| <!--T:148--> | |
| if (end_date >= start_date) {
| |
| if ((now >= start_date) && (now < end_date)) {
| |
| return true;
| |
| }
| |
| } else {
| |
| // end date is less than start date, | |
| // assuming they belong to a different days (e.g. today and tomorrow)
| |
| | |
| <!--T:149-->
| |
| // option 1: what if it's now the day of "end" date?
| |
| // in this case the following is enough:
| |
| if (now < end_date) {
| |
| return true;
| |
| }
| |
| | |
| <!--T:150-->
| |
| // well, that seems not to be the case. ok,
| |
| // option 2: it's the day of "start" date: | |
| | |
| <!--T:151-->
| |
| if (now >= start_date) {
| |
| return true;
| |
| }
| |
| }
| |
| return false;
| |
| | |
| <!--T:152-->
| |
| }
| |
| | |
| <!--T:153-->
| |
| function checkSchedule(schedule, now) {
| |
| if (now == undefined) {
| |
| now = new Date();
| |
| }
| |
| | |
| <!--T:154-->
| |
| for (var i = 0; i < schedule.intervals.length; ++i) {
| |
| var item = schedule.intervals[i];
| |
| if (checkScheduleInterval(now, item[0], item[1])) {
| |
| log("found matching schedule interval at {}".format(item));
| |
| return true;
| |
| }
| |
| } | | } |
| return false;
| |
| } | | } |
|
| | }); |
| function updateSingleScheduleDevStatus(schedule) {
| |
| log("updateSingleScheduleDevStatus {}".format(schedule.name));
| |
| dev["_schedules"][schedule.name] = checkSchedule(schedule);
| |
| };
| |
|
| |
|
| <!--T:155--> | | defineRule({ |
| function addScheduleDevCronTasks(schedule) { | | whenChanged: "test_button/enabled", |
| for (var i = 0; i < schedule.intervals.length; ++i) { | | then: function (newValue, devName, cellName) { |
| var interval = schedule.intervals[i];
| | runShellCommand("mosquitto_pub -t '/devices/test_button/controls/enabled/meta/readonly' -r -m 1"); |
| for (var j = 0; j < 2; ++j) { // either start or end of the interval
| | setTimeout(function () { |
| var hours = interval[j][0];
| | runShellCommand("mosquitto_pub -t '/devices/test_button/controls/enabled/meta/readonly' -r -m 0"); |
| var minutes = interval[j][1];
| | }, 5000); |
| log("cron at " + "0 " + minutes + " " + hours + " * * *");
| |
| defineRule("_schedule_dev_{}_{}_{}".format(schedule.name, i, j), {
| |
| when: cron("0 " + minutes + " " + hours + " * * *"),
| |
| then: function () {
| |
| log("_schedule_dev_ {}_{}_{}".format(schedule.name, i, j));
| |
| updateSingleScheduleDevStatus(schedule);
| |
| }
| |
| });
| |
| }
| |
| }
| |
| } | | } |
| | }); |
| | </syntaxhighlight> |
|
| |
|
| <!--T:156-->
| |
| function addScheduleAutoUpdCronTask(schedule) {
| |
| defineRule("_schedule_auto_upd_{}".format(schedule.name), {
| |
| when: cron("@every " + schedule.autoUpdate),
| |
| then: function() {
| |
| dev._schedules[schedule.name] = dev._schedules[schedule.name];
| |
| }
| |
| });
| |
| }
| |
|
| |
|
| <!--T:157-->
| |
| var _schedules = {};
| |
|
| |
|
| <!--T:158-->
| | == Пишем сложные правила == <!--T:189--> |
| Schedules.registerSchedule = function(schedule) {
| |
| _schedules[schedule.name] = schedule;
| |
| };
| |
|
| |
|
| <!--T:159-->
| | <!--T:190--> |
| Schedules.initSchedules = function() {
| | Чтобы начать писать сложные правила, нужно посмотреть примеры правил и полную документацию по движку правил: |
| var params = {
| | #Примеры правил: |
| title: "Schedule Status",
| | #* на этой же странице ниже; |
| cells: {}
| | #* в [http://forums.contactless.ru/t/dvizhok-pravil-primery-koda/483 специальной теме на нашем форуме ]. |
| };
| | #[https://github.com/contactless/wb-rules Полное описание движка правил]. |
|
| |
|
| <!--T:160-->
| | == Совместимость скриптов при обновлении wb-rules == |
| for (var schedule_name in _schedules) {
| |
| if (_schedules.hasOwnProperty(schedule_name)) {
| |
| var schedule = _schedules[schedule_name];
| |
| params.cells[schedule_name] = {type: "switch", value: false, readonly: true};
| |
| }
| |
| };
| |
|
| |
|
| <!--T:161-->
| | * [[Special:MyLanguage/Движок_правил_wb-rules_1.7|Движок правил wb-rules 1.7]] |
| defineVirtualDevice("_schedules", params);
| | * [[Special:MyLanguage/Движок_правил_wb-rules_2.0|Движок правил wb-rules 2.0]] |
|
| |
|
| | Предполагается, что при обновлении с предыдущей на следующую версию wb-rules и при соблюдении гайдлайнов при написании скриптов - все сценарии продолжат работать без каких-либо изменений. |
| | Далее перечислены возможные проблемы в связи с изменением логики обработки скриптов новыми версиями движка. |
|
| |
|
| | === Обновление до версии 2.2 === |
|
| |
|
| <!--T:162-->
| | С версии 2.2 стали более строго проверяться типы устанавливаемых значений для контролов: |
| for (var schedule_name in _schedules) {
| |
| if (_schedules.hasOwnProperty(schedule_name)) {
| |
| var schedule = _schedules[schedule_name];
| |
|
| |
|
| <!--T:163-->
| | если для установления признака включено/выключено для контролов типа switch, клики по pushbutton или присваивание значений контролам с типом text применялась недокументировання возможность |
| // setup cron tasks which updates the schedule dev status at schedule
| | использования для этой цели числовые значения (1, 0 и т.д.) в версии движка 2.2 операция присваивания не выполнится и завершится с ошибкой. |
| // interval beginings and ends
| | Корректный способ — устанавливать булевы значений (true/false) для switch/pushbutton и строковые значения для типа text |
| addScheduleDevCronTasks(schedule);
| |
|
| |
|
| <!--T:164-->
| | При возникновении подобной проблемы в логах можно видеть подобные записи: |
| // if needed, setup periodic task to trigger rules which use this schedule | | <pre> |
| if (schedule.autoUpdate) {
| | ERROR: control wb-mr3_30/K1 SetValue() error: can't convert control value '1' (type float64) to datatype 'switch' |
| addScheduleAutoUpdCronTask(schedule);
| | 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' |
| | </pre> |
|
| |
|
| <!--T:165-->
| | Вместо: |
| // set schedule dev status as soon as possible at startup | | <syntaxhighlight lang="ecmascript"> |
| (function(schedule) {
| | dev["wb-mr3_30"]["K1"] = 1 // включение |
| setTimeout(function() {
| | dev["status"]["someStatus"] = -1.47 // float |
| updateSingleScheduleDevStatus(schedule);
| |
| }, 1);
| |
| })(schedule);
| |
| | |
| <!--T:166-->
| |
| };
| |
| };
| |
| | |
|
| |
| <!--T:167-->
| |
| };
| |
| | |
| <!--T:168-->
| |
| })(Schedules);
| |
| </syntaxhighlight> | | </syntaxhighlight> |
|
| |
|
| <!--T:169-->
| | Нужно: |
| Пример правил, с использованием Schedules:
| |
| <syntaxhighlight lang="ecmascript"> | | <syntaxhighlight lang="ecmascript"> |
| (function() { // замыкание | | dev["wb-mr3_30"]["K1"] = true // включение |
| | dev["status"]["someStatus"] = (-1.47).toString() // text |
| | </syntaxhighlight> |
|
| |
|
| <!--T:170-->
| | === Обновление до версии 1.7 === |
| defineAlias("countersTemperature", "wb-msw2_30/Temperature");
| |
| defineAlias("vegetablesTemperature", "wb-msw2_31/Temperature");
| |
|
| |
|
| <!--T:171-->
| | Начиная с версии wb-rules 1.7, локальные переменные и функции, объявленные в файле сценария не видны в других сценариях. |
| defineAlias("heater1EnableInverted", "wb-mrm2-old_70/Relay 1");
| | Таким образом, каждый сценарий может определять свои функции и переменные без риска изменить поведение других сценариев. |
| defineAlias("frontshopVentInverted", "wb-gpio/EXT1_R3A3");
| |
|
| |
|
| <!--T:172-->
| | В качестве примера приведём два сценария, одновременно запускаемых в движке правил. Каждый сценарий определяет переменные и функции. |
| Schedules.registerSchedule({
| | В предыдущих версиях wb-rules обращение к переменной, изменяемой в нескольких файлах сценариев, может привести к неопределённому поведению. |
| "name" : "signboard", // вывеска
| | В версиях, начиная с 1.7, поведение строго определено и такое же, как будто сценарий единственный в системе. |
| "autoUpdate" : "1m",
| |
| "intervals" : [
| |
| [ [12, 30], [20, 30] ], // в UTC, 15:30 - 23:30 MSK
| |
| [ [3, 30], [5, 20] ], // в UTC, 6:30 - 8:20 MSK
| |
| ]
| |
| });
| |
| Schedules.registerSchedule({
| |
| "name" : "ext_working_hours_15m",
| |
| "autoUpdate" : "1m",
| |
| "intervals" : [
| |
| [ [4, 45], [20, 15] ], // всё ещё UTC, 7:45 - 23:15 MSK
| |
| ]
| |
| });
| |
| Schedules.registerSchedule({
| |
| "name" : "working_hours",
| |
| "autoUpdate" : "1m",
| |
| "intervals" : [
| |
| [ [5, 0], [19, 0] ], // всё ещё UTC, 8:00 - 22:00 MSK
| |
| ]
| |
| });
| |
| Schedules.registerSchedule({
| |
| "name" : "working_hours_15m",
| |
| "autoUpdate" : "1m",
| |
| "intervals" : [
| |
| [ [4, 45], [19, 15] ], // всё ещё UTC, 7:45 - 22:15 MSK
| |
| ]
| |
| });
| |
| Schedules.registerSchedule({
| |
| "name" : "frontshop_lighting",
| |
| "autoUpdate" : "1m",
| |
| "intervals" : [
| |
| [ [4, 20], [20, 45] ], // всё ещё UTC, 7:20 -23:45 MSK
| |
| ]
| |
| });
| |
| Schedules.registerSchedule({
| |
| "name" : "heaters_schedule",
| |
| "intervals" : [
| |
| [ [4, 0], [17, 0] ], // всё ещё UTC, 07:00 - 20:00 MSK дневной режим
| |
| ]
| |
| });
| |
| Schedules.initSchedules();
| |
|
| |
|
| <!--T:173-->
| | Сценарий 1 (rules1.js): |
| // Вывеска и фасадное освещение
| | <syntaxhighlight lang="ecmascript"> |
| defineRule("signboardOnOff", {
| | var test1 = 42; |
| when: function() {
| |
| return dev._schedules.signboard || true;
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("signboardOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| var on = dev._schedules.signboard; //
| |
|
| |
| dev["wb-mr6c_80/K2"] = !on;
| |
| dev["wb-mr6c_80/K1"] = !on;
| |
| dev["wb-mr6c_80/K3"] = !on;
| |
| }
| |
| });
| |
|
| |
|
| | setInterval(function myFuncOne() { |
| | log("myFuncOne called"); |
| | log("test1: {}", test1); |
| | log("test2: {}", test2); |
| | }, 5000); |
| | </syntaxhighlight> |
|
| |
|
| <!--T:174-->
| | Сценарий 2 (rules2.js): |
| // Освещение торгового зала
| | <syntaxhighlight lang="ecmascript"> |
| defineRule("lightingFrontshopOnOff", {
| | var test1 = 84; |
| when: function() {
| | var test2 = "Hello"; |
| return dev._schedules.frontshop_lighting || true;
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("lightingFrontshopOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| dev["wb-gpio/EXT1_R3A1"] = ! dev._schedules.frontshop_lighting; //инвертированный контактор
| |
| }
| |
| });
| |
|
| |
|
| | setInterval(function myFuncTwo() { |
| | log("myFuncTwo called"); |
| | log("test1: {}, test2: {}", test1, test2); |
| | }, 5000); |
| | </syntaxhighlight> |
|
| |
|
| <!--T:175-->
| | Было: |
| // Вентиляция подсобного помещения
| | <pre> |
| defineRule("ventBackstoreOnOff", {
| | 2019-01-05 17:29:50 myFuncTwo called |
| when: function() {
| | 2019-01-05 17:29:50 test1: 84, test2: Hello |
| return dev._schedules.ext_working_hours_15m || true;
| | 2019-01-05 17:29:50 myFuncOne called |
| },
| | 2019-01-05 17:29:50 test1: 84 |
| then: function (newValue, devName, cellName) {
| | 2019-01-05 17:29:50 test2: Hello |
| log("ventBackstoreOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| | </pre> |
| var on = dev._schedules.ext_working_hours_15m;
| |
| dev["wb-mr6c_81/K1"] = ! on; //инвертированный контактор
| |
| dev["wb-mr6c_81/K5"] = ! on; //инвертированный контактор
| |
| }
| |
| });
| |
|
| |
|
| <!--T:176-->
| | Стало: |
| // Освещение холодильных горок
| | <pre> |
| defineRule("lightingCoolingshelfsOnOff", {
| | 2019-01-05 17:28:42 myFuncTwo called |
| when: function() {
| | 2019-01-05 17:28:42 test1: 84, test2: Hello |
| return dev._schedules.frontshop_lighting || true;
| | 2019-01-05 17:28:42 myFuncOne called |
| },
| | 2019-01-05 17:28:42 test1: 42 |
| then: function (newValue, devName, cellName) {
| | 2019-01-05 17:28:42 ECMAScript error: ReferenceError: identifier 'test2' undefined |
| log("lightingCoolingshelfsOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| | duk_js_var.c:1232 |
| var on = dev._schedules.working_hours_15m;
| | myFuncOne /etc/wb-rules/rules1.js:6 preventsyield |
| | </pre> |
|
| |
|
| <!--T:177-->
| | Возможные способы решения проблемы: |
| // освещение в горках через нормально-закрытые реле (инвертировано)
| |
| dev["wb-mrm2-old_60/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_61/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_62/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_63/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_64/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_65/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_66/Relay 1"] = !on;
| |
| dev["wb-mrm2-old_67/Relay 1"] = !on;
| |
| }
| |
| });
| |
|
| |
|
| | '''Способ 1''' |
|
| |
|
| <!--T:178-->
| | Самый простой способ: перенести всю логику, которая использует доступ к переменной в другм файле в тот же файл, где определена переменная. Т.е. на примере приведённых выше правил нужно перенести всё в один файл rules1.js |
| //Брендовые холодильники (пиво, лимонады)
| | <syntaxhighlight lang="ecmascript"> |
| defineRule("powerBrandFridgesOnOff", {
| | var test1 = 42; |
| when: function() {
| | var test2 = "Hello"; |
| return dev._schedules.working_hours || true;
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("powerBrandFridgesOnOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| var on = dev._schedules.working_hours;
| |
|
| |
| dev["wb-gpio/EXT1_R3A5"] = !on; // инвертировано
| |
| }
| |
| });
| |
|
| |
|
| | setInterval(function myFuncTwo() { |
| | log("myFuncTwo called"); |
| | log("test1: {}, test2: {}", test1, test2); |
| | }, 5000); |
|
| |
|
| <!--T:179-->
| | setInterval(function myFuncOne() { |
| // ========= Котлы и приточная вентиляция ТЗ ===========
| | log("myFuncOne called"); |
| // обратная связь по температуре овощной зоны
| | log("test1: {}", test1); |
| | log("test2: {}", test2); |
| | }, 5000); |
| | </syntaxhighlight> |
|
| |
|
| <!--T:180-->
| | '''Способ 2''' |
| // днём работает позиционный регулятор
| |
| defineRule("heatersDayOff", {
| |
| when: function() {
| |
| return (dev._schedules.heaters_schedule) && (vegetablesTemperature > 17.0);
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("heatersDayOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| heater1EnableInverted = !false; // инвертировано
| |
| }
| |
| });
| |
|
| |
|
| <!--T:181-->
| | Следующий способ заключается в использовании глобального постоянного хранилища (PersistentStorage) ==== |
| defineRule("heatersDayOn", {
| |
| when: function() {
| |
| return (dev._schedules.heaters_schedule) && (vegetablesTemperature < 16.7);
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("heatersDayOn newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| heater1EnableInverted = !true; // инвертировано
| |
| }
| |
| });
| |
|
| |
|
| <!--T:182-->
| | '''Внимание:''' при использовании глобальных постоянных хранилищ может произойти совпадение имён, в этом случае возможно труднообнаруживаемое нарушение поведения. |
| // ночью работает позиционный регулятор
| |
| defineRule("heatersNightOff", {
| |
| when: function() {
| |
| return (!dev._schedules.heaters_schedule) && (vegetablesTemperature > 11.6);
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("heatersNightOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| heater1EnableInverted = !false; // инвертировано
| |
| }
| |
| });
| |
|
| |
|
| <!--T:183-->
| | Вышеприведённый пример можно исправить следующим образом: |
| defineRule("heatersNightOn", {
| |
| when: function() {
| |
| return (!dev._schedules.heaters_schedule) && (vegetablesTemperature < 11.3);
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("heatersNightOn newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| heater1EnableInverted = !true; // инвертировано
| |
| }
| |
| });
| |
|
| |
|
| | Сценарий 1 (rules1.js): |
| | <syntaxhighlight lang="ecmascript"> |
| | var test1 = 42; |
|
| |
|
| <!--T:184-->
| | var ps = new PersistentStorage("my-global-storage", {global: true}); |
| // приточная и вытяжная вентиляция принудительно выключены
| |
|
| |
| defineRule("ventFrontshopAlwaysOff", {
| |
| when: cron("@every 1m"),
| |
| then: function() {
| |
| dev["wb-gpio/EXT1_R3A3"] = !false;
| |
| dev["wb-gpio/EXT1_R3A4"] = !false;
| |
| }
| |
| });
| |
|
| |
|
| |
| // ================== Кассовая зона =================
| |
|
| |
|
| <!--T:185-->
| | ps.test1 = test1; |
| // в кассовой зоне в рабочее время температура поддерживается кондиционерами (позиционный регулятор)
| |
|
| |
|
| <!--T:186-->
| | setInterval(function myFuncOne() { |
| defineRule("countersACOn", {
| | log("myFuncOne called"); |
| when: function() {
| | log("test1: {}", test1); |
| return (dev._schedules.working_hours_15m) && (countersTemperature < 17.7);
| | log("test2: {}", ps.test2); |
| }, | | }, 5000); |
| then: function (newValue, devName, cellName) { | | </syntaxhighlight> |
| log("countersACOn newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| dev["wb-mir_75/Play from ROM7"] = true; // кондиционер кассовой зоны на нагрев
| |
| }
| |
| });
| |
|
| |
|
| <!--T:187-->
| | Сценарий 2 (rules2.js): |
| // в нерабочее время кондиционер выключен
| | <syntaxhighlight lang="ecmascript"> |
| defineRule("countersACOff", {
| | var test2 = "Hello"; |
| when: function() {
| |
| return (!dev._schedules.working_hours_15m) || (countersTemperature > 18.0);
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("countersACOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| dev["wb-mir_75/Play from ROM2"] = true; // кондиционер кассовой зоны выключить
| |
| }
| |
| });
| |
|
| |
|
| <!--T:188-->
| | var ps = new PersistentStorage("my-global-storage", {global: true}); |
| // =============== Овощная зона ==============
| |
| // Охлаждение овощей кондиционером только при температуре воздуха выше 18.5C
| |
|
| |
|
| <!--T:189-->
| | ps.test2 = test2; |
| defineRule("acVegOn", {
| |
| when: function() {
| |
| return vegetablesTemperature >= 18.5
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| log("acVegOn newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| dev["wb-mir_76/Play from ROM3"] = true; // Охлаждение +18
| |
| }
| |
| });
| |
|
| |
|
| <!--T:190-->
| | setInterval(function myFuncTwo() { |
| defineRule("acVegOff", {
| | log("myFuncTwo called"); |
| when: function() {
| | log("test1: {}, test2: {}", ps.test1, test2); |
| return vegetablesTemperature < 17.8
| | }, 5000); |
| }, | |
| then: function (newValue, devName, cellName) {
| |
| log("acVegOff newValue={}, devName={}, cellName={}", newValue, devName, cellName);
| |
| dev["wb-mir_76/Play from ROM2"] = true; // выключить
| |
| }
| |
| });
| |
| })()
| |
| </syntaxhighlight> | | </syntaxhighlight> |
|
| |
|
|
| |
|
| | '''Способ 3''' |
|
| |
|
| == Полное описание возможностей движка правил == <!--T:191-->
| | "Грязный" способ, который использует прототип глобального объекта - использовать этот способ не рекомендуется из-за возможного замусоривания глобального пространства пользовательскими данными. |
|
| |
|
| <!--T:192-->
| | Исправленная версия: |
| Самое полное описание движка правил: https://github.com/contactless/wb-rules/blob/master/README.md
| |
|
| |
|
| | Сценарий 1 (rules1.js): |
| | <syntaxhighlight lang="ecmascript"> |
| | global.__proto__.test1 = 42; |
|
| |
|
| | setInterval(function myFuncOne() { |
| | log("myFuncOne called"); |
| | log("test1: {}", global.__proto__.test1); |
| | log("test2: {}", global.__proto__.test2); |
| | }, 5000); |
| | </syntaxhighlight> |
|
| |
|
| == Новые возможности последних версий == <!--T:193--> | | Сценарий 2 (rules2.js): |
| | <syntaxhighlight lang="ecmascript"> |
| | global.__proto__.test2 = "Hello"; |
|
| |
|
| <!--T:194-->
| | setInterval(function myFuncTwo() { |
| * [[Special:MyLanguage/Движок_правил_wb-rules_1.7|Движок правил wb-rules 1.7]]
| | log("myFuncTwo called"); |
| | log("test1: {}, test2: {}", global.__proto__.test1, global.__proto__.test2); |
| | }, 5000); |
| | </syntaxhighlight> |
|
| |
|
|
| |
|
| |
| == В разработке == <!--T:195-->
| |
|
| |
| <!--T:196-->
| |
| Описание возможностей будущих версий движка правил можно прочесть здесь:
| |
| * [[Special:MyLanguage/Движок_правил_wb-rules_2.0|Движок правил wb-rules 2.0]]
| |
|
| |
|
| </translate> | | </translate> |