Странное поведение редактора правил

Добрый день! Столкнулся с тем, что фактически идентичные фрагменты кода в одном файле правил сохраняются нормально, а в другом

var my_topic = 'BathTambHeater/temp1Sensor';
var my_device_id = my_topic.split('/')[0];
var my_channel_id = my_topic.split('/')[1];
var s9 = '';
s9 = getDevice(my_device_id).getControl(my_channel_id).getValue();

дает ошибку в последней приведенной строке

TypeError: invalid base value duk_hobject_props.c:2000 F /etc/wb-rules/rules.js:3018 preventsyield

Доступ к устройству предоставлен. Речь идет о фрагменте файла 33.js (строки 11-15) и файла rules.js (строки 3014-3018). Строка 3018 временно закомментирована для сохранения работоспособности кода, при ее раскомментировании имеем ошибку.

Здравствуйте! Я автоматический помощник техподдержки Wiren Board. Сейчас выходной — я попробую помочь сразу, а в ближайший рабочий день инженеры посмотрят тему и ответят.
Я проанализировал ваш вопрос и постарался найти возможное решение:
Если потребуется дополнительное уточнение, приложенную информацию прошу направить нажав ответить на мое крайнее сообщение.


Оборудование и контекст

Речь идёт о контроллере Wiren Board (скорее всего WB6/WB7/WB8) и движке правил wb-rules. Ошибка возникает при выполнении:

var my_topic = 'BathTambHeater/temp1Sensor';
var my_device_id = my_topic.split('/')[0];
var my_channel_id = my_topic.split('/')[1];
var s9 = '';
s9 = getDevice(my_device_id).getControl(my_channel_id).getValue();

в файле rules.js (стр. 3014–3018), при этом аналогичный фрагмент в 33.js работает.

Сообщение:

TypeError: invalid base value duk_hobject_props.c:2000 F /etc/wb-rules/rules.js:3018 preventsyield

означает, что один из вызовов по цепочке getDevice(...).getControl(...).getValue() вызывается на undefined / null и движок Duktape не может получить свойство/метод.


Что, скорее всего, происходит

  1. getDevice(my_device_id) возвращает undefined
    Это бывает, если:

    • на момент выполнения правила устройства с таким ID ещё нет в wb-mqtt-db (например, драйвер ещё не поднялся, устройство по Modbus ещё не опрошено, или MQTT-топик ещё не появился);
    • устройство существует, но его ID отличается от ожидаемого (регистр/опечатка, лишние пробелы, другой prefix и т.д.).
  2. или getControl(my_channel_id) возвращает undefined
    Аналогично — если контрол не существует / ещё не создан.

  3. Почему в 33.js работает, а в rules.js нет
    Наиболее частые причины:

    • в одном правиле код выполняется позже, когда устройство гарантированно есть (например, внутри defineRule с when: function () { return dev[...] } или по таймеру),
      а во втором — при загрузке правил (глобально, при старте wb-rules), когда устройства ещё не доступны;
    • в одном файле вы вызываете код в обработчике события (по изменению топика), а в другом — однократно при загрузке;
    • возможны отличия в строке my_topic (даже один символ или регистр).

Что проверить прямо сейчас

  1. Убедитесь, что устройство и контрол реально есть и названия совпадают:
mqtt-sub -t '/devices/+/controls/+'

Посмотрите, появляется ли топик /devices/BathTambHeater/controls/temp1Sensor.
В wb-rules это должно соответствовать:

getDevice("BathTambHeater").getControl("temp1Sensor")
  1. Вставьте временную отладку в проблемный участок rules.js:
var my_topic = 'BathTambHeater/temp1Sensor';
var my_device_id = my_topic.split('/')[0];
var my_channel_id = my_topic.split('/')[1];

log.info("my_device_id =", my_device_id, "my_channel_id =", my_channel_id);

var devObj = getDevice(my_device_id);
log.info("devObj =", devObj);

if (!devObj) {
  log.error("Device not found:", my_device_id);
} else {
  var ctrlObj = devObj.getControl(my_channel_id);
  log.info("ctrlObj =", ctrlObj);
  if (!ctrlObj) {
    log.error("Control not found:", my_channel_id);
  } else {
    var s9 = ctrlObj.getValue();
    log.info("s9 =", s9);
  }
}

Если на этапе devObj или ctrlObj увидите undefined — значит проблема именно в отсутствии устройства/контрола в момент выполнения.


Как корректно защититься от ошибки

  1. Проверяйте наличие устройства и контрола:
var devObj = getDevice(my_device_id);
if (!devObj) {
  log.error("No such device:", my_device_id);
  return;
}

var ctrlObj = devObj.getControl(my_channel_id);
if (!ctrlObj) {
  log.error("No such control:", my_channel_id);
  return;
}

var s9 = ctrlObj.getValue();
  1. Не вызывать это глобально при загрузке файла, а выполнять:

    • либо внутри defineRule по событию/условию:
      defineRule("read_bath_temp", {
        when: function () {
          return dev["BathTambHeater"]["temp1Sensor"] != undefined;
        },
        then: function () {
          var s9 = dev["BathTambHeater"]["temp1Sensor"];
          // ...
        }
      });
      
    • либо через таймер, который подождёт некоторое время после старта:
      setTimeout(function () {
        // тот же код с проверками devObj / ctrlObj
      }, 5000);
      
  2. Упростить запись через dev[...], если подходит:

var s9 = dev["BathTambHeater"]["temp1Sensor"];

Это тоже бросит ошибку, если устройства нет, но вы можете обернуть в try/catch или в условие:

if (dev["BathTambHeater"] && dev["BathTambHeater"]["temp1Sensor"] != undefined) {
  var s9 = dev["BathTambHeater"]["temp1Sensor"];
}

Что лучше приложить дальше

Чтобы инженеры могли точно воспроизвести проблему, пожалуйста, ответьте в теме и приложите:

  1. Обе версии фрагмента кода:
    • из работающего 33.js (строки 11–15),
    • и из rules.js (строки 3010–3020) целиком — с контекстом (внутри/вне defineRule, есть ли setTimeout, и т.п.).
  2. Вывод:
    mqtt-sub -t '/devices/+/controls/+'
    
    (хотя бы строки с BathTambHeater).
  3. Лог фрагмента service wb-rules status или часть journalctl -u wb-rules с этой ошибкой.

*Примечание: Я — искусственный

Разобрался. Дело было в ошибке в названии одного из каналов, что мешало сохранению без ошибки.

Здравствуйте! Я автоматический помощник техподдержки Wiren Board. Сейчас выходной — я попробую помочь сразу, а в ближайший рабочий день инженеры посмотрят тему и ответят.
Я проанализировал ваш вопрос и постарался найти возможное решение:
Если потребуется дополнительное уточнение, приложенную информацию прошу направить нажав ответить на мое крайнее сообщение.

Хорошо, что удалось разобраться самостоятельно. Ваше наблюдение полностью укладывается в типичное поведение wb-rules: если в цепочке

getDevice(my_device_id).getControl(my_channel_id).getValue();

хотя бы один элемент (device или control) не существует (в том числе из‑за опечатки в имени канала), движок отдаёт именно такую TypeError от Duktape.

Чтобы в будущем такие ситуации было легче отлавливать и не ломать всё правило, можно добавить промежуточные проверки:

var devObj = getDevice(my_device_id);
if (!devObj) {
  log.error("Device not found:", my_device_id);
  return;
}

var ctrlObj = devObj.getControl(my_channel_id);
if (!ctrlObj) {
  log.error("Control not found:", my_channel_id);
  return;
}

var s9 = ctrlObj.getValue();

Так при ошибке в названии канала правило не «падает» целиком, а в логах сразу видно, чего именно не хватает.

Документация по работе с getDevice и контролами приведена в разделе про движок правил wb-rules.[1]

Если захотите, можете прислать сюда проблемный фрагмент rules.js и актуальные имена каналов — можно будет подсказать, как сделать код более устойчивым к таким опечаткам.

Примечание: Я — искусственный интеллект, а не сотрудник-человек. В моих ответах возможны неточности.


  1. Движок правил wb-rules — Wiren Board ↩︎