|
Метка: новое перенаправление |
(не показано 6 промежуточных версий 2 участников) |
Строка 1: |
Строка 1: |
| <languages/>
| | #REDIRECT [[wb-rules]] |
| <translate>
| |
| {{DISPLAYTITLE: Как писать правила}}
| |
| | |
| == Типы правил defineRule ==
| |
| | |
| === whenChanged ===
| |
| Правило срабатывает при любых изменениях значений параметров или функций
| |
| В примере кнопка подключена ко входу А1 контроллера. При нажатии на кнопку срабатывает реле 1 устройства MRM2-mini, при отжатии реле возвращается в исходное состояние.
| |
| <!--T:40-->
| |
| | |
| <syntaxhighlight lang="ecmascript">
| |
| defineRule("test_rule", { //имя правила test_rule
| |
| whenChanged: "wb-gpio/A1_IN",
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-mrm2-mini_2"]["Relay 1"] = newValue;
| |
| }
| |
| });
| |
| | |
| </syntaxhighlight>
| |
| <!--T:41-->
| |
| | |
| === asSoonAs ===
| |
| Правила, задаваемые при помощи asSoonAs, срабатывают в случае, когда значение, возвращаемое функцией, заданной в asSoonAs, становится истинным при том, что при предыдущем просмотре данного правила оно было ложным.
| |
| <!--T:43-->
| |
| <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;
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| | |
| При нажатии на кнопку, подключенную к входу А2, включаем реле, а при нажатии на кнопку, подключенную к входу А3 — выключаем.
| |
| | |
| === when ===
| |
| | |
| Правила, задаваемые при помощи when срабатывают при каждом просмотре, при котором функция, заданная в when, возвращает истинное значение. При срабатывании правила выполняется функция, заданная в свойстве when.
| |
| | |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_button2", {
| |
| title: "test_relay2",
| |
| cells: {
| |
| "switch_on": {
| |
| type: "pushbutton",
| |
| value: false,
| |
| },
| |
| "switch_off": {
| |
| type: "pushbutton",
| |
| value: false,
| |
| }
| |
| }
| |
| });
| |
| | |
| defineRule({
| |
| when: function() {
| |
| return dev["test_button2"]["switch_on"];
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-mrm2-mini_2"]["Relay 2"] = true;
| |
| }
| |
| });
| |
| | |
| defineRule({
| |
| when: function() {
| |
| return dev["test_button2"]["switch_off"];
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-mrm2-mini_2"]["Relay 2"] = false;
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| | |
| При нажатии на кнопку switch_on, включаем реле, а при нажатии на кнопку,switch_off — выключаем.
| |
| <!--T:49-->
| |
| | |
| === cron-правила ===
| |
| | |
| Отдельный тип правил - cron-правила. Такие правила задаются следующим образом:
| |
| | |
| <syntaxhighlight lang="ecmascript">
| |
| defineRule("crontest_hourly", {
| |
| when: cron("@hourly"),
| |
| then: function () {
| |
| log("@hourly rule fired");
| |
| }
| |
| })
| |
| </syntaxhighlight>
| |
| | |
| Вместо @hourly здесь можно задать любое выражение, допустимое в стандартном crontab, например, 00 00 20 * * (секунды минуты часы выполнять правило каждый день в 20:00). Помимо стандартных выражений допускается использование ряда расширений, см. описание формата выражений используемой cron-библиотеки.
| |
| | |
| == Таймеры ==
| |
| | |
| === setTimeout(); ===
| |
| | |
| С помощью данного таймера можно отсрочить выполнение правила на определенное время. В примере после нажатия кнопки включается пищалка и затем выключается спустя 2 секунды.
| |
| | |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_buzzer", {
| |
| title: "Test Buzzer",
| |
| cells: {
| |
| enabled: {
| |
| type: "pushbutton",
| |
| value: false
| |
| }
| |
| }
| |
| });
| |
| | |
| defineRule({
| |
| whenChanged: "test_buzzer/enabled",
| |
| then: function (newValue, devName, cellName) {
| |
| dev["buzzer"]["enabled"] = true;
| |
| setTimeout(function () {
| |
| dev["buzzer"]["enabled"] = false;
| |
| }, 2000);
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| <!--T:59-->
| |
| | |
| === setInterval(); ===<!--T:60-->
| |
| | |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_buzzer", {
| |
| title: "Test Buzzer",
| |
| cells: {
| |
| enabled: {
| |
| type: "pushbutton",
| |
| value: false
| |
| }
| |
| }
| |
| });
| |
| | |
| | |
| var test_interval = null;
| |
| | |
| defineRule({
| |
| whenChanged: "test_buzzer/enabled",
| |
| then: function (newValue, devName, cellName) {
| |
| var n = 0;
| |
| if (dev["test_buzzer"]["enabled"]){
| |
| test_interval = setInterval(function () {
| |
| dev["buzzer"]["enabled"] = !dev["buzzer"]["enabled"];
| |
| n = n+1;
| |
| if (n >= 10){
| |
| clearInterval(test_interval);
| |
| }
| |
| }, 500);
| |
| }
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| <!--T:61-->
| |
| | |
| === startTimer(); ===
| |
| Запускает однократный таймер. При срабатывании таймера происходит просмотр правил, при этом timers.<name>.firing для этого таймера становится истинным на время этого просмотра.
| |
| <!--T:63-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_buzzer", {
| |
| title: "Test Buzzer",
| |
| cells: {
| |
| enabled: {
| |
| type: "switch",
| |
| value: false
| |
| }
| |
| }
| |
| });
| |
| | |
| defineRule("1",{
| |
| asSoonAs: function () {
| |
| return dev["test_buzzer"]["enabled"] ;
| |
| },
| |
| then: function () {
| |
| startTimer("one_second", 1000);
| |
| dev["buzzer"]["enabled"] = true;//включаем пищалку
| |
| }
| |
| });
| |
| | |
| defineRule("2",{
| |
| when: function () { return timers.one_second.firing; },
| |
| then: function () {
| |
| dev["buzzer"]["enabled"] = false;//выключаем пищалку
| |
| dev["test_buzzer"]["enabled"] = false;
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| <!--T:64-->
| |
| | |
| === startTicker(); ===
| |
| Запускает периодический таймер с указанным интервалом. В примере правило 1 запускает таймер с интервалом 1 сек. Вызывая срабатывание правила 2. Метод stop() приводит к остановке таймера.
| |
| <!--T:66-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_buzzer", {
| |
| title: "Test Buzzer",
| |
| cells: {
| |
| enabled: {
| |
| type: "switch",
| |
| value: false
| |
| }
| |
| }
| |
| });
| |
| | |
| defineRule("1",{
| |
| asSoonAs: function () {
| |
| return dev["test_buzzer"]["enabled"] ;
| |
| },
| |
| then: function () {
| |
| startTicker("one_second", 1000);
| |
| }
| |
| });
| |
| defineRule("2",{
| |
| when: function () { return timers.one_second.firing; },
| |
| then: function () {
| |
| dev["buzzer"]["enabled"] = !dev["buzzer"]["enabled"];
| |
| if (dev["test_buzzer"]["enabled"] == false){
| |
| timers.one_second.stop();
| |
| }
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| <!--T:67-->
| |
| == Виртуальное устройство defineVirtualDevice ==
| |
| Правила делятся на два типа: непосредственно правила (defineRule) и виртуальные устройства (defineVirtualDevice).
| |
| | |
| Виртуальные устройства - это появляющиеся в веб-интерфейсе новые элементы управления - например, кнопка-выключатель, которая на самом деле выключает два устройства одновременно. Она не привязана напрямую ни к какому физическому устройству, а действия при её нажатии определяются написанным вами скриптом.
| |
| При написании скрипта вы можете создать виртуальное устройство для включения/выключения тех или иных управляющих алгоритмов и установки их параметров.
| |
| <!--T:20-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("wb-1", {
| |
| title: "Отопление",
| |
| cells: {
| |
| "Температура": {
| |
| type: "range",
| |
| max: 24,
| |
| value: 20,
| |
| },
| |
| "Вкл.": {
| |
| type: "switch",
| |
| value: false,
| |
| }
| |
| }
| |
| });
| |
| | |
| </syntaxhighlight>
| |
| | |
| === Типы устройств (type): ===
| |
| | |
| '''switch''' — переключатель. Может принимать значения true или false. По-умолчанию доступен для изменения пользователем.
| |
| <!--T:21-->
| |
| | |
| '''wo-switch''' — переключатель, аналог switch. Может принимать значения true или false. По-умолчанию не доступен для изменения пользователем.
| |
| <!--T:22-->
| |
| | |
| '''pushbutton''' — кнопка. Может принимать значения true. По-умолчанию доступна для нажатия.
| |
| <!--T:23-->
| |
| | |
| '''range''' — ползунок. Может принимать значения от 0 до max. По-умолчанию доступен для изменения пользователем.
| |
| <!--T:24-->
| |
| | |
| '''rgb''' — специальный тип для задания цвета. Кодируется 3 числами от 0 до 255, разделенными точкой с запятой. Например „255;0;0“ задает красный цвет. По-умолчанию доступен для изменения пользователем.
| |
| <!--T:25-->
| |
| | |
| '''alarm''' — индикатор. Может принимать значения true или false.
| |
| <!--T:26-->
| |
| | |
| '''text''' — текстовое поле. По-умолчанию не доступно для редактирования пользователем.
| |
| <!--T:27-->
| |
| | |
| '''value''' — значение с плавающей точкой. По-умолчанию не доступно для редактирования пользователем.
| |
| <!--T:28-->
| |
| | |
| Так же существуют еще 14 специальных типов. Все они аналогичны value, но имеют соответствующие подписи в интерфейсе.
| |
| <!--T:29-->
| |
| | |
| {| 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-->
| |
| | |
| === value: ===
| |
| Обязательное поле. При создании устройства в первый раз его значение будет установлено в значение по-умолчанию. В дальнейшем значения сохраняются в специальное хранилище в постоянной памяти и восстанавливаются при загрузке сценария.
| |
| <!--T:31-->
| |
| | |
| === forceDefault: ===
| |
| Если необходимо каждый раз при перезагрузке скрипта восстанавливать строго определённое значение (т.е. не восстанавливать предыдущее сохранённое), нужно добавить в описание контрола поле '''forceDefault: true'''
| |
| <!--T:33-->
| |
| | |
| === max: ===
| |
| Для параметра типа '''range''' может задавать его максимально допустимое значение.
| |
| <!--T:35-->
| |
| | |
| === readonly: ===
| |
| Когда задано истинное значение — устройство становится не доступным для редактирования пользователем. Если надо предоставить пользователю возможность редактировать значение, следует добавить в описание '''readonly: false'''
| |
| | |
| == Сообщения в лог ==
| |
| В зависимости от функции сообщение классифицируется как отладочное (debug), информационное (info), предупреждение (warning) или сообщение об ошибке (error).
| |
| <!--T:69-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_buzzer", {
| |
| title: "Test Buzzer",
| |
| cells: {
| |
| enabled: {
| |
| type: "pushbutton",
| |
| value: false
| |
| }
| |
| }
| |
| });
| |
| | |
| defineRule({
| |
| whenChanged: "test_buzzer/enabled",
| |
| then: function (newValue, devName, cellName) {
| |
| log.info('info');
| |
| log.debug('debug');
| |
| log.error('error');
| |
| log.warning('warning');
| |
| log('log!'); //сокращение для log.info();
| |
| debug('deb!'); //сокращение для log.debug();
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| <!--T:70-->
| |
| == Выполнение произвольной команды runShellCommand(); ==
| |
| | |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("test_button", {
| |
| title: "Test Button",
| |
| cells: {
| |
| enabled: {
| |
| type: "pushbutton",
| |
| value: false
| |
| }
| |
| }
| |
| });
| |
| | |
| defineRule({
| |
| whenChanged: "test_button/enabled",
| |
| then: function (newValue, devName, cellName) {
| |
| runShellCommand("mosquitto_pub -t '/devices/test_button/controls/enabled/meta/readonly' -r -m 1");
| |
| setTimeout(function () {
| |
| runShellCommand("mosquitto_pub -t '/devices/test_button/controls/enabled/meta/readonly' -r -m 0");
| |
| }, 5000);
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| | |
| | |
| | |
| == Примеры == <!--T:189-->
| |
| | |
| <!--T:190-->
| |
| Чтобы начать писать сложные правила, нужно посмотреть примеры правил и полную документацию по движку правил:
| |
| #Примеры правил: | |
| #* в статье [[Rule_Examples|Примеры правил]];
| |
| #* в [http://forums.contactless.ru/t/dvizhok-pravil-primery-koda/483 специальной теме на нашем форуме ].
| |
| #[https://github.com/contactless/wb-rules Полное описание движка правил].
| |
| | |
| == Совместимость скриптов при обновлении wb-rules ==
| |
| | |
| * [[Special:MyLanguage/Движок_правил_wb-rules_1.7|Движок правил wb-rules 1.7]]
| |
| * [[Special:MyLanguage/Движок_правил_wb-rules_2.0|Движок правил wb-rules 2.0]]
| |
| | |
| Предполагается, что при обновлении с предыдущей на следующую версию 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>
| |
| | |
| | |
| </translate>
| |