Движок правил wb-rules: различия между версиями

Материал из Wiren Board
м (Перенаправление на Wb-rules)
Метка: новое перенаправление
 
(не показаны 92 промежуточные версии 10 участников)
Строка 1: Строка 1:
В контроллере Wiren Board есть '''движок правил''', позволяющий писать как простые правила (например, "Если температура датчика больше 18С, выключи нагреватель"), так и сложные. В статье кратко описывается механизм работы правил и показываются примеры написания. Полная документация доступна на странице https://github.com/contactless/wb-rules.
#REDIRECT [[Wb-rules]]
== Как создать правило ==
=== Подготовка ===
Правила имеют вид файлов, которые пишутся на языке Javascript, и находятся на контроллере в папке <code>/etc/wb-rules</code>. Инструкцию по их редактированию смотрите в статье [[Просмотр файлов контроллера с компьютера]].
 
После изменения файла в папке <code>/etc/wb-rules</code> или создания нового скрипт автоматически начинает работать.
 
Список типов контролов в интерфейсе: https://github.com/contactless/homeui/blob/contactless/conventions.md
 
== Примеры правил ==
 
=== Слежение за контролом ===
 
Это простейшее правило следит за контролом и устанавливает другой контрол в такое же состояние.
 
Например правило может включать сирену и лампу, если датчик движения заметил движение.
 
В примере датчик движения подключен к входу "сухой контакт", контрол типа "switch". Сирена подключена к встроеному реле Wiren Board, а лампа - через релейный блок по Modbus.  Когда вход типа "сухой контакт" (выход датчика движения) замкнут, то на лампу и реле подаётся "1", когда выключен - "0".
 
 
Правило срабатывает каждый раз при изменении значения контрола "D1_IN" у устройства "wb-gpio".  В код правила передаётся новое значение этого контрола в виде переменной newValue.
 
<syntaxhighlight lang="ecmascript">
 
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;
 
  }
});
 
</syntaxhighlight>
 
 
То же самое, но с виртуальным девайсом в качестве источника событий. Пример использования: сценарная кнопка, которая включает/выключает сирену и лампочку.
 
<syntaxhighlight lang="ecmascript">
defineVirtualDevice("simple_test", {
    title: "Simple switch",
    cells: {
enabled: {
    type: "switch",
    value: false
},
    }
});
 
 
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;
 
  }
});
 
</syntaxhighlight>
 
 
=== Системные правила ===
Некторые правила поставляются с системой правил по-умолчанию в пакете wb-rules-system.
 
Полный список правил [https://github.com/contactless/wirenboard/tree/master/system_rules/rules в репозитории].
 
Некоторые примеры:
 
==== Правило для пищалки ====
[https://github.com/contactless/wirenboard/blob/master/system_rules/rules/buzzer.js Правило] создаёт виртуальное устройство buzzer с ползунками для регулировки громкости и частоты, а также кнопкой включения звука.
 
 
<syntaxhighlight lang="ecmascript">
defineVirtualDevice("buzzer", {
  title: "Buzzer", //
 
  cells: {
    frequency : {
        type : "range",
        value : 3000,
        max : 7000,
    },
    volume : {
        type : "range",
        value : 10,
        max : 100,
    },
    enabled : {
        type : "switch",
        value : false,
    },
  }
});
 
 
// setup pwm2
runShellCommand("echo 2 > /sys/class/pwm/pwmchip0/export");
 
 
 
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);
 
 
        runShellCommand("echo " + period + " > /sys/class/pwm/pwmchip0/pwm2/period");
        runShellCommand("echo " + duty_cycle + " > /sys/class/pwm/pwmchip0/pwm2/duty_cycle");
};
 
 
defineRule("_system_buzzer_params", {
  whenChanged: [
    "buzzer/frequency",
    "buzzer/volume",
    ],
 
  then: function (newValue, devName, cellName) {
    if ( dev.buzzer.enabled) {
        _buzzer_set_params();
    }
  }
});
 
 
defineRule("_system_buzzer_onof", {
  whenChanged: "buzzer/enabled",
  then: function (newValue, devName, cellName) {
    if ( dev.buzzer.enabled) {
        _buzzer_set_params();
        runShellCommand("echo 1  > /sys/class/pwm/pwmchip0/pwm2/enable");
    } else {
        runShellCommand("echo 0  > /sys/class/pwm/pwmchip0/pwm2/enable");
    }
  }
});
 
 
 
</syntaxhighlight>
 
==== Правило для статуса питания ====
 
[https://github.com/contactless/wirenboard/blob/master/system_rules/rules/power_status.js Правило] создаёт виртуальное устройство, которое сообщает текущий статус питания. В качестве входных данных используется два канала АЦП: измерение напряжения на аккумуляторе и измерение входного напряжения.
 
Реализована следующая логика:
 
1. Если входное напряжение меньше напряжение на аккмуляторе, то значит плата питается от аккумулятора. В этом случае, также отображается 0V в качестве входного напряжения.
 
2. Если входное напряжение  больше напряжения на аккумуляторе, то плата работает от внешнего источника питания. В качестве входонго напряжения отображается измерение с канала Vin.
 
 
Для иллюстрации правила используют два разных способа срабатывания: по изменению значения контрола (правило _system_track_vin) и по изменению значения выражения (два других).
 
<syntaxhighlight lang="ecmascript">
 
defineVirtualDevice("power_status", {
  title: "Power status", //
 
  cells: {
    'working on battery' : {
        type : "switch",
        value : false,
        readonly : true
    },
    'Vin' : {
        type : "voltage",
        value : 0
    }
 
 
  }
});
 
 
 
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"] ;
        }
    }
});
 
 
 
defineRule("_system_dc_on", {
  asSoonAs: function () {
    return  dev["wb-adc"]["Vin"] > dev["wb-adc"]["BAT"];
  },
  then: function () {
    dev["power_status"]["working on battery"] = false;
  }
});
 
defineRule("_system_dc_off", {
  asSoonAs: function () {
    return  dev["wb-adc"]["Vin"] <= dev["wb-adc"]["BAT"];
  },
  then: function () {
    dev["power_status"]["working on battery"] = true;
  }
});
 
</syntaxhighlight>
 
=== Отправка команд по RS-485 ===
 
Для примера отправим команду устройству на порт ''/dev/ttyNSC0'' (соответствует аппаратному порту RS-485-ISO на [[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 в документации].
 
С помощью движка правил создадим виртуальное устройство с контролом типа switch (переключатель).
 
При включении переключателя будем отправлять команду (уст. Яркость кан. 00=0xff) для Uniel UCH-M141:
<pre>
FF FF 0A 01 FF 00 00 0A
</pre>
 
 
При выключении переключателя будем отправлять команду (уст. Яркость кан. 00=0x00) для Uniel UCH-M141:
<pre>
FF FF 0A 01 00 00 00 0B
</pre>
 
 
 
 
 
1. Настройка порта
 
Для настройки порта /dev/ttyNSC0 на скорость 9600 надо выполнить следующую команду
 
<pre>
stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8
</pre>
 
2. Отправка команды
 
Отправка данных делается следующей шелл-командой:
 
<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".
 
 
3. Создадим в движке правил новый файл с правилами <code>/etc/wb-rules/rs485_cmd.js</code>
 
Файл можно редактировать с помощью vim, nano или mcedit в сеансе ssh на устройстве, либо залить его с помощью SCP.
 
<pre>
root@wirenboard:~# mcedit  /etc/wb-rules/rs485_cmd.js
</pre>
 
 
4. Описываем в файле виртуальный девайс
 
<syntaxhighlight lang="ecmascript">
defineVirtualDevice("rs485_cmd", {
    title: "Send custom command to RS-485 port",
    cells: {
enabled: {
    type: "switch",
    value: false
},
    }
});
</syntaxhighlight>
 
 
5. Перезапускаем wb-rules и проверяем работу
 
<pre>
root@wirenboard:~# /etc/init.d/wb-rules restart
root@wirenboard:~# tail -f /var/log/messages
</pre>
 
В логе не должно быть сообщений об ошибке (выход через control-c)
 
 
В веб-интерфейсе в разделе Devices должно появиться новое устройство "Send custom command to RS-485 port".
 
 
6. Добавим функцию для конфигурирования порта.
 
 
<syntaxhighlight lang="ecmascript">
function setup_port() {
    runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8");
}
 
</syntaxhighlight>
 
 
7. Опишем правила на включение и выключение переключателя
 
<syntaxhighlight lang="ecmascript">
defineRule("_rs485_switch_on", {
  asSoonAs: function () {
    return dev.rs485_cmd.enabled;
  },
  then: function() {
    runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\xff\\x00\\x00\\x0a' > /dev/ttyNSC0");
  }
});
 
defineRule("_rs485_switch_off", {
  asSoonAs: function () {
    return !dev.rs485_cmd.enabled;
  },
  then: function() {
    runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\x00\\x00\\x00\\x0b' >/dev/ttyNSC0");
  }
});
 
</syntaxhighlight>
 
 
Обратите внимание на двойное экранирование.
 
 
 
 
7. Собираем всё вместе
 
Полное содержимое файла с правилами:
 
<syntaxhighlight lang="ecmascript">
defineVirtualDevice("rs485_cmd", {
    title: "Send custom command to RS-485 port",
    cells: {
enabled: {
    type: "switch",
    value: false
},
    }
});
 
 
function setup_port() {
    runShellCommand("stty -F /dev/ttyNSC0 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8");
}
 
defineRule("_rs485_switch_on", {
  asSoonAs: function () {
    return dev.rs485_cmd.enabled;
  },
  then: function() {
    runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\xff\\x00\\x00\\x0a' > /dev/ttyNSC0");
  }
});
 
defineRule("_rs485_switch_off", {
  asSoonAs: function () {
    return !dev.rs485_cmd.enabled;
  },
  then: function() {
    runShellCommand("/usr/bin/printf '\\xff\\xff\\x0a\\x01\\x00\\x00\\x00\\x0b' >/dev/ttyNSC0");
  }
});
 
setTimeout(setup_port, 1000); // запланировать выполненеи setup_port() через 1 секунду после старта правил.
 
</syntaxhighlight>

Текущая версия на 18:37, 7 сентября 2021

Перенаправление на: