whenChanged

	defineRule("Example_rule", {
	 whenChanged: ["dev1/some_topic", "dev2/another_topic"],
	 then: function (newValue, devName, cellName) {
		if (devName == "dev1")
		 dev["dev2/another_topic"] = newValue;
	   else if (devName == "dev2")
		 dev["dev1/some_topic"] = newValue;
	 }
	});

Вопрос: сколько раз вызовется правило?
А если добавить ещё одно устройство?

Добрый день.

Как указано здесь, при задании whenChanged правило срабатывает при любых изменениях значений параметров, указанных в массиве.
То есть, если изменится, dev1/some_topic или dev2/another_topic, то правило сработает. Если изменится снова, то сработает ещё раз и т.д.

Что означает “изменится”? Изменит своё значение по сравнению с прошлым вызовом правила? Или произойдёт обновление топика? Во втором случае, правило будет перезапускать само себя постоянно, как я понимаю. В первом же случае, правило, видимо, выполнится два раза.

Это значит, что если изменится значение в одном из указанных топиков, то произойдет вызов правила.
Например:

defineVirtualDevice("dev", {
    cells: {
      val_1: {
        type: "value",
        value: 0
      },
      val_2: {
        type: "value",
        value: 0
      }
    }
});

defineRule("rule", {
  whenChanged: ["dev/val_1", "dev/val_2"],
  then: function (newValue, devName, cellName) {
    log(newValue);
  }
});

При изменении значений:

dev["dev/val_1"] = 0;
dev["dev/val_1"] = 1;
dev["dev/val_1"] = 1;
dev["dev/val_2"] = 0;
dev["dev/val_2"] = 1;
dev["dev/val_2"] = 1;

Правило будет вызвано только 2 раза, что мы и наблюдаем в логе:

2024-04-05 15:23:13 1
2024-04-05 15:23:13 1

Меня такой ответ не очень устраивает. Виртуальные устройства создаются и обрабатываются самим движком wb-rules. Меня же интересует работа с реальными устройствами. К примеру, панель KNX нужно связать с устройством Modbus. Если я меняю значение в топике, соответствующему устройству Modbus, это значение отправляется в панель. Если же я меняю значение с панели, новое значение пересылается в Modbus.
Как конкретно работает whenChanged? Как оно проверяет, изменилось ли значение? Если схема усложнится, и управление нужно будет реализовать ещё с одного устройства, как поведёт себя правило?

GitHub - wirenboard/wb-rules: Rule engine for Wiren Board очень подробно описано.

Типично реализуется либо использованием флагов блокировки и trackMqtt либо использованием разных топиков.

Тут совсем непонятно. Правило поведет себя как оно написано, это просо hook на событие.

GitHub - wirenboard/wb-rules: Rule engine for Wiren Board очень подробно описано.

Да, я читал. Там непонятно.

Типично реализуется либо использованием флагов блокировки и trackMqtt либо использованием разных топиков.

Мне непонятно.

это просо hook на событие.

Может быть вам, айтишникам, это понятно. Я же привык не доверять коду, написанному айтишниками и на их айтишных языках. Как формируется событие? Если я правильно понимаю, если я записываю что-то в /on, а именно это я и делаю, присваивая dev, то значение топика не изменится, пока устройство не ответит. Но это не точно. Возможно, это зависит от драйвера. Я очень опасаюсь, что все эти правила начнут циклить и перезапускать друг друга без остановки

В случае внешних устройств — да, правильно.
Собственно это и описано здесь с примером.

	defineRule("Example_rule", {
	 whenChanged: ["dev1/some_topic", "dev2/another_topic"],
	 then: function (newValue, devName, cellName) {
		if (devName == "dev1")
		 dev["dev2/another_topic"] = newValue;
	   else if (devName == "dev2")
		 dev["dev1/some_topic"] = newValue;
	 }
	});

А можете пояснить на пальцах именно по данному коду, предложенному автором темы:
Если изменилось dev1, то

  1. вызывается правило и в нём в dev2 записывается нужное значение;
  2. снова вызывается данное правило, потому что в условиях его вызова указано изменение dev2. И тогда уже в dev1 снова будет записано тоже самое значение.

Далее у вас идёт речь будет ли рекурсия, но судя по всему её не будет, ибо фактического изменения dev1 уже не произойдёт. Об этом же речь?

И тогда вроде всё логично и правильно, если бы не одно но - сам по себе алгоритм уже построен с дефектом - зачем строить алгоритм так, чтобы правило срабатывало дважды на одно изменение. Т.е. получается, что критической ошибки в коде нет, он будет работать, но он составлен криво сам по себе, мне так кажется. Я бы всё же избегал таких построений. Там с точки зрения даже написания кода выбор по if бессмысленен, ибо два вызова пройдут по каждому из условий и будет эквивалентно:

	 then: function (newValue, devName, cellName) {
		 dev["dev2/another_topic"] = newValue;
		 dev["dev1/some_topic"] = newValue;
	 }

Или я не прав?

 then: function (newValue, devName, cellName) {
		 dev["dev2/another_topic"] = newValue;
		 dev["dev1/some_topic"] = newValue;
	 }

Так выглядит более правильно: не важно, откуда свалилось новое значение, записать его в оба топика, так как нам надо, чтобы они были одинаковы.
Но дальше либо dev1, либо dev2 пришлет подтверждение, значение топика обновится…

И снова будет вызвана эта же функция. Фишка то в этом. Вроде и не баг, работать будет, но демонстрация того, что пользователь не понимает смысловой нагрузки кода (либо намеренно забивает на побочный эффект).

что пользователь не понимает смысловой нагрузки кода (либо намеренно забивает на побочный эффект)

Так как же правильно?

Возможно я не очень корректно выражаясь, я не хочу сказать, что код не правильный по выполнению, я хочу сказать, что сама логика программирования не позволяет сделать иначе, но как побочный эффект мы получаем рекурсивный вызов с повторным обращением к другим объектам. Если устройства 2, то 2 вызова, если 3, то 3 вызова и т.п… Это вызывает путаница и вопросы у самого пользователя.

А как в данной среде это правильно сделать, кроме например, ручного отслеживания изменений всех объектов в цикле, запускаемом раз в какое-то время - я тоже пока не знаю)

Сам по себе WhenChanged с массивом аргументов неплох.
Надо только с головой его заполнять
А то ведь действительно можно получить автомат который будет сам себя выключать
как UselessBox
(191) Бесполезная коробка (Useless Box). Демонстрация работы - YouTube

Был на объекте. Работает такая схема плохо, главным образом, потому что ничего не понятно, что происходит. Весь вечер думал, как правильно сделать. Выходит, надо напрямую работать с MQTT. Стандартными средствами не получается.

Это как? На Си программу писать, или как-то из скрипта?

Внимательно читаем документацию. Предположим что есть топикА и топикБ
В whenChanged описаны оба.
Предположим что в функции описано действие так.

{
  if(celName == "топикA"){
    dev["топикБ"]=dev["топикA"]
  }
  else {
    dev["топикA"]=dev["топикБ"]
  }
}

Путь в исходном состоянии значения топикA и топикБ равны 0.
Меняем, например топикA. Публикуем в него 1
Срабатывает правило, попадаем в условие - публикуем 1 в топикБ.
топикБ изменился - опять попадаем в правило, публикуем (вторая ветка условия) в топикА (который уже равен 1) то же самое значение 1.
Все, значение топикА не меняется - правило больше не срабатывает. Ну просто же, все описано в документации.
Но это не значит, конечно что вообще нельзя сделать рекурсивно зацикливающееся правило. Можно. Например из двух отдельных.

1 лайк

Именно такое описание я и привел в сообщении выше. Но с точки зрения программирования это бред - что-то само на себя отвечает с холостым результатом. Зачем. Кстати, если даже разбить эти вызовы на две функции, то поведение будет такое же, я уже тоже об этом думал) и я так понимаю - в данной логике работы скрипта без этой двойной отработки не избавиться. А если три устройства, то вызова будет три, или он в одной итерации сразу отработает все изменения? Об этом спрашивал автор темы, кстати, но ответа не было)

Отнюдь. Крайне логично, хук сработал - обработчик вызвался. Откуда былии изменены данные - тут совершенно неважно.

Три, конечно.

С точки зрения программистов, т.е. задумки как оно должно работать - да, логично, но для построения сложных узлов у пользователей начинается путаница) и я понимаю, что редко какая программа обходится без костылей, идеального ничего не бывает, но объективно - если у нас начинаются ложные вызовы (ложные с точки зрения сути скрипта пользователя, а не написанной программы, то значит код не совсем подходит под задачу. Всё-таки на мой взгляд очень напрашиваются доп механизмы и функции в вб-рулез)