ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Логическая отладка: «почему мой поток не работает?»

Логическая отладка: «почему мой поток не работает?»

Урок 5 · Node-RED: установка, flows, msg/JSON, отладка · 30 мин · theory

Введение в логическую отладку: когда ошибок нет, но ничего не работает

На предыдущих уроках вы научились работать с инструментами, которые помогают отлавливать явные сбои в работе системы. Но что делать, если поток не выдает ошибок, но результат его работы некорректен или отсутствует вовсе? Свет не включается по кнопке, данные с датчика не приходят в MQTT, или сценарий климат-контроля неверно рассчитывает уставку. Это — логические ошибки.

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

> 🔗 Связанный материал: В отличие от ошибок времени выполнения, которые мы научились перехватывать с помощью ноды `Catch` (как рассмотрено в уроке `COURSE-06-M04-L02`), логические ошибки не вызывают исключений. Поток просто «молчит» или выдает неверный результат. Нода `Catch` их не увидит.

Типичные сценарии логических ошибок:

Основная цель логической отладки — это систематическая изоляция точки сбоя в потоке. Процесс похож на работу детектива: вы должны сформировать ментальную модель того, как должен работать ваш поток, а затем шаг за шагом проверять, соответствует ли реальное поведение этой модели. Вы выдвигаете гипотезу («Я думаю, проблема в этой ноде `Function`») и проверяете ее, инспектируя сообщения на входе и выходе из этой ноды.

---

Стратегия «разделяй и властвуй»: локализация проблемы с помощью нод Debug

Самый мощный и доступный инструмент для логической отладки — это нода `Debug`, которую мы уже рассматривали ранее. Однако для поиска логических ошибок мы будем использовать ее не как одиночный инструмент, а как систему трассировки.

Метод «разделяй и властвуй» заключается в том, чтобы разбить сложный поток на логические сегменты и проверить состояние объекта `msg` на границах этих сегментов.

Представьте поток, который должен читать температуру с Modbus-датчика, проверять, не превышает ли она порог, и в случае превышения отправлять команду на включение реле вентиляции.

[Modbus-Getter] -> [Function: Parse Temp] -> [Switch: Check Threshold] -> [Change: Set ON] -> [Relay Out]

Проблема: вентиляция не включается, хотя температура точно выше порога. Ошибок в панели отладки нет.

Алгоритм отладки:
  • Расставьте «посты наблюдения»: Разместите ноды `Debug` после каждого ключевого шага в потоке. Это ваши точки контроля.
  •     [Modbus-Getter] -> (Debug 1) -> [Function: Parse Temp] -> (Debug 2) -> [Switch: Check Threshold] -> (Debug 3) -> [Change: Set ON] -> (Debug 4) -> [Relay Out]

  • Настройте ноды `Debug`:
  • * Вывод полного объекта `msg`: В настройках каждой ноды `Debug` установите `Output` в `complete msg object`. Это критически важно! Часто проблема кроется не в `msg.payload`, а в других свойствах, например, в `msg.topic` или в неожиданно появившемся `msg.error`.

    * Осмысленное именование: Дайте каждой ноде `Debug` понятное имя.

    > 💡 Подсказка: Именуйте ваши `Debug` ноды осмысленно. Вместо «Debug 1», «Debug 2» используйте имена «[01] После Modbus Read», «[02] После форматирования», «[03] Перед включением реле». Нумерация поможет отследить последовательность вывода в панели отладки.

  • Запустите поток и анализируйте: Активируйте поток (например, с помощью ноды `Inject`) и внимательно изучите вывод в панели отладки.
  • * [01] После Modbus Read: Вы видите здесь корректный ответ от датчика? `msg.payload` содержит массив чисел, как и ожидалось?

    * [02] После форматирования: Нода `Function` правильно извлекла и преобразовала значение? `msg.payload` теперь имеет вид `{ "value": 28.5, "unit": "°C" }`?

    * [03] Перед включением реле: Прошло ли сообщение через ноду `Switch`? Если в панели отладки нет вывода от этой ноды, значит, условие в `Switch` не выполнилось. Проблема локализована на участке между `Debug 2` и `Debug 3`.

    * [04] Перед реле: Если сообщение дошло сюда, но реле не включилось, проблема, скорее всего, в конфигурации самой ноды `Relay Out`.

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

    ---

    Анализ msg: «дьявол» в типах данных и путях

    После того как вы локализовали проблемный участок с помощью стратегии «разделяй и властвуй», следующий шаг — детально проанализировать объект `msg` и найти корень проблемы. Две самые частые причины логических ошибок — это несоответствие типов данных и неверно указанный путь к свойству.

    Распространенные ошибки, связанные с типами данных

    JavaScript, язык ноды `Function`, пытается быть "гибким" в отношении типов, что часто приводит к неочевидным ошибкам.

    | Ошибка | Проблема | Пример кода, который не сработает | Решение |

    | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------- | ---------------------------------------------------------------------------- |

    | Строка (`string`) вместо числа (`number`) | Сравнение `msg.payload.value > 25` может работать некорректно, если `msg.payload.value` — это строка `"28.5"`. | `if (msg.payload.value > 25)` | `if (parseFloat(msg.payload.value) > 25)` |

    | Строка вместо булевого (`boolean`) | Нода `Switch` или `Function` проверяет `msg.payload === true`, а получает строку `"true"`. Это разные значения. | `if (msg.payload === true)` | `if (msg.payload === true || msg.payload === 'true')` или привести тип заранее. |

    | Число вместо строки | MQTT-клиент ожидает команду `"ON"`, а получает число `1`. Устройства не отреагируют. | `msg.payload = 1; return msg;` | `msg.payload = "ON"; return msg;` |

    Разбор реального кейса:

    Поток должен включать свет, когда из MQTT приходит сообщение со статусом. Логика в ноде `Switch` настроена на проверку `msg.payload.state == 'ON'`. Но свет не включается.

  • Ставим ноду `Debug` сразу после `MQTT In`.
  • Смотрим на полученное сообщение. Панель отладки показывает:
  •     {

    "topic": "devices/light_1/state",

    "payload": {

    "state": 1,

    "brightness": 100

    },

    "_msgid": "..."

    }

  • Диагноз: Проблема очевидна. Мы ожидали `msg.payload.state` как строку `'ON'`, а получили число `1`. Условие в `Switch` никогда не выполнится.
  • Решение: Изменить конфигурацию ноды `Switch`, чтобы она проверяла числовое значение `1`, или добавить ноду `Change` перед `Switch`, которая будет преобразовывать `1` в `'ON'` и `0` в `'OFF'`.
  • Инструменты для работы с путями и данными

    Node-RED предоставляет мощные встроенные инструменты для предотвращения ошибок с путями и типами.

    ---

    Отладка ноды Function: использование console.warn и console.error

    Нода `Function` — это «черный ящик». Сообщение входит, что-то происходит внутри, и сообщение выходит (или не выходит). Когда логика внутри сложная, расстановки нод `Debug` снаружи может быть недостаточно. Необходимо заглянуть внутрь исполнения кода.

    Для этого в ноде `Function` доступны специальные методы объекта `node`.

    > ⚠️ Внимание: Не используйте `node.error()` для обычной логической отладки. Эта функция создает настоящее сообщение об ошибке, которое будет обработано нодой `Catch`. Если нода `Catch` не установлена на потоке, `node.error()` остановит выполнение всего потока. Используйте его только для сигнализации о реальных, критических сбоях.

    `node.log`, `node.warn`, `node.error`

    | Метод | Куда выводит сообщение | Назначение |

    | -------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |

    | `node.log()` | В системный лог Node-RED (видно при запуске из консоли). | Для низкоуровневой отладки или записи событий, которые не нужны в UI, но важны для администратора системы. |

    | `node.warn()` | В панель отладки (вкладка "Debug messages") в виде предупреждения. | Основной инструмент для логической отладки внутри `Function`. Позволяет выводить значения переменных, не прерывая поток. |

    | `node.error()` | Создает объект ошибки, который передается на второй выход `Function` (если он есть) и перехватывается нодой `Catch`. | Для генерации настоящих ошибок, которые должны быть обработаны как сбои (например, невалидные входные данные). |

    Практика отладки с `node.warn()`

    Предположим, у нас есть нода `Function`, которая должна рассчитать среднюю температуру из массива показаний и проверить, не является ли она аномальной.

    Нерабочий код:
    // Вход: msg.payload = [22.5, 23.1, 22.8, "sensor_error", 23.3]
    

    let readings = msg.payload;

    let sum = 0;

    for (let i = 0; i < readings.length; i++) {

    sum += readings[i];

    }

    msg.payload = {

    average: sum / readings.length

    };

    return msg;

    Этот код сломается, потому что попытается прибавить строку `"sensor_error"` к числу. Но если на выходе мы видим `NaN` (Not a Number), не всегда очевидно, где именно произошел сбой.

    Отладочная версия с `node.warn()`:
    // Вход: msg.payload = [22.5, 23.1, 22.8, "sensor_error", 23.3]
    

    let readings = msg.payload;

    node.warn({step: "Start", input_data: readings}); // Выводим входные данные

    let sum = 0;

    let validReadingsCount = 0;

    for (let i = 0; i < readings.length; i++) {

    let currentValue = readings[i];

    // Проверяем тип каждой переменной перед сложением

    if (typeof currentValue === 'number' && !isNaN(currentValue)) {

    sum += currentValue;

    validReadingsCount++;

    } else {

    // Если данные невалидны, выводим предупреждение с проблемным значением

    node.warn({

    step: "Loop processing",

    warning: "Invalid data point found",

    index: i,

    value: currentValue,

    type: typeof currentValue

    });

    }

    }

    node.warn({step: "Calculation", total_sum: sum, valid_count: validReadingsCount});

    if (validReadingsCount > 0) {

    msg.payload = {

    average: sum / validReadingsCount

    };

    } else {

    // Если валидных данных не было, сообщаем об ошибке

    node.error("No valid readings found to calculate average", msg);

    return null; // Прерываем поток

    }

    return msg;

    Когда этот код выполнится, вы увидите в панели отладки серию сообщений от `node.warn`, которые точно покажут вам, на каком элементе массива (`"sensor_error"`) произошла проблема, каков был его тип, и каковы были промежуточные значения `sum` и `validReadingsCount`. Это дает полное понимание хода выполнения алгоритма.

    ---

    Резюме: Чек-лист по логической отладке

    Когда ваш поток не работает, а ошибок нет, не поддавайтесь панике. Применяйте системный подход.

    > 🔗 Связанный материал: Помните, что ваш «Операционный слой» (рассмотренный в `COURSE-06-M04-L04`) и поток журналирования (`COURSE-06-M04-L05`) — это тоже инструменты отладки, которые показывают общую картину работы системы и могут подсказать, где искать проблему.

    Чек-лист для поиска логических ошибок:

  • [ ] Сформулируйте гипотезу: Четко определите, что, по вашему мнению, работает не так. «Я думаю, нода `Switch` не пропускает сообщение».
  • [ ] Примените стратегию «разделяй и властвуй»: Расставьте ноды `Debug` (с выводом полного `msg` объекта) до и после проблемного участка.
  • [ ] Проверьте путь к данным: Запустите поток. В панели отладки найдите сообщение перед проблемной нодой. Скопируйте путь к нужному свойству с помощью кнопки «Copy Path». Убедитесь, что в ноде используется именно этот путь.
  • [ ] Проверьте тип данных: В той же панели отладки посмотрите на тип данных. Это `number`, `string`, `boolean` или `object`? Соответствует ли он тому, что ожидает ваша логика? При необходимости используйте функции преобразования (`parseInt()`, `parseFloat()`) или JSONata.
  • [ ] Загляните внутрь ноды `Function`: Если проблема внутри сложной функции, добавьте в код `node.warn()` для вывода значений промежуточных переменных. Проверьте каждую стадию вычислений.
  • [ ] Перепроверьте конфигурацию нод: Убедитесь в правильности настроек:
  • * `MQTT`: Правильный `topic`, `QoS`, `broker`?

    * `Modbus`: Верный `Unit ID`, `FC`, `Address`, `Server`?

    * `HTTP Request`: Правильный `URL`, `Method`, `Payload`?

    * `Switch`/`Change`: Корректно ли составлены правила?

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

    Что дальше?

    В следующем уроке мы рассмотрим продвинутые техники отладки, включая работу с контекстом потока, симуляцию входных данных для тестирования и использование внешних инструментов для анализа трафика, что позволит вам отлаживать распределенные и асинхронные системы.