|
Метка: новое перенаправление |
(не показано 5 промежуточных версий этого же участника) |
Строка 1: |
Строка 1: |
| <languages/>
| | #REDIRECT [[Rule_Examples]] |
| <translate>
| |
| <!--T:1-->
| |
| [[File:Wb rules demo.png|400px|thumb|right|Редактирование правил в веб-интерфейсе]]
| |
| Для контроллера можно писать правила, например: "Если температура датчика меньше 18°С, включи нагреватель". Правила создаются через [[Special:MyLanguage/Веб-интерфейс Wiren Board|веб-интерфейс]] и пишутся на простом языке, похожем на Javascript.
| |
| | |
| <!--T:2-->
| |
| Самое полное описание движка правил: https://github.com/contactless/wb-rules
| |
| | |
| | |
| | |
| | |
| | |
| == Как создавать и редактировать правила == <!--T:3-->
| |
| | |
| <!--T:4-->
| |
| *Список файлов с правилами находится на странице ''Scripts'' веб-интерфейса.
| |
| *Нажмите на название файла, чтобы открыть его для редактирования.
| |
| **Чтобы создать новый файл, нажмите на пункт ''New...'', вверху введите название скрипта (используйте для названия только латинские буквы и цифры, в качестве расширения укажите ''.js''), в основное поле введите текст скрипта, затем нажмите кнопку ''Save'' вверху.
| |
| *Правило начинает работать автоматически после нажатия кнопки ''Save'', если в нём нет ошибок (смотрите ниже).
| |
| Примечания:
| |
| #Файлы с правилами хранятся на контроллере в виде обычных текстовых файлов в папке <code>/etc/wb-rules/</code>, поэтому [[Special:MyLanguage/Просмотр файлов контроллера с компьютера|их можно редактировать и загружать напрямую с компьютера]]. | |
| #Правила исполняются сервисом ''wb-rules'', документацию по нему смотрите [https://github.com/contactless/wb-rules странице сервиса в Github].
| |
| | |
| | |
| | |
| == Пишем первое правило == <!--T:5-->
| |
| | |
| <!--T:6-->
| |
| [[File:Web-scripts-rule1.png|400px|thumb|Правило для управления обогревателем, записанное через веб-интерфейс]]
| |
| | |
| <!--T:7-->
| |
| Правила бывают двух типов - непосредственно правила (начинаются со слов ''defineRule'') и виртуальные устройства (начинаются со слов ''defineVirtualDevice''). Виртуальные устройства - это появляющиеся в веб-интерфейсе новые элементы управления - например, кнопка-выключатель, которая на самом деле выключает два устройства одновременно. Она не привязана напрямую ни к какому физическому устройству, а действия при её нажатии определяются написанным вами скриптом.
| |
| | |
| <!--T:8-->
| |
| Любое количество разных правил можно хранить в одном файле. Обычно в одном файле хранятся правила, отвечающие за близкие функции.
| |
| | |
| | |
| | |
| === Первое правило === <!--T:9-->
| |
| | |
| <!--T:10-->
| |
| Для начала разберём простое правило - при превышении температуры выключи обогреватель. Температуру получаем с датчика [[Special:MyLanguage/1-Wire|1-Wire]], обогреватель подключён к Реле 1 внешнего релейного модуля [[Special:MyLanguage/WB-MRM2|WB-MRM2]].
| |
| <syntaxhighlight lang="ecmascript">
| |
| | |
| <!--T:11-->
| |
| defineRule("heater_control", { //название правила - "контроль обогревателя", может быть произвольным
| |
| whenChanged: "wb-w1/28-0115a48fcfff", //при изменении состояния датчика 1-Wire с идентификатором 28-0115a48fcfff
| |
| then: function (newValue, devName, cellName) { //выполняй следующие действия
| |
| if ( newValue > 30) { //если температура датчика больше 30 градусов
| |
| dev["wb-mrm2_130"]["Relay 1"] = 0; //установи Реле 1 модуля WB-MRM2 с адресом 130 в состояние "выключено"
| |
| } else {
| |
| dev["wb-mrm2_130"]["Relay 1"] = 1; //установи Реле 1 модуля WB-MRM2 с адресом 130 в состояние "включено"
| |
| }
| |
| }
| |
| });
| |
| | |
| <!--T:12-->
| |
| </syntaxhighlight>
| |
| *Первая строка - кодовое слово ''defineRule'' и название правила
| |
| *Вторая строка - кодовое слово для определения, когда выполняется правило, - ''whenChanged'' - "при изменении параметра", далее название параметра, при изменении которого запустится правило - температура с датчика 1-Wire. Название параметра записывается в виде "Device/Control", где названия ''Device'' и ''Control'' для каждого параметра можно найти на странице ''Settings'' веб-интерфейса в таблице ''MQTT Channels''.
| |
| *Третья строка - начало функции, которая будет исполняться
| |
| *Затем идёт условие - "если значение температуры больше порогового, то ...". Значение параметра записывается в виде ''dev[Device][Control]'' - заметьте, оно отличается от вида записи параметра, при изменении которого запускается правило, потому что там речь идёт о ''параметре'', а здесь - о ''значении'' того же параметра.
| |
| *Затем мы выставляем значения для реле в каждом случае - ''0'' - "выключено", ''1'' - "включено". Названия ''Device'' и ''Control'' для реле смотрим всё в той же таблице ''MQTT Channels'' на странице ''Settings'' веб-интерфейса.
| |
| | |
| | |
| | |
| === Первое правило с виртуальным устройством === <!--T:13-->
| |
| | |
| <!--T:14-->
| |
| Создаём виртуальный переключатель, при нажатии на который переключаются сразу два реле.
| |
| | |
| <!--T:15-->
| |
| <syntaxhighlight lang="ecmascript">
| |
| defineVirtualDevice("switch_both", {
| |
| title: "Switch both relays",
| |
| cells: {
| |
| enabled: {
| |
| type: "switch",
| |
| value: false
| |
| },
| |
| }
| |
| });
| |
| | |
| <!--T:16-->
| |
| defineRule("control_both", {
| |
| whenChanged: "switch_both/enabled",
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-mrm2_130"]["Relay 1"] = newValue;
| |
| dev["wb-mrm2_130"]["Relay 2"] = newValue;
| |
| }
| |
| });
| |
| </syntaxhighlight>
| |
| | |
| | |
| == Виртуальное устройство defineVirtualDevice == <!--T:17-->
| |
| | |
| | |
| | |
| Виртуальные устройства - это появляющиеся в веб-интерфейсе новые элементы управления - например, кнопка-выключатель, которая на самом деле выключает два устройства одновременно. Она не привязана напрямую ни к какому физическому устройству, а действия при её нажатии определяются написанным вами скриптом.
| |
| При написании скрипта вы можете создать виртуальное устройство для включения/выключения тех или иных управляющих алгоритмов и установки их параметров.
| |
| <!--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. По-умолчанию доступна для нажатия.
| |
| range — ползунок. Может принимать значения от 0 до max. По-умолчанию доступен для изменения пользователем.
| |
| <!--T:23-->
| |
| | |
| '''rgb''' — специальный тип для задания цвета. Кодируется 3 числами от 0 до 255, разделенными точкой с запятой. Например „255;0;0“ задает красный цвет. По-умолчанию доступен для изменения пользователем.
| |
| <!--T:24-->
| |
| | |
| '''alarm''' — индикатор. Может принимать значения true или false.
| |
| <!--T:25-->
| |
| | |
| '''text''' — текстовое поле. По-умолчанию не доступно для редактирования пользователем.
| |
| <!--T:26-->
| |
| | |
| '''value''' — значение с плавающей точкой. По-умолчанию не доступно для редактирования пользователем.
| |
| <!--T:27-->
| |
| | |
| Так же существуют еще 14 специальных типов. Все они аналогичны value, но имеют соответствующие подписи в интерфейсе.
| |
| <!--T:28-->
| |
| | |
| {| 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'''
| |
| | |
|
| |
| == Типы правил 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;
| |
| }
| |
| });
| |
| defineRule({
| |
| asSoonAs: function() {
| |
| return dev["wb-gpio"]["A3_IN"];
| |
| },
| |
| then: function (newValue, devName, cellName) {
| |
| dev["wb-mrm2-mini_2"]["Relay 2"] = false;
| |
| }
| |
| });
| |
| | |
| </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, например, 0 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-->
| |
| | |
| == Сообщения в лог ==
| |
| В зависимости от функции сообщение классифицируется как отладочное (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-->
| |
| Чтобы начать писать сложные правила, нужно посмотреть примеры правил и полную документацию по движку правил:
| |
| #Примеры правил:
| |
| #* на этой же странице ниже;
| |
| #* в [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>
| |