Анатомия объекта `msg`: payload, topic и другие свойства
Введение: `msg` как универсальный контейнер данных
В самом сердце Node-RED лежит простая, но чрезвычайно мощная концепция: объект `msg`. Представьте его как универсальный цифровой конверт или контейнер, который путешествует по вашему потоку автоматизации. Каждый раз, когда один узел (node) завершает свою работу, он помещает результат в этот контейнер и передает его следующему узлу через «провод» (wire). Этот непрерывный поток сообщений и есть суть работы любого сценария в Node-RED.
На техническом уровне, `msg` — это стандартный объект JavaScript. Это критически важный факт, который наделяет его невероятной гибкостью. Поскольку это обычный JavaScript-объект, вы можете:
- Добавлять в него новые свойства (например, `msg.location = "Гостиная"`).
- Изменять существующие свойства (например, преобразовывать температуру из Цельсия в Фаренгейт в `msg.payload`).
- Удалять свойства, которые больше не нужны, для экономии памяти.
- Вкладывать в него другие объекты и массивы, создавая сложные и структурированные данные.
Понимание анатомии объекта `msg` — это не просто теоретическое знание; это фундаментальный, краеугольный навык для любого инженера автоматизации, работающего с нашей платформой. Без глубокого понимания того, как формировать, читать и модифицировать `msg`, ваши потоки (flows) будут хрупкими, сложными в отладке и плохо масштабируемыми. И наоборот, овладев этим навыком, вы сможете создавать элегантные, эффективные и самодокументируемые сценарии, которые легко поддерживать и расширять.
Каждый раз, когда вы подключаете узел `Debug` к выходу другого узла, вы, по сути, просите его "вскрыть" этот контейнер `msg` и показать вам его содержимое в данный момент времени. Это ваш главный инструмент для понимания того, что происходит внутри потока.
---
`msg.payload`: Основное свойство для передачи данных
Если `msg` — это конверт, то `msg.payload` (полезная нагрузка) — это письмо внутри него. По общепринятому соглашению в сообществе Node-RED, именно это свойство используется для хранения основной, самой важной информации, которую узел хочет передать дальше. Большинство стандартных узлов по умолчанию ожидают найти входные данные именно в `msg.payload` и помещают результат своей работы туда же.
Содержимое `msg.payload` может иметь любой из базовых типов данных JavaScript, что позволяет передавать практически любую информацию.
📋 Ключевые понятия: Типы данных в `msg.payload`
- String (строка): Текстовые данные. Например, команда `"ON"` или `"OFF"`, серийный номер устройства `"A4-DA-32-8E-5C-62"`, состояние `"open"`.
- Number (число): Числовые значения. Например, показания температуры `23.5`, уровень освещенности `450` (в люксах), состояние реле `1` или `0`.
- Boolean (логический тип): Значения `true` или `false`. Идеально подходит для представления бинарных состояний: включено/выключено, открыто/закрыто, активно/неактивно.
- Buffer (буфер): Массив байт. Используется для работы с бинарными данными, например, при чтении из TCP-сокета, последовательного порта (RS-485) или при работе с файлами.
- Object (объект, JSON): Структурированные данные. Это самый мощный и рекомендуемый способ передачи данных, так как позволяет объединить несколько значений в одном сообщении.
Давайте рассмотрим примеры `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/<комната>/<группа>`.
* `msg.topic` = `"commands/light/living_room/main"`
* `msg.topic` = `"commands/light/kitchen/spots"`
* `msg.topic` = `"commands/light/bedroom/night_light"`
┌───────────────────────────┐
[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. Наша задача — преобразовать это значение в проценты и добавить информацию о местоположении датчика.
// Входящее сообщение: 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 устройства, временные метки создания сообщения или параметры протокола.
- `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
{
"payload": "read_temperature",
"config": {
"unitId": 5,
"address": 100
},
"topic": "commands/modbus/poll"
}
Резюме и лучшие практики
езюме и лучшие практики
Давайте подведем итог. Объект `msg` — это центральный элемент коммуникации в Node-RED, представляющий собой стандартный объект JavaScript. Его ключевые компоненты:
- `msg.payload`: Используется для переноса основной "полезной нагрузки" — данных.
- `msg.topic`: Содержит контекст, служит для идентификации и маршрутизации сообщения.
- `msg.meta`: Служит для хранения метаданных (информация об источнике, метки времени, версия схемы данных).
- `msg.trace`: Используется для сквозного логирования и отслеживания пути сообщения через цепочку узлов.
- Пользовательские свойства: Могут быть добавлены для передачи специфической служебной информации.
Чтобы ваша работа с `msg` была эффективной и профессиональной, придерживайтесь следующих практик: