Мини-Runbook: Шаг 4 (Проверка логики)
Введение в тестирование логики
> 🔗 Связанный материал: Этот урок является прямым продолжением урока COURSE-01-M08-L03, где мы убедились, что все входные сигналы корректно поступают в систему. Теперь наша задача — проверить, что система на них правильно реагирует.
Проверка логики — это четвертый и ключевой этап в нашем "Мини-Runbook" по диагностике и сдаче объекта. Последовательность шагов (1. Питание → 2. Сеть → 3. Входы → 4. Логика) выбрана не случайно. Она основана на принципе пошаговой изоляции неисправностей. Если мы убедились, что оборудование получает питание, имеет стабильное сетевое соединение и корректно передает данные (например, нажатие кнопки видно в MQTT-брокере), но конечный результат (включение света) отсутствует, то с вероятностью 99% проблема находится именно в логическом слое — в потоках Node-RED.
Основной принцип тестирования логики — тестирование в изоляции. Это означает, что мы временно отключаем наши потоки от физического мира (реальных датчиков и исполнительных устройств) и проверяем их работу в "лабораторных" условиях. Зачем это нужно?
Частые логические ошибки
В процессе создания потоков инженеры, особенно на начальном этапе, сталкиваются с типовыми ошибками, которые легко обнаружить при изолированном тестировании:
- Неверные условия в узлах `Switch`: Самая частая ошибка. Например, для датчика освещенности выставляется условие "если `msg.payload` < 200", но из MQTT приходит строка `"150"`. Узел `Switch`, ожидающий число, не обработает это условие. Или путаница между операторами `>` (больше) и `>=` (больше или равно), которая может быть критичной для уставки температуры.
- Некорректная работа с контекстом: Использование `global` контекста там, где достаточно `flow`, что может привести к конфликтам между разными потоками. Другой пример — логика опирается на значение из контекста (`flow.get("isNight")`), но это значение не инициализируется при старте контроллера, что приводит к непредсказуемому поведению до первого срабатывания соответствующего сценария.
- Ошибки преобразования типов данных: Логика ожидает на входе булево значение (`true`/`false`), а с MQTT-топика приходит строка (`"ON"`/`"OFF"`) или число (`1`/`0`). Без явного узла-преобразователя поток "ломается". Это прямое нарушение паттерна "Контракт сообщения", который требует стандартизации данных на входах в систему.
Инструменты для отладки логики в 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`:- Payload: `boolean` : `true`
- Topic: (опционально, если логика использует топик для маршрутизации) `knx/command/living_room/light`
- Name: `Эмуляция: Кнопка Гостиная (ON)`
Теперь каждый клик по этому узлу `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`:- Payload: `JSON`
- Содержимое редактора JSON:
{
"temperature": -5.2,
"humidity": 88,
"pressure": 1021,
"wind_speed": 4.5,
"is_raining": true
}
- Name: `Эмуляция: ИДЕТ ДОЖДЬ`
Подключив этот узел к вашему потоку, вы можете одним кликом проверить, что логика закрытия окон срабатывает корректно, без необходимости ждать реального дождя.
---
Анализ потока данных с помощью Debug и Function
> ⚠️ Внимание: Никогда не оставляйте активные узлы `Debug`, настроенные на вывод полного объекта сообщения (`complete message object`), в промышленной системе. На объектах с высокой частотой сообщений (например, мониторинг энергии каждую секунду) это создает избыточную нагрузку на процессор контроллера, может переполнять журнал и замедлять работу всей логики.
Если узел `Inject` — это наш симулятор, то узел `Debug` — это наш микроскоп. Он позволяет в деталях рассмотреть объект `msg`, который является "кровью" системы автоматизации, на любом участке его пути.
Эффективное использование узла `Debug`
Просто "кинуть" узел `Debug` на поток — недостаточно. Эффективная отладка требует правильной его настройки.
- Вывод `msg.payload` vs. `complete message object`:
* `complete message object`: Режим для глубокого анализа. Он показывает не только `payload`, но и `topic`, `_msgid` (уникальный идентификатор сообщения), а также любые другие свойства, которые были добавлены в объект `msg` предыдущими узлами. Это незаменимо для отладки сложной маршрутизации по `msg.topic` или когда нужно понять, почему данные теряются между узлами.
- Именование узлов `Debug`:
| Плохое имя | Хорошее имя |
| :--- | :--- |
| debug 1 | `Debug: После MQTT In` |
| debug 2 | `Debug: После валидации` |
| debug 3 | `Debug: Перед записью в реле` |
| debug 4 | `Debug: Команда для KNX` |
Такой подход позволяет мгновенно идентифицировать в журнале, из какой точки потока пришло сообщение.
Продвинутая отладка с помощью узла `Function`
Иногда простой вывод `msg` недостаточен. Нужно вывести в журнал кастомное сообщение или проверить значение переменной, не прерывая поток. Для этого идеально подходит узел `Function` и его встроенные методы `node.warn()` и `node.error()`.
- `node.log("сообщение")`: Выводит обычное лог-сообщение (аналогично `console.log`).
- `node.warn("сообщение")`: Выводит предупреждение (в панели отладки выделяется желтым цветом). Ключевое преимущество: не требует подключения узла `Debug` и не прерывает поток.
- `node.error("сообщение", msg)`: Генерирует ошибку (выделяется красным цветом) и останавливает поток. Эта ошибка может быть поймана узлом `Catch` для централизованной обработки.
Представим поток, где мы получаем температуру, преобразуем ее и формируем команду.
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"`). Это позволяет отслеживать трансформацию данных на каждом шаге.
---
Пример: Сквозное тестирование логики управления освещением
Давайте объединим полученные знания и проведем полное тестирование логики для популярного сценария "Вечерний свет".
Требования к сценарию: Основной свет в гостиной должен включаться, если:Построение тестового стенда в Node-RED
Для проверки этой логики нам не нужно реальное оборудование. Мы создадим его виртуальные копии.
1. Симуляция входов:Нам понадобится три узла `Inject`, которые будут эмулировать наши датчики и выключатель.
- `[Inject] Датчик движения (ЕСТЬ)`:
* `Payload`: `boolean` : `true`
* `Topic`: `sensor/motion/living_room`
- `[Inject] Датчик освещенности (ТЕМНО)`:
* `Payload`: `number` : `150`
* `Topic`: `sensor/lux/living_room`
- `[Inject] Настенный выключатель`:
* `Payload`: `boolean` : `true`
* `Topic`: `switch/wall/living_room/set`
2. Логический блок:Реализуем логику `(движение И темно) ИЛИ выключатель`. Проще всего это сделать в узле `Function`, который будет хранить состояния входов в контексте потока (`flow context`).
- `[Function] Логика "Вечерний свет"`:
// Получаем текущие состояния из контекста, если их нет - инициализируем
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`.
- `[Debug] Выходная команда`:
* `Output`: `complete message object`
Проведение теста
Теперь мы можем "проиграть" разные сценарии:
- Тест 1: Движение в темноте.
2. Нажмите на `[Inject] Эмуляция: Движение ЕСТЬ`.
3. Ожидаемый результат: В панели отладки от узла `Команда для реле света` появится сообщение:
{
"topic": "wirenboard/wb-mr6c_33/controls/K1/on",
"payload": "1",
"_msgid": "..."
}
Это доказывает, что логика сформировала правильную команду.
- Тест 2: Движение днем.
2. Затем нажмите на `[Inject] Эмуляция: Движение ЕСТЬ`.
3. Ожидаемый результат: В панели отладки не появится никаких сообщений. Это доказывает, что условие `lux < 200` работает корректно.
- Тест 3: Нажатие выключателя днем.
2. Ожидаемый результат: В панели отладки появится та же команда на включение. Это доказывает, что логика `ИЛИ` работает.
Таким образом, мы полностью проверили работу сложного сценария, не имея под рукой ни одного физического устройства.
---
Итоги и следующие шаги
> 🔗 Связанный материал: В следующем уроке, COURSE-01-M08-L05, мы объединим всё воедино и проведем полную end-to-end проверку от физического входа до срабатывания реле.
На этом уроке мы завершили предпоследний и самый интеллектуально насыщенный этап диагностики и пусконаладки. Давайте подведем итог нашего "Мини-Runbook":
Этот пошаговый, структурированный подход — залог быстрой и эффективной локализации любых проблем на объекте. Он позволяет не блуждать в потемках, хаотично проверяя все подряд, а методично сужать круг поиска, пока неисправность не будет найдена.
Тестирование, которое мы проводили сегодня, в профессиональной разработке ПО называется "white-box" тестированием (тестирование "белого ящика"). Мы не просто подавали сигнал и смотрели на результат, мы "заглядывали внутрь" системы с помощью узлов `Debug` и `node.warn()`, отслеживая прохождение данных через все внутренние шестеренки нашего механизма. Это дает глубокое понимание работы системы и уверенность в ее корректности.
Что дальше?
Мы проверили входы. Мы проверили логику. Остался последний шаг — убедиться, что команды, которые формирует наша логика, правильно исполняются "в железе". В следующем уроке мы перейдем к проверке выходных цепей и исполнительных механизмов. Мы подключим наши проверенные потоки к реальным узлам (`MQTT Out`, `Modbus-Write`, `KNX Out`) и убедимся, что реле щелкают, диммеры меняют яркость, а приводы открывают клапаны. Это будет финальный аккорд в процессе сдачи объекта.