ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Группировка и описание потоков

Группировка и описание потоков

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

Необходимость структурирования потоков Node-RED

> ℹ️ Информация: Визуальная чистота потока — это не эстетический каприз, а ключевое требование для долгосрочной стабильности и масштабируемости системы умного дома или промышленного объекта. Организованный поток напрямую влияет на стоимость владения системой, сокращая время на диагностику, отладку и внесение изменений.

В процессе разработки систем автоматизации на Node-RED инженеры часто сталкиваются с проблемой, известной как "спагетти-код" (spaghetti flow). Это состояние проекта, при котором рабочее пространство заполнено десятками узлов, соединенных хаотично пересекающимися линиями. На первый взгляд, такая система может даже работать, но ее поддержка и развитие превращаются в крайне сложную и рискованную задачу.

Последствия "спагетти-кода" для проекта автоматизации:

Визуальная организация потоков является первым и самым эффективным инструментом борьбы с этим хаосом. Логически сгруппированные и подписанные блоки позволяют "читать" архитектуру системы с первого взгляда, мгновенно идентифицируя функциональные зоны: вот блок, отвечающий за сбор данных с датчиков; вот логика управления освещением; а здесь — система безопасности.

Этот подход тесно связан со стандартами именования, которые мы рассматривали в предыдущем уроке. Если стандарт именования отвечает на вопрос "Что это за узел?" (например, `Get Living Room Temp`), то структурирование и группировка отвечают на вопрос "К какой части системы он относится и какова его роль в общей картине?" (например, узел находится внутри группы "Климат-контроль / Зона Гостиной").

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

---

Визуальная группировка логики с помощью Groups

> 💡 Подсказка: Разработайте и зафиксируйте в проектной документации единую цветовую схему для групп. Это значительно ускоряет "чтение" потоков всеми инженерами на проекте.

Groups (Группы) — это простейший, но чрезвычайно мощный инструмент для наведения визуального порядка в потоках Node-RED. Группа представляет собой цветную рамку с заголовком, в которую можно поместить несколько узлов, семантически объединяя их. Это не меняет логику работы узлов, но кардинально улучшает читаемость потока.

Создание и настройка групп

  • Откройте меню в правом верхнем углу редактора Node-RED (иконка "гамбургер").
  • Выберите пункт `Groups -> Add group`. На рабочем поле появится пустая рамка.
  • Дважды щелкните по рамке, чтобы открыть окно ее настроек. Здесь вы можете:
  • * Name: Задать осмысленный заголовок для группы.

    * Appearance: Настроить цвет заливки (`Fill`) и рамки (`Border`).

  • Перетащите необходимые узлы внутрь рамки. Они будут "привязаны" к группе и будут перемещаться вместе с ней.
  • Изменяйте размер группы, потянув за правый нижний угол.
  • Практика: цветовое кодирование функциональных блоков

    Правильное использование цветов превращает ваш поток в интуитивно понятную карту. Рекомендуется придерживаться единого стандарта в рамках всех проектов.

    | Цвет группы | Назначение блока | Пример |

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

    | Синий | Внешние интерфейсы: Взаимодействие с внешним миром. | MQTT-брокеры, HTTP-запросы, Modbus-опросы, DALI-шины, TCP/UDP. |

    | Зеленый | Основная бизнес-логика: Ядро автоматизации. | Сценарии управления, конечные автоматы (FSM), сложные вычисления, таймеры. |

    | Желтый | Преобразование и валидация: Подготовка и очистка данных. | Парсинг JSON, проверка диапазонов значений, форматирование, приведение типов. |

    | Красный | Критические операции: Действия, требующие особого внимания. | Запись в EEPROM, управление реле безопасности, централизованная обработка ошибок. |

    | Серый | Вспомогательные/Отладочные: Логирование, комментарии. | Узлы `Debug`, блоки `Comment`, тестовые `Inject`. |

    Практический пример:

    Представим задачу опроса трехфазного электросчетчика по шине RS-485 (Modbus RTU) и отправки данных в MQTT.

  • Создайте группу и назовите ее `Электросчетчик WB-MAP3E (Modbus ID:42)`.
  • Задайте ей синий цвет заливки, так как это блок взаимодействия с внешним оборудованием.
  • Поместите внутрь группы следующие узлы:
  • * Узел `Inject`, настроенный на запуск каждые 15 секунд.

    * Узел `Modbus-Getter` для чтения регистров напряжения (VA, VB, VC).

    * Еще один `Modbus-Getter` для чтения регистров мощности (PA, PB, PC).

    * Узел `Function` с названием `Format Power Metrics`, который принимает данные от обоих геттеров (с помощью узла `Join`), валидирует их и формирует единый JSON-объект.

    * Узел `mqtt out`, который публикует итоговый JSON в топик `telemetry/main_switchboard/power_meter`.

    В результате у вас получится аккуратный, самодостаточный блок, назначение которого понятно с первого взгляда. Если потребуется изменить Modbus ID или MQTT топик, вы точно будете знать, где искать соответствующие узлы.

    ---

    Декомпозиция и переиспользование логики через Subflows

    Subflows (Подпотоки) — это механизм декомпозиции и абстракции в Node-RED. В отличие от групп, которые являются лишь средством визуальной организации, подпотоки позволяют инкапсулировать повторяющуюся логику в отдельный, переиспользуемый узел, который можно рассматривать как "черный ящик".

    | Критерий | Group (Группа) | Subflow (Подпоток) |

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

    | Назначение | Визуальная организация на текущей вкладке | Функциональная абстракция и переиспользование по всему проекту |

    | Логика | Узлы внутри остаются частью основного потока | Узлы "спрятаны" внутри, сам подпоток становится новым узлом |

    | Переиспользование | Нет. Только копирование всей группы (плохая практика) | Да. Может быть использован многократно на разных вкладках |

    | Параметризация | Нет. Все настройки жестко заданы в узлах | Да. Через переменные окружения (`Environment Variables`) |

    | Сложность | Простое создание и редактирование | Требует проектирования входов, выходов и интерфейса (свойств) |

    Создание и параметризация подпотока

  • Выделите узлы, логику которых вы хотите инкапсулировать.
  • В меню выберите `Subflows -> Selection to subflow`.
  • Выделенные узлы исчезнут, а на их месте появится новый узел подпотока. В панели палитры также появится новый раздел "subflows" с вашим созданным узлом.
  • Дважды щелкните по узлу, чтобы отредактировать его. Вы перейдете в отдельное рабочее пространство, где можно редактировать "внутренности" подпотока.
  • Нажмите кнопку `Edit subflow properties`. Здесь вы можете:
  • * Задать имя и иконку.

    * Настроить количество входов и выходов.

    * Во вкладке `Environment Variables` определить параметры, которые можно будет настраивать для каждого экземпляра подпотока. Это ключ к созданию гибких компонентов.

    Практический пример: Subflow "Standard Error Handler"

    Как мы уже знаем из урока по обработке ошибок, каждый поток должен иметь механизм логирования. Дублировать эту логику везде — плохая практика. Создадим для этого переиспользуемый подпоток `standardErrorHandler`.

    Шаг 1: Проектирование * `LOG_LEVEL`: Уровень важности ошибки (e.g., `CRITICAL`, `WARNING`).

    * `NOTIFY_USER`: ID пользователя в Telegram для отправки уведомления.

    Шаг 2: Создание

    Создадим временный поток: `Function` -> `mysql` -> `telegram sender`. Выделим эти узлы и преобразуем их в подпоток с именем `standardErrorHandler`.

    Шаг 3: Реализация логики внутри подпотока

    Внутри подпотока `standardErrorHandler` настроим узлы:

  • Узел `Function` "Format Error Message":
  • Этот узел подготовит данные для записи в лог и отправки.

        // msg от узла Catch содержит msg.error

    const errorInfo = msg.error;

    const originalMsg = errorInfo.message; // Исходное сообщение, вызвавшее ошибку

    // Получаем переменные окружения подпотока

    const logLevel = env.get("LOG_LEVEL") || "ERROR";

    const notifyUser = env.get("NOTIFY_USER");

    const errorMessage = `[${logLevel}] в узле "${errorInfo.source.name}" (${errorInfo.source.type}): ${errorInfo.source.id}. Причина: ${errorInfo.text}`;

    // Готовим сообщение для отправки в Telegram

    msg.payload = {

    chatId: notifyUser,

    type: 'message',

    content: errorMessage

    };

    // Готовим объект для записи в БД

    msg.query = "INSERT INTO audit_log (level, source_node, message, original_payload) VALUES (?, ?, ?, ?)";

    msg.params = [

    logLevel,

    errorInfo.source.id,

    errorInfo.text,

    JSON.stringify(originalMsg) // Сохраняем исходный payload для анализа

    ];

    // Узел Function имеет 2 выхода.

    // На первый отправляем payload для Telegram, на второй - запрос к MySQL.

    return [msg, {query: msg.query, payload: msg.params}];

  • Узел `telegram sender`: Подключен к первому выходу `Function`. Он просто отправит `msg.payload`.
  • Узел `mysql`: Подключен ко второму выходу `Function`. Он выполнит SQL-запрос из `msg.query` с параметрами из `msg.payload`.
  • Теперь в любом потоке проекта можно просто перетащить наш узел `standardErrorHandler` из палитры, подключить его к узлу `Catch` и в свойствах указать `LOG_LEVEL` и `NOTIFY_USER`. Вся сложная логика форматирования и отправки инкапсулирована.

    ---

    Документирование: вкладка 'Description' и синтаксис Markdown

    > ⚠️ Внимание: Отсутствие описаний превращает отладку проекта спустя 6 месяцев в сложный квест. Документация, написанная сегодня, — это сэкономленные часы работы в будущем. Любой нетривиальный узел, группа или подпоток без описания является техническим долгом.

    Каждый элемент в Node-RED — узел, группа, подпоток и даже целая вкладка (flow) — имеет встроенную возможность для документирования. Это поле `Description` на панели Info (иконка в виде книги на боковой панели справа).

    Эта панель поддерживает синтаксис Markdown, что позволяет создавать структурированную и легко читаемую документацию прямо внутри проекта.

    Основы синтаксиса Markdown для Node-RED

    `Курсивный текст*` или `_Курсивный текст_` ` Элемент маркированного списка` msg.payload = { "status": "ok" };

    return msg;

    `

    Обязательные пункты для документирования

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

  • Назначение: Краткое описание, что делает этот блок. Какую бизнес-задачу он решает?
  • Входной контракт: Описание ожидаемой структуры объекта `msg`, особенно `msg.payload`. Какие свойства обязательны?
  • Выходной контракт: Описание структуры `msg` на выходе из блока при успешном выполнении.
  • Обработка ошибок: Как блок ведет себя в случае ошибки? Генерирует ли он ошибку для узла `Catch`? Что содержит `msg.error`?
  • Зависимости: Зависит ли этот блок от переменных контекста (`flow` или `global`)? Ожидает ли он данных от какого-то конкретного другого потока?
  • Параметры (для Subflows): Описание каждой переменной окружения.
  • Автор и дата: Кто и когда последний раз вносил значимые изменения.
  • Пример описания для подпотока обработки датчика

    Предположим, у нас есть подпоток `Process Temp Sensor`, который обрабатывает сырые данные с датчика DS18B20. Его описание на вкладке "Info" может выглядеть так:

    `

    # Подпоток: Process Temp Sensor (FLOW-SENS-TEMP-001)

    Автор: А. Иванов Дата изменения: 2023-10-26

    1. Назначение

    Этот подпоток предназначен для обработки сырых показаний температуры с датчиков 1-Wire (DS18B20), их валидации и преобразования в стандартный формат `msg` "Контракт сообщения".

    2. Входной контракт (Input)

    Ожидается `msg`, где `msg.payload` является строкой, полученной от узла `file in` (читающего файл `/sys/bus/w1/devices/.../w1_slave`).

    t=23125

    
    

    3. Выходной контракт (Output)

    При успешной обработке на выход подается `msg` в стандартном формате.

    `msg.payload` (JSON):
    json

    {

    "value": 23.1,

    "source": "28-01234567abcd",

    "ts": 1678886400000,

    "unit": "°C"

    }

    
    

    4. Обработка ошибок

    В случае невозможности распарсить значение или если оно выходит за допустимые пределы (-55°C до +125°C), подпоток генерирует ошибку через `node.error()` и возвращает `null`, останавливая поток.

    5. Параметры (Environment Variables)

    • `SENSOR_ID` (string): Уникальный ID датчика (например, `28-01234567abcd`), используемый для поля `source` в выходном сообщении.
    `

    Такое описание превращает подпоток из "черного ящика" в понятный и документированный компонент системы.

    ---

    Сводка и лучшие практики организации потоков

    Грамотная организация потоков Node-RED — это залог создания профессиональных, надежных и легко поддерживаемых систем автоматизации. В этом уроке мы рассмотрели три кита структурирования: группы, подпотоки и документирование.

    Чек-лист "здорового" потока

    Проверьте свои потоки на соответствие этим критериям:

  • [ ] Именование: Все узлы, группы и потоки имеют осмысленные имена согласно стандартам проекта (рассмотрено в `COURSE-06-M06-L01`).
  • [ ] Группировка: Логически связанные узлы объединены в группы с соответствующими заголовками и цветовой кодировкой.
  • [ ] Отсутствие дублирования: Повторяющиеся последовательности узлов (3 и более) вынесены в параметризованные подпотоки.
  • [ ] Документация: Все нетривиальные узлы `Function`, все группы и подпотоки имеют подробное описание на вкладке `Info` с указанием назначения, контрактов `msg` и зависимостей.
  • [ ] Читаемость: Поток легко читается сверху вниз и слева направо. Отсутствуют длинные, пересекающиеся линии (используйте узлы `Link In/Out` для их устранения).
  • [ ] Размер: Поток на одной вкладке не разрастается до сотен узлов. Сложная система декомпозирована на несколько логических вкладок (например, "Освещение", "Климат", "Безопасность").
  • Анти-паттерны (Как делать НЕ надо)

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

    Что дальше

    В следующем уроке мы перейдем к одной из самых мощных концепций в Node-RED — управлению состоянием. Мы изучим, как использовать переменные контекста (`flow` и `global`) для создания "умных" сценариев, которые помнят свое состояние между событиями, и как реализовать паттерн "Конечный автомат" (FSM) для управления сложными системами, такими как климат-контроль или система полива.