ГлавнаяАкадемияОсновы умного дома → Мини-Runbook: Шаг 4 (Проверка логики)

Мини-Runbook: Шаг 4 (Проверка логики)

Урок 3 · Основы умного дома · 30 мин · theory

Введение в тестирование логики

> 🔗 Связанный материал: Этот урок является прямым продолжением урока COURSE-01-M08-L03, где мы убедились, что все входные сигналы корректно поступают в систему. Теперь наша задача — проверить, что система на них правильно реагирует.

Проверка логики — это четвертый и ключевой этап в нашем "Мини-Runbook" по диагностике и сдаче объекта. Последовательность шагов (1. Питание → 2. Сеть → 3. Входы → 4. Логика) выбрана не случайно. Она основана на принципе пошаговой изоляции неисправностей. Если мы убедились, что оборудование получает питание, имеет стабильное сетевое соединение и корректно передает данные (например, нажатие кнопки видно в MQTT-брокере), но конечный результат (включение света) отсутствует, то с вероятностью 99% проблема находится именно в логическом слое — в потоках Node-RED.

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

  • Скорость и удобство: Вам не нужно бегать по объекту, нажимая кнопки или закрывая датчики движения, чтобы проверить одну ветку логики. Все входные сигналы можно сэмулировать одним кликом мыши, не вставая со стула.
  • Надежность: Если вы тестируете логику одновременно с проверкой физического устройства (например, Modbus-счетчика), вы не можете быть уверены, где именно кроется ошибка. Счетчик может быть неверно настроен, кабель RS-485 может быть поврежден, и вы потратите часы на отладку идеально работающего потока Node-RED. Изолируя логику, вы проверяете только один компонент за раз.
  • Воспроизводимость: Вы можете многократно и предсказуемо подавать на вход логики одни и те же данные, чтобы убедиться в стабильности ее работы, включая пограничные значения и нештатные ситуации.
  • Частые логические ошибки

    В процессе создания потоков инженеры, особенно на начальном этапе, сталкиваются с типовыми ошибками, которые легко обнаружить при изолированном тестировании:

    Инструменты для отладки логики в Node-RED

    Платформа Node-RED предоставляет мощный встроенный набор инструментов, который делает процесс отладки наглядным и эффективным. Для тестирования логики нам понадобятся четыре основных узла:

    | Узел | Назначение | Ключевая роль в тестировании |

    | :--- | :--- | :--- |

    | Inject | Инициирует поток, отправляя сообщение по клику или расписанию. | Симулятор/Эмулятор. Заменяет физические входы (MQTT, Modbus, KNX) и позволяет подавать любые тестовые данные в нашу логику. |

    | Debug | Отображает полученные сообщения в боковой панели отладки. | Микроскоп. Позволяет "заглянуть" внутрь сообщения `msg` на любом этапе его прохождения по потоку и увидеть, как изменяются `msg.payload`, `msg.topic` и другие свойства. |

    | Catch | Перехватывает ошибки, сгенерированные другими узлами на вкладке. | Система безопасности. Ловит все необработанные ошибки (например, при попытке парсинга некорректного JSON), предотвращая "тихий" сбой потока и логируя проблему. |

    | Status | Отображает короткий текстовый статус под узлом. | Индикатор/Дашборд. Позволяет в реальном времени видеть состояние узла (например, "ОК: 22.5°C" или "Ошибка: Таймаут"), что очень полезно для быстрой визуальной диагностики без открытия панели отладки. |

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

    ---

    Практика: Симуляция входных сигналов с помощью узла Inject

    > 💡 Подсказка: Чтобы быстро переключаться между реальным входом (например, узлом `MQTT In`) и симуляцией, не удаляйте соединения! Просто добавьте узел `Inject` и подключите его к тому же входу логики. Для перевода системы в "боевой" режим достаточно отключить (`disable`) узел `Inject` в редакторе Node-RED, а не удалять его.

    Техника замещения входа — это основа изолированного тестирования. Вместо того чтобы ждать сигнала от реального оборудования, мы сами создаем этот сигнал с помощью узла `Inject`. Это позволяет нам проверить все ветки логики, включая те, которые сложно воспроизвести в реальности (например, одновременное срабатывание трех датчиков).

    Настройка узла `Inject` для эмуляции

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

  • Эмуляция дискретного сигнала (кнопка, геркон, датчик движения):
  • * Откройте настройки узла `Inject`.

    * В поле `payload` выберите тип `boolean`.

    * Установите значение `true` для имитации срабатывания (нажатие кнопки, обнаружение движения) или `false` для имитации отпускания/отсутствия движения.

    * Назовите узел соответственно, например, "Движение ЕСТЬ" или "Кнопка НАЖАТА".

  • Эмуляция аналогового сигнала (датчик температуры, освещенности):
  • * В поле `payload` выберите тип `number`.

    * Введите числовое значение. Например, `23.5` для датчика температуры или `150` для датчика освещенности.

    * Создайте несколько узлов `Inject` для тестирования пограничных состояний: "Темно (50 люкс)", "Сумерки (250 люкс)", "Светло (1000 люкс)".

  • Эмуляция сложной структуры данных (метеостанция, счетчик энергии):
  • * Часто устройства передают данные в виде структурированного объекта. Для этого используйте тип `JSON`.

    * В редакторе `JSON` введите объект, который имитирует ответ устройства.

    Пример 1: Эмуляция нажатия KNX-кнопки

    Предположим, у вас есть логика, которая запускается при получении команды от KNX-кнопки. Обычно это узел `knx-in`, который при нажатии выдает `msg.payload = true`.

    Старый поток (с реальным оборудованием):

    `[KNX In: Кнопка Гостиная]` --> `[Логика включения света]`

    Тестовый поток (с эмуляцией):

    `[Inject: Кнопка НАЖАТА]` --> `[Логика включения света]`

    Настройка узла `Inject`:

    Теперь каждый клик по этому узлу `Inject` будет равносилен физическому нажатию KNX-кнопки на стене.

    Пример 2: Эмуляция данных с метеостанции по MQTT

    Представим, что контроллер получает данные с уличной метеостанции в виде JSON-объекта в MQTT-топике `weather/station/data`.

    Пример сообщения в MQTT:
    {
    

    "temperature": -5.2,

    "humidity": 88,

    "pressure": 1021,

    "wind_speed": 4.5,

    "is_raining": false

    }

    Чтобы протестировать логику, которая реагирует на эти данные (например, "закрыть окна, если `is_raining` = `true`"), мы создаем узел `Inject`, который в точности повторяет эту структуру.

    Настройка узла `Inject`:
        {
    

    "temperature": -5.2,

    "humidity": 88,

    "pressure": 1021,

    "wind_speed": 4.5,

    "is_raining": true

    }

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

    ---

    Анализ потока данных с помощью Debug и Function

    > ⚠️ Внимание: Никогда не оставляйте активные узлы `Debug`, настроенные на вывод полного объекта сообщения (`complete message object`), в промышленной системе. На объектах с высокой частотой сообщений (например, мониторинг энергии каждую секунду) это создает избыточную нагрузку на процессор контроллера, может переполнять журнал и замедлять работу всей логики.

    Если узел `Inject` — это наш симулятор, то узел `Debug` — это наш микроскоп. Он позволяет в деталях рассмотреть объект `msg`, который является "кровью" системы автоматизации, на любом участке его пути.

    Эффективное использование узла `Debug`

    Просто "кинуть" узел `Debug` на поток — недостаточно. Эффективная отладка требует правильной его настройки.

    * `msg.payload` (настройка по умолчанию): Быстрый и удобный способ посмотреть основное значение, которое несет сообщение. Используется в 80% случаев.

    * `complete message object`: Режим для глубокого анализа. Он показывает не только `payload`, но и `topic`, `_msgid` (уникальный идентификатор сообщения), а также любые другие свойства, которые были добавлены в объект `msg` предыдущими узлами. Это незаменимо для отладки сложной маршрутизации по `msg.topic` или когда нужно понять, почему данные теряются между узлами.

    В сложном потоке у вас может быть 5-10 узлов `Debug`. Если все они называются "debug", панель отладки превращается в хаос. Всегда давайте узлам `Debug` осмысленные имена, отражающие их местоположение в потоке.

    | Плохое имя | Хорошее имя |

    | :--- | :--- |

    | debug 1 | `Debug: После MQTT In` |

    | debug 2 | `Debug: После валидации` |

    | debug 3 | `Debug: Перед записью в реле` |

    | debug 4 | `Debug: Команда для KNX` |

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

    Продвинутая отладка с помощью узла `Function`

    Иногда простой вывод `msg` недостаточен. Нужно вывести в журнал кастомное сообщение или проверить значение переменной, не прерывая поток. Для этого идеально подходит узел `Function` и его встроенные методы `node.warn()` и `node.error()`.

    Практический кейс: отслеживание "жизни" сообщения `msg`

    Представим поток, где мы получаем температуру, преобразуем ее и формируем команду.

    ASCII-схема:

    `[Inject]` -> `[Function: Обработка]` -> `[Change: Формирование топика]` -> `[Debug: Финальная команда]`

    Код для узла `Function: Обработка`:
    // Входящее сообщение: msg.payload = 22.7
    

    let temp = msg.payload;

    // Выводим отладочное сообщение в панель, не прерывая поток

    node.warn({ "статус": "Начало обработки", "входное_значение": temp });

    if (temp > 25) {

    msg.payload = "HIGH";

    } else if (temp < 18) {

    msg.payload = "LOW";

    } else {

    msg.payload = "NORMAL";

    }

    // Теперь msg.payload содержит "NORMAL"

    return msg;

    В панели отладки мы увидим желтое сообщение от `node.warn` с исходным значением температуры, а узел `Debug`, подключенный после, покажет нам уже преобразованное значение (`"NORMAL"`). Это позволяет отслеживать трансформацию данных на каждом шаге.

    ---

    Пример: Сквозное тестирование логики управления освещением

    Давайте объединим полученные знания и проведем полное тестирование логики для популярного сценария "Вечерний свет".

    Требования к сценарию: Основной свет в гостиной должен включаться, если:
  • Есть движение в комнате И уровень освещенности ниже 200 люкс.
  • ИЛИ было нажатие настенного выключателя.
  • Построение тестового стенда в Node-RED

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

    1. Симуляция входов:

    Нам понадобится три узла `Inject`, которые будут эмулировать наши датчики и выключатель.

    * `Name`: `Эмуляция: Движение ЕСТЬ`

    * `Payload`: `boolean` : `true`

    * `Topic`: `sensor/motion/living_room`

    * `Name`: `Эмуляция: Освещенность 150 люкс`

    * `Payload`: `number` : `150`

    * `Topic`: `sensor/lux/living_room`

    * `Name`: `Эмуляция: Нажатие выключателя`

    * `Payload`: `boolean` : `true`

    * `Topic`: `switch/wall/living_room/set`

    2. Логический блок:

    Реализуем логику `(движение И темно) ИЛИ выключатель`. Проще всего это сделать в узле `Function`, который будет хранить состояния входов в контексте потока (`flow context`).

        // Получаем текущие состояния из контекста, если их нет - инициализируем
    

    let motion = flow.get("motion_living_room") || false;

    let lux = flow.get("lux_living_room") || 999;

    let wall_switch = false; // Состояние выключателя не храним, реагируем на событие

    // Обновляем состояния на основе топика входящего сообщения

    if (msg.topic === 'sensor/motion/living_room') {

    motion = msg.payload;

    flow.set("motion_living_room", motion);

    } else if (msg.topic === 'sensor/lux/living_room') {

    lux = msg.payload;

    flow.set("lux_living_room", lux);

    } else if (msg.topic === 'switch/wall/living_room/set') {

    wall_switch = msg.payload;

    }

    // Основная логика

    if ((motion && lux < 200) || wall_switch) {

    // Условие выполнено, формируем команду на включение

    // Используем контракт сообщения для реле Wirenboard

    msg.topic = "wirenboard/wb-mr6c_33/controls/K1/on";

    msg.payload = "1";

    return msg;

    }

    // Если условия не выполнены, ничего не делаем

    return null;

    Подключите все три узла `Inject` к входу этого узла `Function`.

    3. Верификация выхода:

    Чтобы проверить, что наш логический блок работает правильно, подключим к его выходу именованный узел `Debug`.

    * `Name`: `Команда для реле света`

    * `Output`: `complete message object`

    Проведение теста

    Теперь мы можем "проиграть" разные сценарии:

    1. Нажмите на `[Inject] Эмуляция: Освещенность 150 люкс`. Логика обновит контекст, но ничего не произойдет.

    2. Нажмите на `[Inject] Эмуляция: Движение ЕСТЬ`.

    3. Ожидаемый результат: В панели отладки от узла `Команда для реле света` появится сообщение:

            {

    "topic": "wirenboard/wb-mr6c_33/controls/K1/on",

    "payload": "1",

    "_msgid": "..."

    }

    Это доказывает, что логика сформировала правильную команду.

    1. Сначала создайте и нажмите на `[Inject] Эмуляция: Освещенность 800 люкс`.

    2. Затем нажмите на `[Inject] Эмуляция: Движение ЕСТЬ`.

    3. Ожидаемый результат: В панели отладки не появится никаких сообщений. Это доказывает, что условие `lux < 200` работает корректно.

    1. Нажмите на `[Inject] Эмуляция: Нажатие выключателя`.

    2. Ожидаемый результат: В панели отладки появится та же команда на включение. Это доказывает, что логика `ИЛИ` работает.

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

    ---

    Итоги и следующие шаги

    > 🔗 Связанный материал: В следующем уроке, COURSE-01-M08-L05, мы объединим всё воедино и проведем полную end-to-end проверку от физического входа до срабатывания реле.

    На этом уроке мы завершили предпоследний и самый интеллектуально насыщенный этап диагностики и пусконаладки. Давайте подведем итог нашего "Мини-Runbook":

  • Шаг 1: Питание. Убедились, что все устройства, включая контроллер, получают стабильное электропитание.
  • Шаг 2: Сеть. Проверили физические (Ethernet, RS-485) и логические (IP-адреса, MQTT-связь) соединения.
  • Шаг 3: Входы. Удостоверились, что сигналы от датчиков и выключателей доходят до контроллера и видны в системе.
  • Шаг 4: Логика. (Сегодняшний урок) Изолированно протестировали логические потоки Node-RED, симулируя входные сигналы и проверяя правильность формирования выходных команд.
  • Этот пошаговый, структурированный подход — залог быстрой и эффективной локализации любых проблем на объекте. Он позволяет не блуждать в потемках, хаотично проверяя все подряд, а методично сужать круг поиска, пока неисправность не будет найдена.

    Тестирование, которое мы проводили сегодня, в профессиональной разработке ПО называется "white-box" тестированием (тестирование "белого ящика"). Мы не просто подавали сигнал и смотрели на результат, мы "заглядывали внутрь" системы с помощью узлов `Debug` и `node.warn()`, отслеживая прохождение данных через все внутренние шестеренки нашего механизма. Это дает глубокое понимание работы системы и уверенность в ее корректности.

    Что дальше?

    Мы проверили входы. Мы проверили логику. Остался последний шаг — убедиться, что команды, которые формирует наша логика, правильно исполняются "в железе". В следующем уроке мы перейдем к проверке выходных цепей и исполнительных механизмов. Мы подключим наши проверенные потоки к реальным узлам (`MQTT Out`, `Modbus-Write`, `KNX Out`) и убедимся, что реле щелкают, диммеры меняют яркость, а приводы открывают клапаны. Это будет финальный аккорд в процессе сдачи объекта.