ГлавнаяАкадемияДатчики и входы: нормализация сигналов → Генерация алертов: отправка MQTT-сообщения или запись в лог об отказе датчика

Генерация алертов: отправка MQTT-сообщения или запись в лог об отказе датчика

Урок 4 · Датчики и входы: нормализация сигналов · 30 мин · theory

От теории к практике: Что делать после срабатывания Watchdog

В предыдущих уроках этого модуля мы спроектировали и реализовали механизм Watchdog — стража, неустанно следящего за работоспособностью наших датчиков. Мы научились добавлять временные метки к каждому сообщению и использовать узел `Trigger` для создания таймера, который срабатывает, если данные перестают поступать в течение заданного интервала.

> 🔗 Связанный материал: Для полного понимания работы механизма Watchdog, настоятельно рекомендуется ознакомиться с материалом урока `COURSE-04-M07-L03` "Использование ноды Trigger для создания таймера watchdog". В нем подробно разобрана логика настройки этого ключевого компонента.

Теперь мы подошли к логическому завершению этого механизма. Представим ситуацию: на объекте (в умном доме, офисе или на небольшом производстве) произошел сбой. Например, оборвался кабель, ведущий к датчику температуры в серверной, или вышло из строя питание Modbus-шлюза, опрашивающего счетчики электроэнергии. Наш Watchdog, как и положено, обнаружил проблему — он не получил очередного сообщения от датчика в течение, скажем, 300 секунд и сработал.

Но что дальше? Само по себе срабатывание таймера, не приводящее ни к каким действиям, бессмысленно. Система должна отреагировать. Отсутствие реакции равносильно отсутствию самого механизма контроля. Инженер, эксплуатирующий систему, и, возможно, конечный пользователь должны быть немедленно и информативно извещены о проблеме.

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

Все системные реакции на отказ можно разделить на два больших типа:

  • Пассивная реакция (Логирование): Система фиксирует факт сбоя, записывая подробную информацию в специальный файл или базу данных. Этот подход является основой для "посмертного" анализа инцидентов. Инженер, прибывший на объект для устранения неисправности, может открыть лог-файл и увидеть полную хронологию событий: когда именно датчик перестал отвечать, какие еще события происходили в это время. Это фундаментальный инструмент для диагностики и отладки.
  • Активная реакция (Отправка уведомлений): Система не просто записывает информацию, а активно пытается сообщить о ней внешнему миру. Это может быть отправка сообщения через MQTT, которое будет получено панелью оператора (HMI), мобильным приложением, Telegram-ботом или системой верхнего уровня (SCADA, BMS). Такая реакция необходима для ситуаций, требующих немедленного вмешательства человека.
  • На профессионально спроектированных объектах всегда используются оба подхода одновременно. Каждый отказ датчика должен быть и записан в лог для истории, и отправлен как активное уведомление. В этом уроке мы на практике разберем, как реализовать оба этих механизма в Node-RED на контроллере HI.

    ---

    Стратегии генерации алертов: лог-файлы vs. MQTT-сообщения

    Выбор между пассивным логированием и активной отправкой MQTT-алертов — это не вопрос "или/или", а вопрос "когда и для чего". Каждый метод решает свою задачу и имеет свои сильные и слабые стороны. Понимание этих различий позволяет инсталлятору спроектировать по-настоящему надежную и информативную систему.

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

    Логирование в файл: цифровой след инцидента

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

    MQTT-алерты: нервная система умного объекта

    MQTT-алерты — это публикация специальных сообщений в определенные MQTT-топики при возникновении нештатных ситуаций. Любая система, подписанная на эти топики, немедленно получит уведомление. Преимущества: Недостатки:

    Сравнительная таблица стратегий

    | Критерий | Логирование в файл | MQTT-алерты |

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

    | Основная цель | Анализ инцидентов инженером (Post-mortem) | Немедленное оповещение оператора/системы (Real-time) |

    | Скорость реакции | Низкая (требуется ручной доступ) | Высокая (мгновенная доставка подписчикам) |

    | Надежность | Высокая (не зависит от сети) | Средняя (зависит от сети и MQTT-брокера) |

    | Сложность реализации | Низкая (узел `file`) | Низкая (узел `mqtt out`) |

    | Зависимости | Локальное хранилище на контроллере | Работоспособная IP-сеть и MQTT-брокер |

    | Типичный потребитель | Инженер-наладчик, служба поддержки | Панель оператора (HMI), мобильное приложение, SCADA/BMS |

    | Типовой сценарий | "Датчик температуры в гостиной не отвечал с 14:05." | "ВНИМАНИЕ! Потеряна связь с датчиком протечки в с/у №1!" |

    ---

    Практика: Запись алерта в системный лог-файл

    Перейдем к реализации пассивной реакции. Наша задача — при срабатывании Watchdog-таймера записать в специальный лог-файл информативное сообщение о том, какой именно датчик и когда перестал выходить на связь.

    Архитектура потока

    Мы будем использовать второй выход узла `Trigger`, который, как мы помним из предыдущего урока, отправляет сообщение именно тогда, когда таймер истек, не получив "сброса".

    ASCII-схема потока:
    // Поток данных от датчика
    

    [Sensor Node] --> [Function: Add Timestamp] -->+

    |

    // Watchdog механизм v

    [Trigger: 300s 'send nothing', then 'send timeout msg'] -- (Выход 1) --> [НИКУДА]

    |

    +-- (Выход 2, срабатывает через 300с) --> [Function: Format Log] --> [File]

    Настройка узлов

  • Узел `Trigger`:
  • * Мы предполагаем, что он уже настроен согласно уроку `COURSE-04-M07-L03`. Ключевая настройка:

    * `Send`: `nothing` (при получении сообщения)

    * `then wait for`: `5` `minutes` (или любой другой интервал, например, 300 секунд)

    * `If no message arrives`: `send` `a message with the payload:` `{ "error": "timeout" }`

    * Соединяем второй выход этого узла со следующим узлом в нашей цепочке.

  • Узел `Function` (Имя: `Format Log Message`):
  • * Этот узел получит сообщение от `Trigger` и должен будет превратить его в осмысленную строку для лог-файла.

    * Важно добавить в лог максимум полезной информации: точное время, идентификатор датчика и суть проблемы.

    * Код для узла:

            // Предполагаем, что в потоке есть переменная, хранящая ID датчика

    // Например, flow.get("sensorId") или msg.topic

    // Для примера зададим его здесь статически

    const sensorId = "temp-sensor-server-room";

    const sensorName = "Датчик температуры в серверной";

    // Получаем текущую дату и время в читаемом формате

    const timestamp = new Date().toISOString();

    // Формируем стандартизированную строку лога

    // Формат: [TIMESTAMP] [LEVEL] [SENSOR_ID] Message

    const logMessage = `[${timestamp}] [ERROR] [${sensorId}] Watchdog timeout: No data received. Sensor "${sensorName}" is considered offline.\n`;

    // Передаем эту строку в msg.payload для следующего узла

    msg.payload = logMessage;

    // Обновляем статус узла для наглядной диагностики в редакторе

    node.status({ fill: "red", shape: "dot", text: `TIMEOUT: ${sensorId}` });

    return msg;

  • Узел `file` (Имя: `Write to failures.log`):
  • * Этот узел является финальной точкой нашей цепочки. Он принимает строку из `msg.payload` и записывает ее в файл.

    * Настройка узла:

    * Filename: Укажите путь к лог-файлу. На контроллерах HI рекомендуется использовать директорию `/var/log/`. Например: `/var/log/hi_sensor_failures.log`.

    * Action: Выберите `Append to file` (Дописать в файл). Это критически важно, чтобы не перезаписывать лог при каждом новом событии.

    * Add newline (\n) to each payload? Поставьте галочку. Это обеспечит запись каждого нового сообщения с новой строки.

    * Create directory if it doesn't exist? Поставьте галочку. Это удобно при первом запуске.

    Теперь, если датчик `temp-sensor-server-room` перестанет присылать данные, через 5 минут в файле `/var/log/hi_sensor_failures.log` появится запись вида:

    [2023-10-27T12:30:00.123Z] [ERROR] [temp-sensor-server-room] Watchdog timeout: No data received. Sensor "Датчик температуры в серверной" is considered offline.
    

    Эта запись станет бесценным источником информации для инженера при разборе инцидента.

    ---

    Практика: Отправка алерта по MQTT

    Теперь реализуем активную реакцию — отправку структурированного сообщения по MQTT. Это позволит другим системам мгновенно узнать о проблеме.

    Архитектура потока и проектирование топика

    Логика потока аналогична предыдущему примеру, но вместо узла `file` мы будем использовать `mqtt out`.

    ASCII-схема потока:
    // ... Watchdog механизм ...
    

    [Trigger: 300s] -- (Выход 2) --> [Function: Format MQTT Alert] --> [mqtt out: hi/alerts/sensor_failure]

    Ключевым моментом здесь является правильное проектирование MQTT-топика и полезной нагрузки (payload).

    Настройка узлов

  • Узел `Function` (Имя: `Format MQTT Alert`):
  • * Задача этого узла — сформировать JSON-объект, который станет телом MQTT-сообщения.

    * Код для узла:

            // Это сообщение приходит со второго выхода узла Trigger

    // msg.payload = { "error": "timeout" }

    // Мы обогатим его дополнительной информацией

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

    // храниться в контексте или приходить в исходном сообщении.

    const sensorId = "temp-sensor-server-room";

    const sensorName = "Датчик температуры в серверной";

    const location = "Этаж 2, Серверная";

    // Получаем последнее известное значение (если оно есть)

    const lastValue = flow.get("last_value_" + sensorId) || "N/A";

    // Формируем полезную нагрузку по нашему контракту сообщения

    msg.payload = {

    "alert_type": "sensor_failure",

    "sensor_id": sensorId,

    "sensor_name": sensorName,

    "location": location,

    "status": "offline",

    "reason": "Watchdog timeout. No data received for 300 seconds.",

    "last_known_value": lastValue,

    "timestamp": new Date().getTime() // Unix timestamp в миллисекундах

    };

    // Задаем топик для отправки. Это можно делать и в узле mqtt out,

    // но установка в function дает больше гибкости.

    msg.topic = "hi/alerts/sensor_failure";

    // Задаем флаг retain для MQTT-сообщения.

    // Это значит, что брокер сохранит это сообщение как последнее известное

    // для данного топика. Новый подписчик сразу узнает об алерте.

    msg.retain = true;

    // QoS (Quality of Service)

    // 1 = Гарантия доставки хотя бы один раз. Хороший выбор для алертов.

    msg.qos = 1;

    node.status({ fill: "blue", shape: "dot", text: `MQTT Alert: ${sensorId}` });

    return msg;

    * Пример итогового `msg.payload`:

            {

    "alert_type": "sensor_failure",

    "sensor_id": "temp-sensor-server-room",

    "sensor_name": "Датчик температуры в серверной",

    "location": "Этаж 2, Серверная",

    "status": "offline",

    "reason": "Watchdog timeout. No data received for 300 seconds.",

    "last_known_value": 24.5,

    "timestamp": 1678886400000

    }

  • Узел `mqtt out` (Имя: `Publish Alert`):
  • * Этот узел опубликует сформированное сообщение на MQTT-брокере.

    * Настройка узла:

    * Server: Выберите предварительно настроенный MQTT-брокер контроллера HI.

    * Topic: Можно оставить пустым, так как мы задаем его динамически в `msg.topic`.

    * QoS: `1 - At least once`.

    * Retain: `false`. Мы управляем этим флагом динамически через `msg.retain`.

    * Name: `Publish Alert`.

    Теперь любая система (например, панель визуализации), подписанная на топик `hi/alerts/sensor_failure`, немедленно получит исчерпывающую информацию об инциденте в удобном для машинной обработки формате.

    ---

    Проектирование отказоустойчивой архитектуры алертов

    По мере роста проекта количество потенциальных источников сбоев увеличивается. Могут отказать не только датчики, но и исполнительные устройства (реле, диммеры), может пропасть связь с Modbus-модулями, могут возникнуть логические ошибки в самих сценариях. Создавать для каждого случая отдельный поток мониторинга — неэффективно. Профессиональный подход заключается в создании централизованной архитектуры алертов.

    Единый топик для системных событий

    Основная идея — направлять все системные уведомления в одно "дерево" MQTT-топиков. Например, `hi/system/alerts/`.

    Преимущества такого подхода:
  • Простота мониторинга: Чтобы следить за здоровьем всей системы, достаточно подписаться на один топик с wildcard: `hi/system/alerts/#`.
  • Легкая интеграция: Любую систему верхнего уровня (BMS, SCADA, Service Desk) нужно подключить только к одной точке, чтобы получать все инциденты.
  • Централизованное логирование: Можно создать отдельный, очень простой поток в Node-RED, который будет выполнять только одну функцию: подписываться на `hi/system/alerts/#` и записывать каждое полученное сообщение в единый системный лог. Это объединяет преимущества обоих подходов — и немедленное оповещение, и надежный "цифровой след".
  • > ⚠️ Внимание: Избегайте использования флага `Retain=true` для часто изменяющихся статусов (например, для самих показаний датчиков). Однако для алертов о недоступности (`status: "offline"`) флаг `Retain=true` очень полезен. Если система мониторинга перезагрузится, она, подключившись к брокеру, сразу же получит последнее сообщение об отказе и будет знать, что датчик все еще не в сети. Не забудьте отправить сообщение с `Retain=true` и пустым payload или `{ "status": "online" }` в тот же топик, когда датчик вернется в строй, чтобы "очистить" сохраненный алерт.

    Пример сценария: центральный логгер алертов

    Этот поток демонстрирует, как можно элегантно объединить MQTT-алерты и логирование.

    ASCII-схема:
    [mqtt in: hi/system/alerts/#] --> [Function: Format for Log] --> [file: /var/log/hi_master_alert.log]
    

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

    ---

    Итоги и следующие шаги

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

    📋 Ключевые понятия, которые мы освоили:

    Мы убедились, что выбор между логированием и MQTT-оповещениями диктуется исключительно задачей: нужен ли нам инструмент для глубокого анализа инцидента или механизм для немедленного реагирования. На практике всегда используется комбинация обоих подходов.

    Создание централизованной архитектуры алертов на базе иерархических MQTT-топиков (`hi/system/alerts/#`) выводит систему на новый уровень профессионализма, упрощая ее мониторинг, обслуживание и дальнейшее расширение.

    В следующем уроке мы рассмотрим, как настроить аварийные сценарии — что именно должна делать система, когда датчик отказал. Например, как перевести климат-контроль в безопасный режим при отказе датчика температуры или включить свет на 100% при отказе датчика освещенности.