ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → Watchdog: контроль зависания сценариев и устройств

Watchdog: контроль зависания сценариев и устройств

Урок 5 · Сценарии умного дома: режимы, состояния, приоритеты · 30 мин · theory

Концепция Watchdog Timer (WDT) и её роль в автоматизации

> ℹ️ Информация: Термин 'Watchdog' (сторожевой пёс) пришел из схемотехники, где специальные микросхемы следили за 'пульсом' процессора и перезапускали его в случае зависания. В современных системах автоматизации этот принцип успешно применяется на программном уровне.

Watchdog Timer (WDT) — это механизм контроля, предназначенный для автоматического обнаружения и реагирования на зависания программ или отказ оборудования. Его работа основана на простой, но чрезвычайно надежной аналогии со сторожевым псом: система или её компонент, за которым ведется наблюдение, должен регулярно посылать сигнал "я в порядке". Этот сигнал принято называть сигналом "пульса" или heartbeat.

"Сторожевой пёс" представляет собой таймер, который непрерывно отсчитывает время в обратном порядке. Каждый приходящий heartbeat-сигнал сбрасывает этот таймер на его первоначальное значение. Если по какой-либо причине сигнал не приходит в течение установленного времени (таймаута), таймер достигает нуля. Это событие называется "укусом сторожевого пса" (watchdog bite) и инициирует заранее определённую процедуру восстановления.

В контексте платформы автоматизации HI, основанной на контроллерах с Linux и Node-RED, WDT помогает обнаруживать и обрабатывать несколько типов сбоев:

  • Отказ оконечного устройства: Modbus-счетчик перестал отвечать из-за сбоя питания, датчик Zigbee "отвалился" от сети, IP-камера зависла. Устройство перестает отправлять свои данные, heartbeat-сигналы прекращаются, и WDT фиксирует сбой.
  • Зависание сценария или потока: В узле `Function` произошла логическая ошибка, приведшая к бесконечному циклу. Или внешний API-запрос (например, к сервису погоды) "завис" и не возвращает ни ответа, ни ошибки. Сценарий перестает функционировать и генерировать heartbeat, что позволяет WDT обнаружить проблему.
  • Потеря сетевого соединения: Произошел обрыв кабеля Ethernet или сбой в работе Wi-Fi роутера. Устройства, подключенные по сети (MQTT-клиенты, Modbus TCP устройства), теряют связь с контроллером. WDT, не получая от них данных, сигнализирует о неисправности.
  • Важность применения WDT напрямую зависит от критичности контролируемой системы. Для второстепенных задач, таких как сбор статистики по влажности в кладовой, его отсутствие не приведет к катастрофе. Однако для ключевых подсистем умного дома или промышленного объекта использование Watchdog является обязательным элементом обеспечения отказоустойчивости:

    Таким образом, Watchdog Timer является не просто инструментом, а фундаментальной концепцией построения надежных систем, позволяющей перейти от пассивного ожидания сбоя к его проактивному обнаружению и автоматической нейтрализации.

    ---

    Подготовка к работе

    > ℹ️ Необходимые компоненты: Для выполнения практической части этого урока убедитесь, что у вас установлен узел `node-red-contrib-watchdog` через `Меню -> Manage Palette -> Install`.

    ---

    Программная реализация: Узел `node-red-contrib-watchdog`

    Для реализации программного WDT в среде Node-RED используется специализированный узел из палитры `node-red-contrib-watchdog`. Он полностью инкапсулирует логику таймера, сброса и "укуса", позволяя инженерам сосредоточиться на логике восстановления.

    > 💡 Подсказка: Выбирайте таймаут WDT с запасом. Он должен быть как минимум в 2-3 раза больше максимального ожидаемого интервала между heartbeat-сообщениями, чтобы избежать ложных срабатываний из-за кратковременных сетевых задержек. Например, если устройство шлет данные каждые 30 секунд, установите таймаут на 90-120 секунд.

    Установка и базовые настройки

    Установка узла стандартна и производится через редактор Node-RED: `Меню -> Manage Palette -> Install` и введите в поиске `node-red-contrib-watchdog`.

    После установки узел `watchdog` появляется в палитре. Рассмотрим его ключевые настройки:

    Формирование 'heartbeat' сообщений

    Узел `watchdog` устроен максимально просто: любое сообщение, пришедшее на его вход, считается heartbeat-сигналом и сбрасывает таймер. Содержимое `msg.payload` при этом игнорируется, но само сообщение `msg` проходит без изменений на первый выход узла. Это позволяет встраивать `watchdog` в существующие потоки, не нарушая их логику.

    [Источник данных] --(heartbeat)--> [watchdog] --(данные без изменений)--> [Дальнейшая обработка]
    

    |

    +---("укус" при сбое)--> [Логика восстановления]

    Обработка 'укуса' (watchdog bite)

    Самая важная часть — это то, что происходит, когда таймер не был сброшен вовремя. В этом случае `watchdog` генерирует новое сообщение на своем втором выходе. Это и есть "укус".

    Сообщение, генерируемое при "укусе", имеет следующую структуру:

    {
    

    "payload": "Watchdog timeout! Name: 'WDT: Датчик CO2'",

    "topic": "",

    "_msgid": "..."

    }

    Именно это сообщение и становится триггером для запуска сценариев восстановления: отправки уведомлений, перезагрузки оборудования, перевода системы в безопасное состояние и журналирования инцидента.

    ---

    Пример 1: Контроль доступности MQTT-устройства

    Рассмотрим самый распространенный сценарий: мониторинг доступности беспроводного датчика, который регулярно (например, раз в 60 секунд) отправляет свои показания по протоколу MQTT.

    Задача: Контролировать доступность датчика температуры и влажности в гостиной. Если данные от него не поступают в течение 3 минут, отправить тревожное уведомление в Telegram и записать инцидент в базу данных MySQL на контроллере. В качестве дополнительной меры, попытаться перезагрузить датчик, если он запитан от управляемой розетки. Flow Diagram:
    // Поток мониторинга
    

    [mqtt in]------------------>[watchdog]----->[debug: Heartbeat OK]

    (telemetry/livingroom/th) |

    +---(timeout)-->[change: Format Alert]--+-->[telegram sender]

    |

    +-->[function: SQL Log]--->[mysql]

    |

    +-->[change: Reboot Cmd]-->[mqtt out]

    (commands/plugs/plug-05/set)

    Реализация потока

  • `mqtt in` (Источник Heartbeat):
  • * Server: Выбираем MQTT-брокер контроллера HI.

    * Topic: `telemetry/livingroom/th` (или топик вашего датчика).

    * QoS: `0` или `1`.

  • `watchdog` (Сторожевой пёс):
  • * Timeout: `3`

    * Units: `Minutes`

    * Name: `WDT: Датчик Т/H в гостиной`

    * Соединяем `mqtt in` с входом `watchdog`. Первый выход `watchdog` можно подключить к узлу `debug` для визуального контроля проходящих heartbeat'ов.

  • `change` (Форматирование тревоги):
  • * Этот узел подключается ко второму выходу `watchdog`.

    * Name: `Format Alert Msg`

    * Правило: `Set` `msg.payload` `to` `⚠️ Тревога! Датчик температуры/влажности в гостиной не отвечает более 3 минут. Возможен сбой или разряд батареи.`

  • `telegram sender` (Уведомление):
  • * Настраиваем узел для отправки `msg.payload` в нужный чат.

  • `function` (Журналирование в БД):
  • * Name: `SQL Log Event`

    * Код для формирования SQL-запроса для записи в таблицу `audit_log`:

        const device_id = "th-sensor-livingroom";

    const event_type = "device_offline";

    const description = msg.payload; // Берем текст из предыдущего узла

    msg.topic = `INSERT INTO audit_log (timestamp, device_id, event_type, description) VALUES (NOW(), ?, ?, ?)`;

    msg.payload = [device_id, event_type, description];

    return msg;

    * Этот узел подключается к узлу `mysql` для выполнения запроса.

  • `change` + `mqtt out` (Сценарий восстановления):
  • * Name: `Prepare Reboot Cmd`

    * Правило: `Set` `msg.payload` `to` JSON:

        {

    "value": "REBOOT",

    "source": "watchdog-th-livingroom",

    "ts": 1678886400000

    }

    > ℹ️ Информация: Контракт сообщения для перезагрузки может отличаться. Некоторые розетки ожидают `{"state":"OFF"}` затем `{"state":"ON"}`. Здесь мы используем гипотетический контракт с командой `REBOOT`.

    * `mqtt out`:

    * Topic: `commands/plugs/plug-05/set` (топик управления розеткой, питающей датчик).

    * Соединяем выход узла `change` с этим узлом.

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

    ---

    Пример 2: Контроль зависания сложного сценария в Function-узле

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

    > ⚠️ Внимание: Неправильное размещение heartbeat-сигналов внутри кода может маскировать проблему. Размещайте их после потенциально долгих или рискованных операций (например, внешние API-вызовы), а не в начале функции.

    Задача: У нас есть сложный узел `Function`, который раз в 5 минут выполняет серию HTTP-запросов к разным внешним API (например, погода, курсы валют, дорожная обстановка), агрегирует данные и формирует отчет. Этот процесс может занять до 30 секунд. Если узел не завершит работу за 60 секунд, нужно считать его "зависшим", отправить уведомление и установить для всех связанных с ним устройств безопасное состояние.

    Реализация потока

  • Модификация узла `Function`:
  • * Сделаем у узла `Function` два выхода. Первый выход (индекс 0) — для результата работы. Второй (индекс 1) — для отправки heartbeat-сигналов.

  • Построение потока:
  •     // Поток контроля сценария

    [inject: раз в 5 мин] ---> [Function: Сбор данных с API] --+-- (результат) --> [Обработка отчета]

    | |

    '--(heartbeat)----------------+--> [watchdog]

    |

    +-- (timeout) --> [Сценарий восстановления]

  • Код узла `Function`:
  • * Name: `Сбор данных с API`

    * Outputs: `2`

    * Код (псевдокод для наглядности):

        // Отправляем первый heartbeat в самом начале, чтобы показать, что мы стартовали

    node.send([null, { payload: "heartbeat_start" }]);

    try {

    // Имитация долгого API-запроса №1

    const weather_data = await some_async_http_request("api.weather.com");

    // Отправляем heartbeat после первого успешного шага

    node.send([null, { payload: "heartbeat_step1" }]);

    // Имитация долгого API-запроса №2

    const traffic_data = await some_async_http_request("api.traffic.com");

    // Отправляем heartbeat после второго успешного шага

    node.send([null, { payload: "heartbeat_step2" }]);

    // Формируем финальный отчет

    const report = { weather: weather_data, traffic: traffic_data };

    // Отправляем результат на первый выход

    node.send([{ payload: report }, null]);

    } catch (error) {

    node.error("Ошибка при сборе данных с API: " + error.message, msg);

    // В случае ошибки не отправляем результат, поток прервется

    // Watchdog сработает, если ошибка вызовет "зависание"

    }

    // `return;` используется, т.к. мы отправляем сообщения через node.send()

    return;

  • `watchdog`:
  • * Timeout: `60`

    * Units: `Seconds`

    * Name: `WDT: Сценарий API`

    * Подключаем второй выход `Function` ко входу `watchdog`.

  • Сценарий восстановления:
  • * Ко второму выходу `watchdog` подключаем цепочку, аналогичную первому примеру:

    * Отправка критического уведомления: `⚠️ Тревога! Сценарий сбора данных с API завис и не отвечает более 60 секунд.`.

    * Журналирование инцидента.

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

    Этот подход позволяет контролировать "здоровье" не только внешних устройств, но и внутренней логики самой системы автоматизации.

    ---

    Системный Watchdog в контроллерах HI на базе Linux

    До сих пор мы рассматривали программные WDT на уровне Node-RED. Но что, если "зависнет" не отдельный сценарий, а весь процесс Node-RED целиком? Или сам контроллер войдет в неработоспособное состояние? Для этого существует следующий, более глубокий уровень защиты — системный Watchdog.

    > 🔗 Связанный материал: Подробная работа с сервисами systemd для обеспечения стабильности разбирается в курсе по администрированию контроллеров HI: `COURSE-12-M01-L04`.

    Следует четко различать уровни:

    На контроллерах HI под управлением Debian Linux роль системного "сторожевого пса" для процессов выполняет сервисный менеджер `systemd`. Он является процессом с PID 1 и отвечает за запуск, остановку и мониторинг всех системных служб.

    `systemd` может автоматически перезапускать сервисы, которые завершили свою работу с ошибкой. Это стандартная практика для обеспечения стабильности работы Node-RED на производственных объектах.

    Конфигурация сервиса Node-RED обычно находится в файле `/lib/systemd/system/nodered.service`. Для обеспечения автоматического перезапуска достаточно убедиться, что в секции `[Service]` присутствуют следующие директивы:

    [Unit]
    

    Description=Node-RED

    After=syslog.target network.target

    [Service]

    ExecStart=/usr/bin/node-red-start

    # Пользователь, от имени которого запускается Node-RED

    User=nodered

    Group=nodered

    Restart=on-failure # <-- Ключевая директива

    RestartSec=20s # Пауза перед перезапуском

    [Install]

    WantedBy=multi-user.target

    Директива `Restart=on-failure` указывает `systemd`: "Если процесс Node-RED завершится с кодом ошибки (т.е. упадет), подожди 20 секунд (`RestartSec=20s`) и запусти его снова".

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

    ---

    Резюме и лучшие практики

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

    📋 Ключевые понятия:

    Лучшие практики:

  • Применяйте эшелонированную оборону: Не полагайтесь на один уровень защиты. В идеальной системе должен быть:
  • * Уровень 1 (Устройство/Сценарий): Программный WDT в Node-RED (`node-red-contrib-watchdog`) для контроля каждого критичного датчика, исполнительного механизма или сложного сценария.

    * Уровень 2 (Процесс): Системный WDT на уровне `systemd` для автоматического перезапуска всего сервиса Node-RED в случае его падения.

    * Уровень 3 (Система): Аппаратный WDT самого контроллера (если он есть), который перезагрузит всю систему в случае зависания операционной системы.

  • Автоматизируйте восстановление: Обнаружение сбоя — это только половина дела. Ценность WDT раскрывается полностью, когда его "укус" запускает четкую и автоматизированную процедуру: уведомить администратора, записать инцидент в лог, перевести систему в безопасный режим, попытаться перезагрузить отказавший компонент.
  • WDT — это детектор, а не лекарство: Срабатывание WDT — это всегда симптом более глубокой проблемы (нестабильный код, плохая сеть, ненадежное оборудование). Всегда анализируйте логи после срабатывания "сторожа", чтобы найти и устранить первопричину сбоя. Главный приоритет — это написание стабильного кода и построение надежной физической инфраструктуры.
  • Что дальше

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