Wb-jscript

Материал из Wiren Board

Введение

Задача автоматизации - это упростить человеку жизнь. “Умный дом” - это, конечно, частный случай. Но полезный. Попытаемся применить скрипты контроллера WB (Wiren Board 6) для управления. Текст ниже предназначен тем, кто не имеет опыта программирования, но хочет научиться.

Используемое оборудование


В названиях “DI” - Digital Input “DO” - Output соответственно.

Программирование

Скрипт

Скриптом называется обыкновенный текстовый файл. Для того чтобы он выполнялся движком wb-rules нужно выполнить два условия:

  1. Имя файла должно заканчиваться на .js
  2. Файл должен располагаться в каталоге /etc/wb-rules

Веб-интерфейс контроллера имеет встроенный редактор скриптов, доступный из его главного меню по ссылке "Rules"

Выполнение скриптов

Только что созданный скрипт после нажатия кнопки "Save" (и при запуске wb-rules) сразу же выполнится. То есть, будут созданы все переменные, правила, таймеры. И дальнейшее поведение скрипта будет определяться только событиями, как внешними (изменения состояния устройств, на которые "подписаны" правила) так и таймерами.

Переменные

Где хранить значения, а также строки и все остальное, включая ссылки на объекты? Именно в переменных. Можно представить каждую переменную как коробку. Подписанную коробку. Подпись - это имя переменной, а содержимое коробки - это ее значение.

Кроме "простых" переменных еще используются массивы. Массив - это "стопка" значений, которые определяются одним именем переменной и "номером" в стопке. Еще есть структуры (объекты), но о них - позже.

Важно: JS, по умолчанию, при присвоении переменной одного типа значения другого типа - меняет тип переменной. Иногда это вызывает неожиданное поведение.

Типы переменных в JS:

  • String: представляет строку
  • Number: представляет числовое значение
  • Boolean: представляет логическое значение true или false.
  • undefined: указывает, что значение не установлено
  • null: указывает на неопределенное значение

Ниже попробуем создать первый скрипт и поймем значение типов.

Логи

Использование логов для отладки - бесценно. Иметь возможность в любом месте скрипта записать текущее значение переменной, состояние устройства очень помогает. Логи в WB двух типов - с записью в файл (/var/log/mesages) и в mqtt топик. Лог вызывается такой командой:

log.{debug,info,warning,error}(fmt, [arg1 [, ...]])

Научимся ими пользоваться, а заодно, и напишем первый скрипт. В меню “Rules” нажимаем “New” и в поле для имени, вверху пишем “logs_and_vars_test.js” И в поле скрипта вставляем:

//logs_and_vars_test.js
//Просто комментарий. Начинается с двойного слеша "//"
var testvar1; //Переменная "testvar1" Ничего не присваиваем при объявлении(создании), "пустая"
log.info("Тип и значение переменной testvar1", typeof(testvar1), testvar1); // Тип (и значение) undefined
testvar1 = "yellow submarine"; // Присвоим строку
log.info("Тип и значение переменной testvar1", typeof(testvar1), testvar1);
testvar1 = 42; //Присвоим число
log.info("Тип и значение переменной testvar1", typeof(testvar1), testvar1);
testvar1 = false; //Булево (true/false)
log.info("Тип и значение переменной testvar1", typeof(testvar1), testvar1);

testvar2 = "red submarine"; //Сразу при объявлении присваиваем значение
log.info("Тип и значение переменной testvar2", typeof(testvar2), testvar2);

testvar1 = testvar2; //Тип testvar1 - булево. testvar2 - строка
log.info("Тип и значение переменной testvar1", typeof(testvar1), testvar1);  // при присвоении тип поменялся.

Не забывайте пустую строку в конце скрипта.


Прежде чем сохранять, давайте зададимся вопросом “как же логи читать?” Два (даже три) способа.

  • Самый простой: нажимаем на кнопку с символом гаечного ключа. Она находится в нижнем правом углу окна редактора скриптов.
Кнопка для логов
Нажимаем - выдвигается консоль. Пока пустая, но после нажатия кнопки "Save" сюда выводятся сообщения.
Панель лога
  • Чуть сложнее: открываем ssh сессию на контроллере и вводим
tail -f /var/log/messages |grep wb-rules

Команда просматривает файл и при появлении в нем строчек с "wb-rules" Нажмем опять кнопку "Save" - выводит их в stdout (на экран)

Логи в SSH сессии

Чтобы кнопка "Save" стала вновь активной, надо что-нибудь изменить в окне редактора. Например, добавить пробел.

Можно убедиться, что скрипт выполняется и даже совершает полезное действие - выводит сообщения.

Как определить тип

Функция

typeof

принимает в качестве аргумента (то, что в скобках) переменную и возвращает нам ее тип

Если выполнить код:

variable1 = "yellow submarine";
variable2 = typeof(variable1);

То

variable2

будет иметь тип "string" и значение "yellow submarine"


2do: Дописать про форматирование вывода логов вида log("Инфо текст {} {} {}".format(var1, var2, var3));

Условия

Очень часто нужно что-нибудь с чем-то сравнить. Например, температуру с желаемой (с уставкой на тайном языке наладчиков-проектировщиков). Напишем простой пример, в котором сравним два значения.

//if_then.js 
//Просто комментарий. Начинается с двойного слеша "//"
var t_real =20; //Переменная в которой будет хранитьтся измеренная (реальная) температура
var t_ust = 22; //Переменная в которой хранится уставка (желаемая) температура

if (t_real > t_ust) { //В "()"скобках - условие.
  log.info("Температура", t_real, "БОЛЬШЕ уставки", t_ust); 
}

Не забывайте пустую строку в конце скрипта.


Использование

Сама конструкция условий основана на простой вещи: Если то, что в круглых скобках после оператора "if" верно - код в "{}" выполнитя Надо отвлечься на понятие - "верно" то есть истина, true Если мы говорим о условиях - сразу перечислим самые распростарненные:

  • > - больше true когда ( 5 > 3 )
  • < - меньше true когда ( 1 < 3 )
  • >= - больше или равно true когда ( 5 >= 3) и когда ( 5 >= 5 )
  • == - равно true когда ( 5 == 5) Внимание! про == ниже
  • || - ИЛИ true когда ( true || true ), кода ( true || false ), хоть одно из условий true
  • && - И true когда ( true && true ), кода оба true

И про "равно":

//if_then_eq.js 
var test1 = 2; 
var test2 = 3; 

if (test1 = test2) {
  log.info("Выполняется! А почему?!"); 
}

log.info("test1", test1); 
log.info("test2", test2);

Все просто. Для того чтобы определить истинность условия компилятор выполняет выражение в скобках. Первой переменной можно присвоить вторую? Можно! Все, условие выполнено. Поэтому не надо использовать "=" в условиях. Только "==".


Что делать, когда надо выполнять разные действия в зависимости от условия? Оператор elseсинтаксис ниже:

//if_then_else.js 
var t_real = 18; //Переменная в которой будет хранитьтся измеренная (реальная) температура
var t_ust = 22; //Переменная в которой хранится уставка (желаемая) температура

if (t_real > t_ust) { //В "()"скобках - условие.
  log.info("Температура", t_real, "БОЛЬШЕ уставки", t_ust); 
}
else { //Если условие НЕ выполняется
  log.info("Температура", t_real, "МЕНЬШЕ уставки", t_ust); 
}

Отладка

Если условие не работает так как нам надо - то:

  • Проверяем тип переменных перед условием
//if_then_types.js
 var test1 = 4; 
var test2 = "aaa"; //Переменная тип string
log.info("перед условием"); 
if (test1 > test2) { //В "()"скобках - условие.
  log.info("Больше"); 
}

Сравнивать переменные разных типов - некорректно. Выведем типы чтобы оценить возможность сравения:

//if_then_types_1.js
 var test1 = 4; 
var test2 = "aaa"; //Переменная тип string
log.info("перед условием. Тип test1", typeof(test1), "Тип test2", typeof(test2)); 
if (typeof(test1) !== typeof(test2)) { //А вот типы сравнивать - можно.
  log.info("Типы разные"); 
}
else {
  log.info("Типы одинаковые"); 
}

if (test1 && test2) { //В "()"скобках - условие.
  log.info("И - работает, потому что обе переменные содержат отличное от нуля"); 
}

"!" - это модификатор "НЕ" для логических условий. !true==false

Управление оборудованием

"Управлять" - значит получать состояния (входы) и реагировать на них, изменяя состояния выходов. Научиимся первому.

Входы и правила

Подключим модуль WBIO-DI-WD-14 и настроим его.

В Devices модуль отображается так:

Входы WD-14

Входы модуля имеют названия от EXT1_IN1 до EXT1_IN14. "EXT1" - потому что модуль первый от контроллера. Если подключить еще один такой же его названия входов будут начинаться с "EXT2".

При замыкании входа (возьмем EXT1_IN14) на клемму iGnd состояние входа меняется:

Вход EXT1_IN14 активен

Для того чтобы использовать устройство (вход или выход) в скриптах мы должны знать как к ним обращаться. Как посмотреть имена проще всего? Три пути:

  • Открыть в веб-интерфейсе MQTTChannels
    Каналы MQTT
  • Открыть в веб-интерфейсе Devices и щелкнуть мышью на имени:
    Всплывающее имя в Devices
    И имя уже будет скопировано в буфер обмена.
  • Выполнить в консоли команду
    mosquitto_sub -v -t /#
    
    Про MQTT

Напомню, что в контроллере используется событийная модель, то есть действовать, реагировать будет на изменения состояния. Для демонстрации создаем новый скрипт

//wd-14_in_14.js
var inputName = "wb-gpio/EXT1_IN14"; //Сохраним в переменной имя входа.

defineRule("wd-14_in_14", { //название правила 
whenChanged: inputName, //при изменении указанного значения
  then: function (newValue, devName, cellName) { //выполняй следующие действия
    log.info("newValue", newValue, "devName", devName, "cellName", cellName); //Это лог. 
  }
});


И сохраним его. После сохранения ничего не происходит. Почему? Как и писал выше - ключевое слово "событие". Мы "подписались" на нужный нам топик MQTT и теперь правило сработает при его изменении. Замкнем-разомкнем клемму "14" модуля WD-14 на его клемму "iGnd":

Правило сработало

То есть конструкция (синтаксис) правила такова:

defineRule("wd-14_in_14", { //название (то, что в кавычках). Можно использовать переменную. Правил с одинаковыми названиями быть не должно.
whenChanged: inputName, //Имя топика, на который подписываемся
  then: function (newValue, devName, cellName) { //Как и в конструкции if-then. Но в качестве выполняемых операторов - функция.
    log.info("newValue", newValue, "devName", devName, "cellName", cellName); //Это лог. Тут мы видим что в функцию передается как НОВОЕ значение топика так и имя устройстви с именем входа. Зачем, если и так знаем на что была подписка? Об этом дальше.
  }
});

Циклы

Функции

Выходы

Таймеры