Расширение контракта сообщения для аналоговых данных (value, unit)
Введение: от простых значений к структурированным данным
На предыдущих этапах обучения мы рассматривали, как получать сырые данные с аналоговых входов контроллера, будь то напрямую с АЦП или через протокол Modbus, и как преобразовывать их в осмысленные физические величины с помощью масштабирования, например, используя узел `Range`. В результате этих операций мы получали в объекте `msg` простое числовое значение, например, `msg.payload = 23.5`. На первый взгляд, это кажется достаточным. Но по мере усложнения системы автоматизации такой подход становится источником серьезных проблем и ошибок.
Представьте себе систему, управляющую климатом в офисном здании. В логах отладки вы видите поток сообщений со значением `23`. Что это?
- Температура воздуха в градусах Цельсия?
- Уровень влажности в процентах?
- Давление в водяном контуре в барах?
- Уровень освещенности в люксах, поделенный на 10?
Без дополнительного контекста, который приходится держать в уме или искать в документации, это число лишено смысла. Инженер, который не создавал этот поток, потратит драгоценное время на выяснение его происхождения и назначения. При отладке системы в реальном времени такая неопределенность недопустима.
Для решения этой фундаментальной проблемы вводится понятие контракта сообщения (Message Contract). Это формальное соглашение внутри проекта о структуре и формате данных, передаваемых между узлами в Node-RED. Вместо того чтобы передавать "голые" данные, мы упаковываем их в само-документируемый объект.
> 📋 Ключевые понятия:
> * Контракт сообщения: Набор правил, описывающих структуру объекта `msg`, в частности `msg.payload`, для обеспечения предсказуемого и стандартизированного обмена данными между компонентами системы.
> * Само-документируемые данные: Данные, которые содержат в себе не только значение, но и метаинформацию, описывающую это значение (например, единицы измерения, источник, временную метку).
Преимущества использования структурированных, само-документируемых данных очевидны:
В этом уроке мы сделаем решительный шаг от передачи простых числовых значений к работе со структурированными объектами, внедрив базовый, но чрезвычайно мощный контракт сообщения для всех аналоговых данных.
---
Стандартный контракт для аналоговых данных: объект { value, unit }
Для всех аналоговых и физических величин, циркулирующих в системе автоматизации на платформе HI, Академия устанавливает единый стандартный контракт сообщения. Полезная нагрузка (`msg.payload`) должна представлять собой JSON-объект, содержащий как минимум два ключевых поля: `value` и `unit`.
`msg.payload = { "value":Разберем компоненты этой структуры:
- `value`: Это поле содержит само числовое значение, которое мы получили после всех преобразований и масштабирования. Тип данных — всегда число (`Number`), будь то целое или с плавающей точкой.
- `unit`: Это поле содержит строковое представление единицы измерения для значения в поле `value`. Использование стандартизированных обозначений (например, из системы СИ) является строго рекомендуемой практикой.
`msg.payload` больше не является просто числом. Теперь это объект, который несет в себе полный контекст измеряемой величины.
Примеры для типовых датчиков
Ниже приведена таблица с примерами правильного форматирования `msg.payload` для различных датчиков, подключаемых к универсальным входам или шинам контроллера HI.
| Тип датчика / Измеряемая величина | Пример `msg.payload` | Поле `value` | Поле `unit` |
| ---------------------------------- | --------------------------------------------------- | ------------------------------------------------ | --------------- |
| Датчик температуры (NTC, DS18B20) | `{ "value": 24.8, "unit": "°C" }` | Температура, градусы Цельсия | `°C` |
| Датчик относительной влажности | `{ "value": 52, "unit": "%" }` | Относительная влажность, проценты | `%` |
| Датчик освещенности | `{ "value": 850, "unit": "lux" }` | Освещенность, люксы | `lux` |
| Датчик давления воды/воздуха | `{ "value": 3.1, "unit": "bar" }` | Давление, бары | `bar` |
| Датчик качества воздуха (CO2) | `{ "value": 620, "unit": "ppm" }` | Концентрация CO2, частей на миллион | `ppm` |
| Аналоговый вход 0-10В (в режиме UI)| `{ "value": 7.5, "unit": "V" }` | Напряжение на входе, вольты | `V` |
| Счетчики (импульсные, Modbus) | `{ "value": 1543.2, "unit": "kWh" }` | Потребленная энергия, киловатт-часы | `kWh` |
Универсальность и расширяемость
Этот, на первый взгляд, простой формат обладает огромной гибкостью.
{
"value": 24.8,
"unit": "°C",
"source": "temp-sensor-office-101",
"ts": 1678886400000
}
Однако для уровня Installer и для большинства повседневных задач автоматизации базовой структуры `{ value, unit }` абсолютно достаточно. Внедрение этого стандарта — это первый и самый важный шаг к построению надежных и профессиональных систем.
---
Практика: формирование объекта с помощью ноды Change
Теория важна, но теперь давайте применим ее на практике. Наша задача — преобразовать `msg.payload`, содержащий простое числовое значение, в стандартизированный объект `{ value, unit }`. Самый эффективный и рекомендуемый способ сделать это в Node-RED — использовать стандартный узел `Change`.
Рассмотрим полный путь прохождения сигнала. Представим, что мы считываем данные с Modbus-датчика температуры, который отдает значение, умноженное на 10.
Настройка узла `Change`
Узел `Change` — это мощный инструмент, который позволяет модифицировать объект `msg` без написания кода в узле `Function`. Для нашей задачи мы будем использовать его способность устанавливать новое значение `msg.payload` с помощью JSONata — языка запросов и преобразований для JSON.
Пошаговая инструкция:* Set (`Установить`)
* `msg.` payload
* to the value (`в значение`)
* `J:` (expression / JSONata)
{ "value": payload, "unit": "°C" }
Что здесь происходит?
- `{ ... }`: Мы говорим JSONata, что хотим создать новый JSON-объект.
- `"value": payload`: Мы создаем ключ `value` и присваиваем ему текущее значение из `msg.payload`. В контексте JSONata, `payload` — это сокращенная ссылка на `msg.payload`.
- `"unit": "°C"`: Мы создаем ключ `unit` и присваиваем ему статичное строковое значение `"°C"`.
Теперь давайте посмотрим на наглядный пример до и после.
Вход узла `Change`:`msg.payload` имеет тип `Number`
25.5
Выход узла `Change`:
`msg.payload` стал типом `Object`
{
"value": 25.5,
"unit": "°C"
}
> 💡 Подсказка: JSONata — это мощный инструмент, встроенный в Node-RED. Он позволяет выполнять не только простые преобразования, как в нашем примере, но и сложные вычисления, условные выражения, форматирование строк и многое другое прямо в узле `Change`. Инвестирование времени в изучение основ JSONata окупится многократно, так как позволит вам создавать более лаконичные и эффективные потоки, избегая лишних узлов `Function`.
Этот метод является предпочтительным, поскольку он визуально понятен (на иконке узла `Change` видно, что происходит преобразование) и не требует написания JavaScript-кода, что снижает порог вхождения и вероятность синтаксических ошибок.
---
Обработка структурированных данных: примеры использования
Создание структурированных данных — это только половина дела. Настоящая мощь этого подхода раскрывается, когда мы начинаем использовать эту структуру в дальнейшей логике потока.
Логика ветвления с помощью узла `Switch`
Предположим, у нас есть универсальный поток, который обрабатывает данные от разных датчиков. Мы можем легко направить сообщения в разные ветки логики в зависимости от типа данных, просто проверив поле `unit`.
Настроим узел `Switch` для маршрутизации:
* `==` (string) `°C` -> выход 1 (логика для температуры, например, управление климатом)
* `==` (string) `%` -> выход 2 (логика для влажности, например, управление вентиляцией или увлажнителем)
* `==` (string) `ppm` -> выход 3 (логика для CO2, например, включение проветривания)
Теперь поток становится читаемым и надежным. Узел `Switch` выступает в роли "диспетчера", который точно знает, с какими данными он работает, и отправляет их по назначению.
Отображение в Dashboard
Многие виджеты `node-red-dashboard` (например, `Gauge`, `Chart`, `Text`) прекрасно работают со структурированными данными.
- Для виджета `Gauge` (Стрелочный прибор) вы можете настроить:
* Units: `{{msg.payload.unit}}` (подставить единицу измерения из сообщения).
В результате у вас получится информативный виджет, который автоматически подписывает отображаемое значение правильной единицей измерения. Если вы отправите на тот же виджет сообщение о влажности, он корректно отобразит `52 %`.
Валидация данных
> ⚠️ Внимание: Всегда валидируйте входящие данные на соответствие контракту. Поток, приходящий из другого модуля или от другого инженера, может не соответствовать ожиданиям. Отсутствие поля `unit` или некорректный тип `value` (например, строка вместо числа) может привести к ошибкам в нижестоящих узлах, которые будет сложно отследить.
Простейший способ валидации — использовать узел `Switch`:
Сообщения, не прошедшие проверку, можно направить на отдельный выход, который ведет к узлу `Catch` или специальному логгеру, сигнализируя о нарушении контракта.
Подготовка данных для записи в базу данных
Структура `{ value, unit }` идеально подходит для записи в современные базы данных временных рядов (Time-Series Databases), такие как InfluxDB или Prometheus, которые часто используются для мониторинга на платформе HI.
При формировании запроса на запись:
- `value` становится полем (field) в метрике.
- `unit` становится тегом (tag).
Использование `unit` в качестве тега позволяет выполнять очень эффективные запросы к базе данных, например: "покажи мне среднее значение всех метрик, где `unit` равно `°C`", что незаменимо при создании сложных дашбордов и аналитических отчетов.
---
Итоги и лучшие практики
В этом уроке мы совершили качественный переход от работы с примитивными числовыми данными к использованию осмысленных, структурированных объектов. Мы проследили весь путь сигнала: от его получения с датчика, через масштабирование, и до финального преобразования в стандартизированный формат с помощью узла `Change`.
Давайте закрепим ключевые моменты:- Проблема: Передача простых числовых значений в `msg.payload` создает неопределенность, затрудняет отладку и является источником потенциальных ошибок в сложных системах.
- Решение: Внедрение контракта сообщения, который определяет стандартную структуру для передаваемых данных.
- Стандарт HI: Для всех аналоговых величин мы используем объект `msg.payload` формата `{ "value":
, "unit": " ." }` - Реализация: Предпочтительным инструментом для формирования такого объекта из числа является узел `Change` с использованием простого выражения JSONata.
С сегодняшнего дня использование структурированного `payload` является обязательным стандартом для всех проектов, разрабатываемых в рамках экосистемы HI. Этот подход обеспечивает:
- Однозначность и читаемость: Данные становятся само-документируемыми.
- Надежность: Снижается риск логических ошибок благодаря возможности проверять тип данных.
- Простоту отладки: В логах сразу виден полный контекст сообщения.
- Легкую интеграцию: Упрощается взаимодействие с узлами `Dashboard`, базами данных и другими системами.
Что дальше?
Теперь, когда у нас есть надежный и стандартизированный способ представления данных, мы готовы перейти к более сложным задачам. В следующих уроках мы будем использовать эти структурированные данные для построения сложной логики управления, создания адаптивных пользовательских интерфейсов и настройки систем оповещения.