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

`msg.payload`: основной контейнер данных

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

Роль msg.payload в потоках Node-RED

В экосистеме Node-RED объект сообщения `msg` является универсальной единицей информации, кровью, циркулирующей по артериям потоков. Но если сам `msg` — это конверт, то `msg.payload` — это его основное содержимое. Именно в этом свойстве, по общепринятому соглашению, передаются полезные данные от одного узла к другому.

> 🔗 Связанный материал: Полная структура объекта `msg`, включая `topic` и другие стандартные свойства, подробно рассмотрена в предыдущем уроке "Анатомия объекта `msg`: payload, topic и другие свойства" (COURSE-06-M02-L01).

Большинство стандартных и сторонних узлов в Node-RED спроектированы так, чтобы по умолчанию считывать данные из `msg.payload` и записывать результат своей работы также в `msg.payload`. Например:

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

Отличие данных от метаданных

Важно понимать различие между `payload` и другими свойствами объекта `msg`, такими как `topic`, `_msgid`, `qos`, `parts` и т.д. Эти свойства несут метаданные — информацию о данных, а не сами данные.

> ℹ️ Информация: Хотя `msg.payload` является стандартом де-факто, многие узлы позволяют изменить это поведение. Например, в узле `mqtt out` можно указать, чтобы данные для отправки брались не из `payload`, а из другого свойства, скажем, `msg.data`. Это может быть полезно в сложных сценариях, когда нужно сохранить оригинальный `payload` неизменным, но не является стандартной практикой и должно использоваться с осторожностью и хорошим комментированием.

Соблюдение этого простого правила — данные в `payload`, контекст в `topic` — является фундаментом для построения надежных и легко поддерживаемых систем автоматизации на платформе HI.

---

Основные типы данных в msg.payload

Поскольку Node-RED построен на базе Node.js, `msg.payload` может содержать любой тип данных, поддерживаемый JavaScript. Понимание этих типов и их правильное применение — ключ к созданию предсказуемых потоков.

| Тип данных | Описание и применение | Пример в `msg.payload` |

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

| String (Строка) | Текстовые данные. Используется для передачи команд (`"ON"`, `"OFF"`), статусов (`"open"`, `"closed"`), имен, а также для передачи данных в формате JSON (сериализованный объект). | `"Hello, World!"` или `"{\"temp\":21.5}"` |

| Number (Число) | Числовые значения, как целые (Integer), так и с плавающей точкой (Float). Основной тип для показаний датчиков: температура, влажность, давление, уровень CO2, показания счетчиков. | `23.7` или `1500` |

| Boolean (Булево) | Логические значения `true` (истина) или `false` (ложь). Идеально подходит для представления бинарных состояний: включено/выключено, открыто/закрыто, есть протечка/нет протечки. | `true` |

| Object (Объект) | Структурированные данные в формате ключ-значение (JSON). Самый мощный и рекомендуемый тип для передачи нескольких связанных параметров в одном сообщении. Позволяет создавать читаемые и расширяемые контракты сообщений. | `{"value": 25.1, "unit": "°C", "ts": 1678886400}` |

| Array (Массив) | Упорядоченный список значений. Полезен для передачи серии показаний, списка устройств или результатов, полученных от нескольких источников. | `[21.5, 21.6, 21.4]` |

| Buffer (Буфер) | Массив байт для представления бинарных данных. Используется в специфических случаях: работа с файлами, изображениями, или при низкоуровневом взаимодействии с протоколами вроде Modbus RTU, где данные приходят в виде сырого набора байт. | `` |

| null / undefined | Специальные значения, обозначающие "отсутствие данных". Поток сообщения с `msg.payload`, равным `null`, часто прерывается, так как многие узлы не знают, как его обработать. Это может быть полезно для фильтрации. | `null` |

Практические примеры для каждого типа:

    {

"payload": "START_PUMP_01",

"topic": "commands/pumps/control"

}

    {

"payload": 22.5,

"topic": "telemetry/living_room/temperature"

}

    {

"payload": true,

"topic": "telemetry/main_door/is_open"

}

    {

"payload": {

"temperature": 24.1,

"humidity": 45.8,

"source": "sensor-th-01-room2",

"ts": 1680525143000

},

"topic": "telemetry/room2/environment"

}

    {

"payload": [22.1, 24.5, 19.8],

"topic": "telemetry/house/avg_temperature_calc"

}

    {

"payload": { "data": [1036, 17203], "buffer": },

"topic": "modbus/read/response"

}

В данном случае `payload` является объектом, но его ключевое содержимое — это свойство `buffer` типа Buffer.

---

Практика: Изменение типа и содержимого payload с помощью узла Change

Узел `Change` — один из самых универсальных и часто используемых инструментов в Node-RED. Он позволяет выполнять самые разнообразные манипуляции с объектом `msg`, и в первую очередь с `msg.payload`, без написания кода в узле `Function`.

Рассмотрим четыре основные операции с `msg.payload`.

1. Установка нового значения (Set)

Это правило полностью заменяет содержимое `msg.payload` новым значением.

Задача: Преобразовать входящее сообщение с timestamp в строку "Проверено".
  • Добавьте узел `Inject`. В его настройках `payload` установите тип `timestamp`.
  • Добавьте узел `Change`.
  • Настройте правило в узле `Change`:
  • * Set: `msg.payload`

    * to: (выберите тип "string") `Проверено`

  • Соедините `Inject` -> `Change` -> `Debug`.
  • Разверните поток и нажмите на `Inject`. В панели отладки вы увидите, что числовое значение timestamp было полностью заменено строкой "Проверено".
  • 2. Изменение внутри строки (Change)

    Это правило позволяет найти и заменить часть строки.

    Задача: Стандартизировать сообщения о состоянии. Заменить "Warning" на "ALERT".
  • Измените узел `Inject` так, чтобы он отправлял строку: `"Device State: Warning"`.
  • Настройте правило в узле `Change`:
  • * Change: `msg.payload`

    * Search for: (тип "string") `Warning`

    * Replace with: (тип "string") `ALERT`

  • Разверните и проверьте. `payload` изменится на `"Device State: ALERT"`.
  • 3. Работа с JSON-объектами

    Узел `Change` отлично подходит для манипуляций со структурированными данными.

    Задачи:

    А. Извлечь одно значение из объекта в `payload`.

    Б. Изменить значение по ключу внутри объекта.

    Настройка:
  • Используйте `Inject` для отправки JSON-объекта:
  •     {

    "temperature": 25,

    "humidity": 60,

    "status": "OK"

    }

  • Для задачи (А), настройте узел `Change` так:
  • * Set: `msg.payload`

    * to: (выберите тип "msg.") `msg.payload.temperature`

    * Результатом будет `msg.payload`, равный числу `25`.

  • Для задачи (Б), настройте другой узел `Change` так:
  • * Set: `msg.payload.status`

    * to: (тип "string") `ERROR`

    * Результатом будет исходный объект, но с измененным полем `status`: `{"temperature": 25, "humidity": 60, "status": "ERROR"}`.

    4. Трансформация данных с помощью JSONata

    JSONata — это мощный язык запросов и преобразований для JSON, встроенный в Node-RED. Он позволяет выполнять сложные трансформации данных одной строкой, на которые в узле `Function` ушли бы десятки строк кода.

    > 💡 Подсказка: Используйте встроенный редактор JSONata в узле `Change`. Когда вы выбираете тип `J: (expression)`, появляется кнопка `...`, открывающая редактор. Он позволяет видеть результат трансформации на примере реального сообщения, что значительно ускоряет отладку.

    Задача: Преобразовать "сырые" данные от Modbus-устройства в понятный JSON-объект.

    Предположим, узел `modbus-read` выдал нам такой `payload`: `{"value": 255, "unitId": 10}`. Нам нужно преобразовать это в стандартный формат `{ "temperature": 25.5, "unit": "°C", "device": "modbus:10" }`.

  • Подготовьте `Inject` с `payload` типа JSON: `{"value": 255, "unitId": 10}`.
  • Настройте правило в узле `Change`:
  • * Set: `msg.payload`

    * to: (выберите тип "J: expression")

    * В поле выражения введите:

            {

    "temperature": payload.value / 10,

    "unit": "°C",

    "device": "modbus:" & payload.unitId

    }

  • Разверните и проверьте. В панели `Debug` вы увидите результат:
  •     {

    "temperature": 25.5,

    "unit": "°C",

    "device": "modbus:10"

    }

    Это наглядный пример мощи JSONata: мы выполнили арифметическую операцию, добавили статическую строку и выполнили конкатенацию (слияние) строк в одном выражении.

    ---

    Пример: msg.payload в протоколе MQTT

    Протокол MQTT является основным протоколом для межмашинного взаимодействия (M2M) в IoT и системах автоматизации. В Node-RED работа с ним строится вокруг `msg.payload` и `msg.topic`.

    Сериализация и десериализация

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

    > ⚠️ Внимание: Многие внешние системы и брокеры MQTT ожидают на входе `payload` в виде строки. Если вы отправляете JSON-объект из Node-RED, его необходимо явно преобразовать в строку с помощью узла `JSON` или функции `JSON.stringify()` в узле `Function`. Узел `mqtt out` в Node-RED часто делает это автоматически для объектов, но полагаться на это не всегда безопасно, особенно при работе со сторонними клиентами. Явное преобразование делает поток более надежным и понятным.

    Практический кейс: датчик и исполнительное устройство

    Сценарий 1: Отправка данных с датчика Цель: Каждые 10 секунд отправлять показания температуры и влажности в виде JSON-объекта в топик `hi/office/env/state`.
  • `Inject`: Настроен на повторение каждые 10 секунд.
  • `Function` "Симуляция датчика": Генерирует объект с данными.
  •     msg.payload = {

    "temperature": parseFloat((22 + Math.random() * 2).toFixed(1)),

    "humidity": parseFloat((40 + Math.random() * 5).toFixed(1)),

    "ts": Date.now()

    };

    return msg;

  • `JSON`: Узел, который преобразует типы. Убедитесь, что он установлен в режим "Always convert to JSON String". Он возьмет объект из `msg.payload` и превратит его в строку.
  • `mqtt out`: Настроен на отправку в топик `hi/office/env/state`.
  • После этого узла `Function` `msg.payload` будет объектом. После узла `JSON` `msg.payload` станет строкой вида: `'{"temperature":22.5,"humidity":42.1,"ts":1680525999000}'`. Именно эта строка и будет отправлена в MQTT.

    Сценарий 2: Получение команды на управление реле Цель: Слушать топик `hi/office/light/set` и управлять реле. Команды приходят в виде строк `"ON"` и `"OFF"`.
  • `mqtt in`: Подписан на топик `hi/office/light/set`.
  • `Switch`: Проверяет `msg.payload`.
  • * Правило 1: `==` (строка) `ON` -> выход 1.

    * Правило 2: `==` (строка) `OFF` -> выход 2.

  • `Change` (подключен к выходу 1): Устанавливает `msg.payload` в `true` (булево).
  • `Change` (подключен к выходу 2): Устанавливает `msg.payload` в `false` (булево).
  • Оба узла `Change` подключены к узлу `rpi gpio out` (управление реле на выходе контроллера), который ожидает на вход `true` или `false`.
  • В этом потоке `mqtt in` получает строку `"ON"` или `"OFF"` в `msg.payload`. Узел `Switch` анализирует эту строку и направляет поток по нужной ветке, где `msg.payload` преобразуется в булев тип, понятный для узла управления физическим оборудованием.

    ---

    Резюме и лучшие практики работы с msg.payload

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

    📋 Ключевые практики:

  • Соблюдайте конвенцию: Используйте `msg.payload` для передачи самих данных, а `msg.topic` — для передачи контекста (маршрутизации). Это золотой стандарт, который понимают все разработчики Node-RED.
  • Предпочитайте JSON для сложных данных: Если вам нужно передать больше одного параметра (например, температуру и влажность), не создавайте несколько параллельных потоков. Упакуйте все данные в один JSON-объект в `msg.payload`. Это делает сообщение самодостаточным и атомарным.
  • Всегда проверяйте тип данных: На входе в поток, особенно из внешних систем (MQTT, HTTP, Modbus), всегда проверяйте тип и структуру `msg.payload`. Не доверяйте внешним данным. Используйте узлы `Switch` или `Function` для валидации.
  • Помните о сериализации: При отправке JSON-объектов во внешние системы (особенно MQTT) убедитесь, что они корректно преобразованы в строку. Используйте узел `JSON` для явного контроля над этим процессом.
  • Документируйте структуру: Для сложных JSON-объектов в `msg.payload` оставляйте комментарии прямо в потоке (с помощью узла `Comment`). Опишите, какие поля что означают. Это сэкономит часы отладки в будущем вам и вашим коллегам.
  • Используйте узел `Change` для простых манипуляций: Не прибегайте к узлу `Function` для простых операций, таких как замена значения, извлечение свойства или переименование ключа. Узел `Change` делает эти операции нагляднее и не требует знания JavaScript.
  • Освоив эти принципы, вы сможете эффективно управлять потоками данных, создавая мощные и элегантные решения на платформе HI.

    Что дальше?

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