ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Анатомия объекта `msg`: payload, topic и другие свойства

Анатомия объекта `msg`: payload, topic и другие свойства

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

Введение: `msg` как универсальный контейнер данных

В самом сердце Node-RED лежит простая, но чрезвычайно мощная концепция: объект `msg`. Представьте его как универсальный цифровой конверт или контейнер, который путешествует по вашему потоку автоматизации. Каждый раз, когда один узел (node) завершает свою работу, он помещает результат в этот контейнер и передает его следующему узлу через «провод» (wire). Этот непрерывный поток сообщений и есть суть работы любого сценария в Node-RED.

На техническом уровне, `msg` — это стандартный объект JavaScript. Это критически важный факт, который наделяет его невероятной гибкостью. Поскольку это обычный JavaScript-объект, вы можете:

Понимание анатомии объекта `msg` — это не просто теоретическое знание; это фундаментальный, краеугольный навык для любого инженера автоматизации, работающего с нашей платформой. Без глубокого понимания того, как формировать, читать и модифицировать `msg`, ваши потоки (flows) будут хрупкими, сложными в отладке и плохо масштабируемыми. И наоборот, овладев этим навыком, вы сможете создавать элегантные, эффективные и самодокументируемые сценарии, которые легко поддерживать и расширять.

Каждый раз, когда вы подключаете узел `Debug` к выходу другого узла, вы, по сути, просите его "вскрыть" этот контейнер `msg` и показать вам его содержимое в данный момент времени. Это ваш главный инструмент для понимания того, что происходит внутри потока.

---

`msg.payload`: Основное свойство для передачи данных

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

Содержимое `msg.payload` может иметь любой из базовых типов данных JavaScript, что позволяет передавать практически любую информацию.

📋 Ключевые понятия: Типы данных в `msg.payload`

Давайте рассмотрим примеры `msg.payload` в контексте задач автоматизации на нашей платформе:

| Задача / Протокол | Пример `msg.payload` | Тип данных | Пояснение |

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

| Включение/выключение света через реле контроллера | `true` или `false` | `Boolean` | Прямое управление выходом `HI I/O Nodes`. |

| Команда от MQTT-клиента | `"ON"` | `String` | Часто используется для совместимости с различными MQTT-панелями и приложениями. |

| Данные с датчика температуры/влажности (Modbus) | ```json { "value": 24.1, "unit": "°C" } ``` | `Object (JSON)` | Стандартизированный формат, который несет и значение, и единицу измерения. |

| Состояние дискретного входа ("сухой контакт") | `1` или `0` | `Number` | Результат от узла `HI I/O Nodes` для универсального входа в режиме DI. |

| Пакетные данные с нескольких датчиков | ```json [{"id":"temp","v":22},{"id":"hum","v":45}] ``` | `Array` | Эффективная передача множества значений от одного устройства. |

Критически важно поддерживать консистентность (единообразие) типов данных. Если узел `Switch` ожидает получить в `msg.payload` число `1`, а ему приходит строка `"1"`, он может не сработать, как ожидалось. Всегда проверяйте в отладчике, какой тип данных приходит от узла, и при необходимости преобразуйте его к нужному формату с помощью узла `Change` или `Function`.

---

`msg.topic`: Контекст, фильтрация и маршрутизация сообщений

В то время как `msg.payload` несет что (данные), свойство `msg.topic` отвечает на вопрос о чем (контекст). Это текстовая метка, которая описывает происхождение, назначение или тип данных в `payload`. Игнорирование `msg.topic` — одна из самых частых ошибок начинающих разработчиков, ведущая к созданию запутанных и трудночитаемых потоков.

Основное назначение `msg.topic` — маршрутизация сообщений. Представьте, что у вас есть десять датчиков температуры в разных комнатах. Все они отправляют показания в `msg.payload`. Как понять, какая температура откуда пришла? Именно для этого и нужен `topic`.

| Источник данных | Пример `msg.topic` |

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

| Датчик температуры в гостиной | `telemetry/living_room/temperature` |

| Датчик влажности в ванной | `telemetry/bathroom/humidity` |

| Команда включения света | `command/kitchen/light/main` |

| Статус протечки воды | `status/security/water_leak_detector_1` |

Такая структура, похожая на используемую в MQTT, позволяет очень эффективно фильтровать и направлять сообщения с помощью узла `Switch`.

Практический пример: Маршрутизация команд освещения

Предположим, у нас есть один MQTT-вход, который принимает команды для управления светом во всем доме. Команды приходят в топик вида `commands/light/<комната>/<группа>`.

  • Узел `mqtt in` подписывается на `commands/light/#` (используя wildcard).
  • На выходе мы получаем сообщения с разными `msg.topic`, например:
  • * `msg.topic` = `"commands/light/living_room/main"`

    * `msg.topic` = `"commands/light/kitchen/spots"`

    * `msg.topic` = `"commands/light/bedroom/night_light"`

  • Далее мы ставим узел `Switch`, настроенный на проверку свойства `msg.topic`.
  •                            ┌───────────────────────────┐

    [mqtt in]─────────────>│ Switch (by msg.topic) ├─[1]──> (Логика света в гостиной)

    (commands/light/#) │ ├─[2]──> (Логика света на кухне)

    │ - ends with "main" ├─[3]──> (Логика ночника в спальне)

    │ - ends with "spots" │

    │ - ends with "night_light" └─[4]──> (Неизвестная команда, лог)

    └───────────────────────────┘

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

    > 💡 Подсказка: Используйте `msg.topic` для создания самодокументируемых потоков. Глядя на `topic` в отладчике, вы или ваши коллеги должны сразу понимать, о каких данных идет речь, без необходимости открывать каждый узел в цепочке.

    ---

    Практика: Создание и модификация `msg` в узле `Function`

    Узел `Function` — это ваш швейцарский нож для работы с объектом `msg`. Он позволяет писать произвольный код на JavaScript для выполнения любых, даже самых сложных преобразований данных. Внутри узла `Function` объект `msg` доступен как обычная переменная с тем же именем.

    Пример 1: Преобразование и обогащение данных

    Предположим, на универсальный вход `UI-05` контроллера подключен аналоговый датчик освещенности, который выдает значение от 0 до 1023. Наша задача — преобразовать это значение в проценты и добавить информацию о местоположении датчика.

  • С выхода узла, читающего показания с `UI-05`, мы получаем сообщение, где `msg.payload` — это число (например, `768`).
  • Мы передаем это сообщение в узел `Function` со следующим кодом:
  • // Входящее сообщение: msg.payload = 768 (тип Number)
    
    

    // 1. Читаем сырое значение из payload

    let rawValue = msg.payload;

    // 2. Проводим валидацию. Если значение некорректно, логируем ошибку и останавливаем поток.

    if (typeof rawValue !== 'number' || rawValue < 0 || rawValue > 1023) {

    node.error("Некорректное значение от датчика освещенности: " + rawValue, msg);

    return null; // Возвращаем null, чтобы прервать цепочку

    }

    // 3. Масштабируем значение в проценты (0-100)

    let lightLevelPercent = (rawValue / 1023) * 100;

    // 4. Формируем новый, структурированный payload в соответствии с контрактом сообщения

    msg.payload = {

    value: parseFloat(lightLevelPercent.toFixed(1)), // Округляем до 1 знака после запятой

    unit: "%"

    };

    // 5. Создаем и заполняем пользовательские свойства для контекста

    msg.location = "office_main";

    msg.device_id = "light_sensor_ceiling_1";

    // 6. Устанавливаем topic для дальнейшей маршрутизации

    msg.topic = "telemetry/office_main/light_level";

    // 7. Обязательно возвращаем измененный объект msg

    return msg;

    После выполнения этого кода на выход узла `Function` придет полностью преобразованный объект `msg`:

    {
    

    "payload": {

    "value": 75.1,

    "unit": "%"

    },

    "location": "office_main",

    "device_id": "light_sensor_ceiling_1",

    "topic": "telemetry/office_main/light_level",

    "_msgid": "a1b2c3d4.5e4f32"

    }

    Этот новый `msg` готов для дальнейшей обработки: его можно отправить в MQTT, сохранить в базу данных MySQL или использовать для управления диммером.

    > ⚠️ Внимание: Всегда возвращайте объект `msg` в конце вашего кода в узле `Function` (`return msg;`). Если функция вернет `null` или ничего (`undefined`), то цепочка сообщений на этом прервется, и `msg` не пойдет к следующим узлам. Чтобы отправить несколько сообщений из одного узла, верните массив объектов `msg`, например: `return [msg1, msg2];`.

    ---

    Другие стандартные и пользовательские свойства `msg`

    ругие стандартные и пользовательские свойства `msg`

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

    `_msgid`

    Это уникальный идентификатор, который Node-RED автоматически присваивает каждому сообщению в момент его создания (например, в узле `Inject` или `mqtt in`). Это свойство чрезвычайно полезно для отладки. Когда вы в панели `Debug` видите несколько сообщений, вы можете по `_msgid` однозначно проследить путь одного конкретного сообщения через разные точки потока.

    Свойства метаданных и трассировки

    Для соблюдения "контракта сообщения" и обеспечения прозрачности работы сложных систем, в `msg` часто используются следующие свойства:

    • `msg.meta`: Объект, содержащий метаданные о источнике сообщения или контексте его возникновения. Сюда могут входить ID устройства, временные метки создания сообщения или параметры протокола.
    > Пример: При получении данных от датчика через MQTT, полезно сохранить оригинальный топик или `client_id` именно в `msg.meta`. Если в дальнейшем узел `Function` изменит `payload` (например, переведет Вольты в Амперы), метаданные в `msg.meta` позволят пост-обработке понять, "чьи" это данные, не заглядывая в тело сообщения.
    • `msg.trace`: Массив или объект, используемый для хранения истории прохождения сообщения через узлы. Это позволяет восстановить всю цепочку изменений и логику принятия решений внутри потока.
    > Пример: В сложной логике выбора сценария отопления каждый узел-фильтр может добавлять запись в массив: `msg.trace.push("Mode: Economy")`. Если на выходе мы получим неожиданный результат, `msg.trace` покажет, какие именно условия были пройдены, превращая объект сообщения в самодокументированный лог.

    Свойства, добавляемые специфическими узлами

    Некоторые узлы добавляют в `msg` свои собственные, специфические свойства, необходимые для их работы.

    • HTTP In: Узел, создающий HTTP-эндпоинт, добавляет в `msg` свойства `msg.req` (объект запроса) и `msg.res` (объект ответа). Это позволяет внутри потока получить доступ к параметрам запроса (URL, заголовки, тело) и сформировать HTTP-ответ.
    • TCP In / Serial In: Эти узлы часто помещают полученные бинарные данные в `msg.payload` в виде объекта Buffer.
    • Catch: Узел для перехвата ошибок добавляет свойство `msg.error`, содержащее информацию о возникшей ошибке, включая текст, источник и оригинальное сообщение, вызвавшее сбой.
    • Split: Узел, разбивающий массив на последовательность сообщений, добавляет `msg.parts`, который содержит информацию об общем количестве частей и индексе текущей части.

    > 🔗 Связанный материал: Работа с `msg.req` и `msg.res` для создания кастомных API-эндпоинтов на контроллере HI подробно рассматривается в уроке COURSE-08-M01-L04: "Создание HTTP API".

    Пользовательские свойства

    Как мы видели в примере с узлом `Function`, вы можете создавать любые собственные свойства для своих нужд. Это мощный инструмент для передачи метаданных, промежуточных вычислений или флагов состояния между узлами без "загрязнения" `msg.payload`.

    Пример: Представим поток, который опрашивает несколько однотипных Modbus-устройств. Вместо того чтобы создавать отдельный узел `Modbus-Getter` для каждого, можно сделать один универсальный поток.
  • Узел `Inject` создает сообщение, где `msg.payload` — это команда, а в пользовательском свойстве `msg.config` передаются параметры устройства.
  •     // Сообщение от Inject

    {

    "payload": "read_temperature",

    "config": {

    "unitId": 5,

    "address": 100

    },

    "topic": "commands/modbus/poll"

    }

  • Узел `Function` читает `msg.config` и на его основе динамически настраивает параметры для следующего узла `Modbus-Getter`.
  • Таким образом, управляя лишь содержимым `msg.config`, мы можем одним и тем же потоком опрашивать десятки разных устройств.
  • Резюме и лучшие практики

    езюме и лучшие практики

    Давайте подведем итог. Объект `msg` — это центральный элемент коммуникации в Node-RED, представляющий собой стандартный объект JavaScript. Его ключевые компоненты:

    • `msg.payload`: Используется для переноса основной "полезной нагрузки" — данных.
    • `msg.topic`: Содержит контекст, служит для идентификации и маршрутизации сообщения.
    • `msg.meta`: Служит для хранения метаданных (информация об источнике, метки времени, версия схемы данных).
    • `msg.trace`: Используется для сквозного логирования и отслеживания пути сообщения через цепочку узлов.
    • Пользовательские свойства: Могут быть добавлены для передачи специфической служебной информации.

    Чтобы ваша работа с `msg` была эффективной и профессиональной, придерживайтесь следующих практик:

  • Используйте `Debug` на полную мощность. Всегда используйте узел `Debug` для инспекции. В его настройках переключите `Output` с `msg.payload` на `complete message object`. Это позволит вам видеть всю структуру `msg` целиком, включая `topic`, `meta`, `trace` и `_msgid`, что кардинально упрощает отладку.
  • Соблюдайте гигиену `msg`. Контроллер HI имеет ограниченный объем оперативной памяти (4 ГБ). Не перегружайте объект `msg` большими объемами данных, которые не нужны на последующих этапах потока. Если вы использовали какое-то свойство для промежуточных вычислений, удалите его, как только оно станет ненужным. Это можно сделать в узле `Function` (`delete msg.temp_property;`) или с помощью узла `Change`.
  • Стандартизируйте! В рамках одного проекта разработайте и строго придерживайтесь единого стандарта именования для `msg.topic` (например, `тип/расположение/параметр`) и структуры `msg.payload` (так называемый "контракт сообщений"). Обязательно используйте `msg.meta` для передачи контекста данных — это сделает ваши потоки предсказуемыми, понятными для коллег и готовыми к масштабированию.