Лабораторная работа: Реализация watchdog'a для датчика протечки
Введение и цели лабораторной работы
> 🔗 Связанный материал: Перед началом работы рекомендуется освежить знания о концепции 'Last Seen' (рассмотренной в уроке COURSE-04-M07-L02) и использовании ноды Trigger для создания таймеров (урок COURSE-04-M07-L04).
В современных системах автоматизации надежность является не просто желательным качеством, а абсолютной необходимостью, особенно когда речь идет о критически важных функциях безопасности. Датчики протечки воды, датчики утечки газа или пожарные извещатели — это первая линия обороны вашего объекта. Но что произойдет, если такой датчик выйдет из строя? Если он перестанет передавать данные из-за севшей батарейки, обрыва провода или программного сбоя, система будет считать, что все в порядке, в то время как реальная угроза может остаться незамеченной. Это создает ложное чувство безопасности и сводит на нет саму идею автоматизации.
Для борьбы с этой проблемой инженеры применяют так называемые сторожевые таймеры, или watchdog-механизмы. Принцип их работы прост и гениален: система постоянно ожидает «пульс» от датчика — регулярный сигнал, подтверждающий его работоспособность. Если этот сигнал не поступает в течение заданного промежутка времени, система бьет тревогу. Это позволяет обнаружить не только аварию (протечку), но и отказ самого средства обнаружения аварии.
Цель данной лабораторной работы — спроектировать, собрать и протестировать в среде Node-RED полноценный watchdog-сценарий для виртуального датчика протечки. Мы научимся не просто обнаруживать "мертвые" датчики, но и инициировать аварийный режим (fail-safe), например, отправляя команду на перекрытие магистрального крана.📋 Ключевые понятия:
- Watchdog (Сторожевой таймер): Механизм, который отслеживает наличие регулярных сигналов от устройства и инициирует тревогу в случае их отсутствия.
- Эмуляция датчика: Создание в Node-RED потока, имитирующего поведение реального физического устройства для целей отладки и тестирования.
- Аварийный режим (Fail-safe): Заранее определенный сценарий действий, который система выполняет при отказе критически важного компонента для минимизации возможного ущерба.
Для выполнения работы нам понадобится следующий набор стандартных узлов (нод) Node-RED:
- `Inject`: Для эмуляции периодических сообщений от датчика.
- `Trigger`: Основной элемент нашего сторожевого таймера.
- `Function`: Для обработки логики и формирования тревожных сообщений.
- `MQTT Out`: Для отправки команд и тревожных сообщений в систему мониторинга.
- `Debug`: Наш главный инструмент для отладки и визуализации потока данных.
Прежде чем мы начнем, откройте редактор Node-RED на вашем контроллере HI и создайте новую вкладку (поток), назвав ее `LAB-Watchdog`. Именно здесь мы будем собирать нашу схему.
---
Шаг 1: Эмуляция датчика протечки
Первым шагом любой отладки или разработки в Node-RED является создание воспроизводимой среды. Вместо того чтобы сразу подключать физический датчик, мы создадим его эмулятор с помощью ноды `Inject`. Это позволит нам полностью контролировать процесс, имитировать штатную работу и отказы, не завися от внешнего оборудования.
> 💡 Подсказка: Используйте JSON в качестве типа данных в ноде Inject. Это позволяет создавать структурированные сообщения, которые соответствуют паттерну "Контракт сообщения" и которые легко парсить и обрабатывать в последующих нодах.
{
"sensor_id": "leak_kitchen_01",
"status": "dry",
"location": "kitchen",
"type": "leak_detector",
"timestamp": 0
}
После выполнения этих шагов мы получили полноценный эмулятор. Он периодически отправляет в поток сообщение, полностью соответствующее контракту сообщения, принятому в нашей системе.
Структура сообщения `msg.payload` от эмулятора:| Поле | Тип | Описание | Пример |
|---------------|----------|---------------------------------------------------------------------|---------------------|
| `sensor_id` | `string` | Уникальный идентификатор датчика. | `"leak_kitchen_01"` |
| `status` | `string` | Текущее состояние. Может быть `"dry"` или `"wet"`. | `"dry"` |
| `location` | `string` | Местоположение датчика для удобства идентификации. | `"kitchen"` |
| `type` | `string` | Тип датчика. | `"leak_detector"` |
| `timestamp` | `number` | Временная метка в формате Unix epoch (ms), добавленная нодой Inject. | `1677611234000` |
Добавьте ноду `Debug` на поле и соедините выход эмулятора с ее входом. Назовите ноду `Debug` как `Выход эмулятора`. Разверните (`Deploy`) изменения. В панели отладки справа вы должны видеть, как каждые 30 секунд появляется новое сообщение от нашего виртуального датчика. Убедитесь, что структура сообщения соответствует описанной выше.
---
Шаг 2: Реализация сторожевого таймера (Watchdog)
Теперь, когда у нас есть стабильный поток данных от эмулятора, мы можем построить сам сторожевой таймер. Его сердцем будет нода `Trigger`. Эта нода работает как таймер "обратного отсчета", который сбрасывается каждый раз, когда на его вход приходит сообщение. Если же в течение заданного времени ни одного сообщения не поступило, таймер "срабатывает" и отправляет сигнал тревоги.
> ⚠️ Внимание: Неправильно подобранный интервал watchdog — частая причина ложных срабатываний. Рекомендуется устанавливать таймаут в 2-3 раза больше периода опроса датчика. Если датчик шлет данные раз в 30 секунд, а таймаут — 31 секунда, то любая минимальная сетевая задержка вызовет ложную тревогу.
Конфигурация ноды Trigger
| Параметр | Значение | Пояснение |
|----------------|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Send | `nothing` | При получении сообщения от датчика (когда все в порядке), нода ничего не должна отправлять дальше. Она просто сбрасывает свой внутренний таймер и ждет следующего сигнала. |
| Then wait for | `65` `seconds` | Это и есть таймаут нашего watchdog'а. Мы выбрали 65 секунд, что чуть больше чем в два раза превышает интервал опроса датчика (30 секунд). Это дает системе запас на случай небольших задержек. |
| Then send | `the original message` | Если таймер на 65 секунд истек, нода отправит на свой второй выход то последнее сообщение, которое она получила от датчика. Это очень важно для диагностики, т.к. мы будем знать, каким было последнее известное состояние датчика. |
| Handling | `extend delay if new message arrives` | Эта опция должна быть включена по умолчанию. Она гарантирует, что каждый новый сигнал от датчика "перезапускает" таймер с самого начала. |
| Name | `Watchdog (65 сек)` | Присвойте ноде понятное имя. |
Таким образом, логика работы следующая:
- 0-30 сек: Приходит сообщение от эмулятора. `Trigger` сбрасывает таймер и молчит.
- 30-60 сек: Приходит второе сообщение. `Trigger` снова сбрасывает таймер и молчит.
- 60-90 сек: Приходит третье сообщение. Все в штатном режиме.
- Сценарий отказа: Допустим, после второго сообщения (на 60-й секунде) эмулятор "ломается". `Trigger` ждет... Проходит 65 секунд с момента получения последнего сообщения (т.е. на 60 + 65 = 125-й секунде от начала). Таймер срабатывает! Нода `Trigger` отправляет последнее полученное от датчика сообщение на свой второй выход.
Именно этот сигнал со второго выхода мы и будем использовать для запуска аварийного сценария. Пока что подключите к обоим выходам ноды `Trigger` по одной ноде `Debug`, назвав их соответственно `Выход 1 (OK)` и `Выход 2 (FAIL)`. Разверните поток. Вы увидите, что в панели отладки появляются сообщения только от `Выхода эмулятора`, но не от двух других нод `Debug`. Это означает, что watchdog работает корректно в штатном режиме.
---
Шаг 3: Обработка отказа и аварийный режим
Сигнал об отказе датчика — это только полдела. Система должна не просто зафиксировать проблему, но и адекватно на нее отреагировать. Сейчас мы создадим логику, которая при срабатывании watchdog'а сформирует стандартизированное тревожное сообщение и инициирует аварийный режим, отправив команду на перекрытие воды.
// Последнее сообщение от датчика, которое сохранила нода Trigger
const lastSensorMsg = msg.payload;
// 1. Формируем новое, стандартизированное тревожное сообщение
// Это сообщение пойдет в систему мониторинга и логирования
const alert_payload = {
alert_type: "WATCHDOG_FAIL",
message: `Датчик ${lastSensorMsg.sensor_id} не выходит на связь более 65 секунд!`,
source_id: lastSensorMsg.sensor_id,
last_known_status: lastSensorMsg.status,
last_seen_ts: lastSensorMsg.timestamp,
severity: "critical",
timestamp: Date.now()
};
// 2. Формируем команду для аварийного режима (fail-safe)
// Эта команда будет отправлена на исполнительное устройство (кран)
const failsafe_command_payload = {
command: "CLOSE",
source: "watchdog_leak_system",
reason: `Отказ датчика ${lastSensorMsg.sensor_id}`
};
// 3. Создаем два независимых сообщения для отправки по разным MQTT-топикам
const alert_msg = {
topic: "alerts/safety",
payload: alert_payload
};
const failsafe_msg = {
topic: `commands/valves/kitchen_main/set`,
payload: failsafe_command_payload
};
// Выводим информацию в статус ноды для быстрой диагностики
node.status({fill:"red", shape:"dot", text:`Отказ: ${lastSensorMsg.sensor_id}`});
// Отправляем оба сообщения на выход ноды.
// Мы используем два выхода, чтобы разделить потоки команд и алертов.
return [alert_msg, failsafe_msg];
Разбор кода:
- `lastSensorMsg`: Мы получаем доступ к последнему сообщению от датчика, которое сохранила для нас нода `Trigger`. Это бесценная информация для анализа причин сбоя.
- `alert_payload`: Мы создаем новый объект `msg.payload`, который соответствует контракту для тревожных сообщений. Он содержит всю необходимую информацию: тип тревоги, человекочитаемое описание, ID отказавшего датчика, его последнее известное состояние и время обнаружения отказа.
- `failsafe_command_payload`: Параллельно мы формируем полезную нагрузку для отправки команды. В данном случае, это команда `CLOSE` для крана.
- `alert_msg` и `failsafe_msg`: Мы упаковываем наши `payload` в полноценные `msg` объекты, задавая им разные `msg.topic`. Это позволит нам направить тревогу в одну систему (например, в главный дашборд), а команду — напрямую на исполнительное устройство.
- `node.status()`: Мы используем паттерн "Визуальный статус", чтобы при отказе нода `Function` немедленно сигнализировала об этом красной точкой в редакторе Node-RED.
- `return [alert_msg, failsafe_msg]`: Функция возвращает массив из двух сообщений. Первое сообщение выйдет из первого выхода ноды, второе — из второго.
Теперь наш сценарий готов к действию. Осталось подключить к выходам обработчики.
---
Шаг 4: Сборка и тестирование сценария
Сборка и тестирование — финальный и самый важный этап, подтверждающий работоспособность нашей логики. Мы проведем два теста: один для штатного режима и один для имитации отказа.
Итоговая схема потока
Соберите все ноды вместе, как показано на ASCII-схеме ниже. Используйте ноды `Debug` для визуализации сообщений на каждом этапе.
// FLOW-ID: LAB-WATCHDOG-001
// Описание: Сценарий отслеживания доступности датчика протечки.
// Часть 1: Эмуляция и Watchdog
[Inject: Эмулятор (30s)] ---> [Trigger: Watchdog (65s)] --+-- (Выход 1, OK) --> [Debug: Штатный режим]
|
+-- (Выход 2, FAIL) --> [Function: Формирование тревоги]
// Часть 2: Обработка отказа
[Function: Формирование тревоги] --+-- (Выход 1, Alert) --> [MQTT Out: alerts/safety] ----> [Debug: Тревога]
|
+-- (Выход 2, Cmd) ---> [MQTT Out: commands/...] ---> [Debug: Команда крану]
Настройка:
Тест-план
Теперь выполним последовательность тестов для проверки нашего сценария.
Сценарий тестирования №1: Штатный режим
* Каждые 30 секунд в панели отладки появляется сообщение от `Эмулятора датчика протечки`.
* Ноды `Debug`, подключенные к выходам `Function` (`Тревога` и `Команда крану`), молчат. Никаких сообщений от них не поступает.
* Статус под нодой `Function: Формирование тревоги` отсутствует.
Сценарий тестирования №2: Режим отказа
* По истечении 65 секунд нода `Trigger` сработает.
* Нода `Function: Формирование тревоги` выполнит свой код. Под ней появится красный статус `Отказ: leak_kitchen_01`.
* В панели отладки одновременно появятся два новых сообщения:
* От ноды `Debug: Тревога` придет JSON с полной информацией об отказе.
* От ноды `Debug: Команда крану` придет JSON с командой на закрытие крана.
Поздравляем! Вы только что реализовали один из важнейших паттернов надежности в системах автоматизации.
---
Заключение: Применение и масштабирование
В рамках этой лабораторной работы мы на практике освоили создание надежного watchdog-механизма с помощью стандартных средств Node-RED. Мы не просто обнаружили отказ, но и реализовали логику аварийного режима, что является признаком профессионально спроектированной системы.
Созданный нами паттерн 'Эмулятор -> Watchdog (Trigger) -> Обработчик тревог' является универсальным и может быть легко адаптирован для мониторинга любого критически важного устройства в вашей системе:
- Датчики CO₂ в котельной.
- Датчики движения в охранной системе.
- Сетевое оборудование (например, проверка доступности IP-камеры или другого контроллера).
- Любое Modbus-устройство, которое должно регулярно опрашиваться.
Для масштабирования этого решения на несколько датчиков можно использовать субпотоки (subflows), как было рассмотрено в скилле "Паттерны Node-RED". В субпоток выносится связка `Trigger` + `Function`, а ID датчика и интервал таймаута передаются через переменные окружения субпотока.
Что дальше?
Созданный нами сценарий является надежным фундаментом, который можно и нужно развивать:
- Интеграция с визуализацией: Отправленные по MQTT тревоги могут быть отображены на дашборде (например, в Grafana или нативной панели Node-RED Dashboard), меняя цвет иконки датчика с зеленого на красный.
- Уведомления: К выходу с тревогой можно подключить ноды для отправки Push-уведомлений на смартфон (через Telegram, Pushover) или E-mail сообщений ответственному персоналу.
- Журналирование: Все тревожные сообщения следует записывать в базу данных (например, MySQL или InfluxDB, доступные на контроллере HI) для последующего анализа и ведения истории инцидентов.
В следующем уроке мы рассмотрим, как применять аналогичные подходы для мониторинга состояния устройств, подключенных по шине Modbus, где проблемы со связью являются частым явлением.