ГлавнаяАкадемияИсполнительные устройства: интерлоки, таймауты → Отлов ошибок выполнения с помощью ноды Catch

Отлов ошибок выполнения с помощью ноды Catch

Урок 1 · Исполнительные устройства: интерлоки, таймауты · 30 мин · theory

Введение в обработку ошибок в Node-RED

🔗 Связанный материал: Эта концепция является логическим продолжением урока COURSE-05-M05-L01, где мы ввели понятие 'Безопасного состояния' (Safe-State). Отлов ошибок — это механизм, который запускает переход в это состояние.

Любая система автоматизации, от простого умного дома до сложного промышленного объекта, является надежной ровно настолько, насколько надежен ее самый слабый компонент. В программном обеспечении таким "слабым звеном" часто становятся непредвиденные сбои: отключение устройства, получение некорректных данных или внутренние ошибки в логике. Игнорирование возможности таких сбоев приводит к созданию хрупких, непредсказуемых систем, которые могут отказать в самый критический момент.

Критическая важность обработки ошибок заключается в обеспечении стабильности и предсказуемости поведения контроллера. Когда в одной из нод потока (flow) возникает ошибка — например, нода `modbus-read` не может связаться с устройством из-за обрыва шины RS-485 — происходит следующее:

  • Выполнение потока прерывается. Сообщение `msg` не передается дальше по цепочке. Логика, которая должна была быть выполнена после сбойной ноды, никогда не будет запущена.
  • Информация об ошибке выводится в системный журнал. В боковой панели отладки (Debug sidebar) появляется запись об ошибке, а также она логируется в консоль операционной системы контроллера.
  • Это стандартное поведение является необработанным исключением. Система сообщает о проблеме, но не предпринимает никаких действий для ее компенсации. Для примера с Modbus-устройством это означает, что система будет продолжать "думать", что у реле или датчика последнее известное состояние, хотя на самом деле связь с ним потеряна. Это может привести к опасным ситуациям: система может считать, что клапан подачи воды закрыт, в то время как она потеряла возможность им управлять.

    Обработанное исключение — это когда мы предвидим возможность сбоя и создаем специальную логику для реакции на него. Именно для этого в Node-RED существует нода `Catch`.

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

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

    ---

    Нода Catch: Ваша основная сеть безопасности

    Нода `Catch` (в русской локализации "Перехват") — это специальная нода в палитре Node-RED, которая не имеет входного порта. Ее единственная задача — "слушать" возникновение ошибок в определенной области и, при их возникновении, создавать новое сообщение `msg`, содержащее всю информацию о сбое. Она действует как глобальная сеть безопасности, которая ловит все, что пошло не так.

    Принцип работы и конфигурация

    При размещении на листе нода `Catch` выглядит просто, но ее возможности определяются в окне конфигурации. Главный параметр — `Scope` (Область действия).

    | Scope / Область действия | Описание | Применение |

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

    | All nodes (Все ноды) | Нода будет перехватывать ошибки, сгенерированные любой нодой на всех листах (вкладках) вашего проекта Node-RED. | Идеально для создания центрального потока логирования. Одна такая нода для всего проекта. |

    | Current flow (Текущий лист) | Нода будет перехватывать ошибки только от нод, расположенных на том же листе (вкладке), что и сама нода `Catch`. | Полезно для логики, специфичной для конкретного функционального блока (например, "Управление климатом"). |

    | Selected nodes (Выбранные ноды) | Позволяет вручную выбрать из списка конкретные ноды, ошибки от которых нужно перехватывать. После выбора нода `Catch` будет игнорировать ошибки от всех остальных нод. | Для создания высокоприоритетной реакции на сбои только критически важного оборудования. |

    Структура сообщения от ноды `Catch`

    Когда нода `Catch` перехватывает ошибку, она генерирует новый объект `msg`. Ключевая информация находится в `msg.error`. Рассмотрим его структуру на примере.

    {
    

    "_msgid": "a1b2c3d4.e5f6g7",

    "topic": "",

    "payload": "Error: Timed out",

    "error": {

    "message": "Error: Timed out",

    "source": {

    "id": "12345678.9abcdef",

    "type": "modbus-read",

    "name": "Опрос счетчика электроэнергии",

    "count": 1

    }

    }

    }

    📋 Ключевые понятия в `msg.error`:

    * `id`: Уникальный идентификатор ноды, сгенерировавшей ошибку. По этому ID можно найти ноду в проекте.

    * `type`: Тип ноды (`function`, `mqtt in`, `modbus-read`). Помогает понять, с каким классом устройств или логики возникла проблема.

    * `name`: Имя, которое вы присвоили ноде в редакторе. Это критически важно! Всегда давайте нодам осмысленные имена (`"Управлять клапаном отопления"`, а не просто `"function"`), чтобы по логам можно было сразу понять, что сломалось.

    > ℹ️ Информация: В объекте `msg` от ноды `Catch` также может присутствовать копия оригинального сообщения, которое вызвало ошибку. Это полезно для отладки, так как позволяет увидеть, какие именно данные "сломали" поток.

    `Catch` vs `Status`: в чем разница?

    Очень важно не путать ноду `Catch` с нодой `Status`.

    Нода `Status` реагирует на изменение состояния* ноды, которое разработчик этой ноды предусмотрел (`connecting`, `connected`, `disconnected`). Она используется для мониторинга штатной работы. Нода `Catch` реагирует на нештатные исключения и сбои*, которые приводят к прерыванию потока.

    Представьте MQTT-ноду. `Status` сообщит вам, когда она успешно подключилась к брокеру или планово отключилась. `Catch` сработает, если при попытке публикации сообщения в брокере произойдет сбой сети, и нода сгенерирует ошибку.

    ---

    Практика: Отлов таймаута Modbus RTU

    Теория важна, но понимание приходит с практикой. Давайте смоделируем одну из самых частых проблем на объектах: потерю связи с устройством на шине RS-485. Мы будем использовать встроенный порт RS-485 контроллера HI и любое Modbus RTU устройство (например, релейный модуль или счетчик).

    ⚠️ Внимание: Не все ноды генерируют ошибки одинаково. Формат `msg.error` может отличаться. Всегда сверяйтесь с документацией конкретной ноды (`node-red-contrib-modbus`, `node-red-contrib-knx-ultimate` и т.д.), чтобы понимать, какие ошибки она может выдавать. Ошибки от Modbus, KNX и DALI будут иметь разную структуру.

    План эксперимента

  • Собрать стенд: Подключить Modbus RTU устройство к порту RS-485 контроллера HI. Настроить параметры шины (скорость, четность) и убедиться, что в штатном режиме данные читаются.
  • Создать поток опроса: В Node-RED создать простой поток с нодами `Inject`, `modbus-read` и `Debug`.
  • Настроить отлов ошибок: Добавить на лист ноду `Catch` и подключить ее к ноде `Debug`.
  • Симулировать сбой: Физически отключить провода A/B шины RS-485 от клемм контроллера.
  • Проанализировать результат: Изучить сообщение, которое появится в панели отладки от ноды `Catch`.
  • Пошаговая реализация

  • Создаем поток опроса:
  • * `Inject`: Настройте на срабатывание каждые 5 секунд.

    * `modbus-read`: Настройте его на чтение любого регистра с вашего Modbus-устройства. Дайте ноде осмысленное имя, например, "Опрос реле MR-8".

    * `Debug`: Подключите к выходу `modbus-read`, чтобы видеть успешные ответы.

  • Добавляем ноду `Catch`:
  • * Перетащите ноду `Catch` на лист.

    * В настройках оставьте `Scope: Current flow`.

    * Подключите ее выход к другой ноде `Debug`, которую для ясности можно назвать "ЛОГ ОШИБОК".

    Ваш поток должен выглядеть примерно так:

        [Inject] ---> [modbus-read: "Опрос реле MR-8"] ---> [Debug: "Успешный ответ"]

    [Catch] ---> [Debug: "ЛОГ ОШИБОК"]

  • Запускаем и симулируем сбой:
  • * Разверните (Deploy) проект. В панели отладки вы должны видеть регулярные сообщения от ноды `"Успешный ответ"`.

    * Теперь физически отсоедините кабель RS-485 от контроллера.

    * Через несколько секунд нода `Inject` снова отправит сигнал, `modbus-read` попытается опросить устройство, но не сможет. Произойдет таймаут.

  • Анализ ошибки:
  • Нода `"Успешный ответ"` замолчит. Вместо этого в панели отладки вы увидите сообщение от ноды `"ЛОГ ОШИБОК"`. Его структура будет похожа на эту:

        {

    "_msgid": "...",

    "error": {

    "message": "Port Not Open",

    "source": {

    "id": "f4b3a2d1.cba987",

    "type": "modbus-read",

    "name": "Опрос реле MR-8"

    },

    "originalMessage": {

    "payload": 1678886400000,

    "topic": "",

    "_msgid": "..."

    }

    }

    }

    Из этого объекта мы можем извлечь бесценную информацию для диагностики:

    * `error.message`: "Port Not Open" или "Timed out" — четко указывает на проблему со связью.

    * `error.source.name`: "Опрос реле MR-8" — мы точно знаем, какое именно устройство перестало отвечать. Если бы у нас было 20 Modbus-устройств, это имя позволило бы мгновенно локализовать проблему.

    * `error.source.type`: "modbus-read" — подтверждает, что проблема связана с протоколом Modbus.

    Теперь мы не просто знаем, что что-то сломалось. Мы знаем, что именно сломалось и почему. Это и есть первый шаг к построению по-настоящему надежной системы.

    ---

    Построение потока для перехода в 'Safe-State'

    Отловить ошибку — это лишь половина дела. Вторая, и самая важная половина, — правильно на нее отреагировать. Теперь мы объединим ноду `Catch` с концепцией "Безопасного состояния".

    Задача: При потере связи с критически важным Modbus-модулем, который управляет приточной вентиляцией, мы должны немедленно перевести систему климата в безопасный режим: выключить все нагреватели и закрыть все клапаны, управляемые через другую шину, например, DALI или KNX, чтобы предотвратить перегрев или работу системы вразнос.

    Логика потока "Аварийный Менеджер"

    Этот поток начинается с ноды `Catch` и использует ноду `Switch` для маршрутизации в зависимости от источника ошибки.

                                    +--> [Function: "Формировать команду ВЫКЛ для DALI"] ---> [dali_out]
    

    |

    [Catch] --> [Switch: "Фильтр ошибок"] --+--> [Function: "Формировать команду ВЫКЛ для KNX"] ---> [knx_out]

    |

    +--> [Function: "Отправить уведомление в Telegram"] ---> [telegram_sender]

  • `Catch` (Scope: All nodes): Служит единой точкой входа для всех ошибок в проекте.
  • `Switch` (Имя: "Фильтр ошибок"): Это "мозг" нашего аварийного менеджера. Он анализирует `msg.error` и решает, что делать.
  • * Свойство для проверки: `msg.error.source.name`

    * Правила:

    * `содержит` "вентиляция" -> выход 1

    * `содержит` "управление светом" -> выход 2

    * `иначе` -> выход 3 (для всех остальных ошибок)

  • `Function` (Имя: "Подготовка Safe-State команды"): Эта нода получает сообщение об ошибке и формирует из него команду для исполнительных устройств.
  • Пример кода для ветки, реагирующей на сбой модуля вентиляции:

        // Сообщение пришло из ноды Catch после сбоя Modbus

    // Анализируем ошибку для аудита (не обязательно для работы, но полезно)

    let sourceName = msg.error.source.name;

    let errorMessage = msg.error.message;

    node.warn(`КРИТИЧЕСКИЙ СБОЙ: Потеря связи с '${sourceName}'. Причина: ${errorMessage}. Активация Safe-State.`);

    // Формируем новую команду для выключения ВСЕХ нагревателей на шине KNX.

    // Мы создаем полностью новый объект msg, т.к. старый содержит инфо об ошибке.

    // Контракт сообщения для ноды KNX:

    // msg.payload = false; (для выключения)

    // msg.topic = '0/1/1'; (групповой адрес KNX)

    // Возвращаем массив сообщений, чтобы одной функцией выключить несколько групп.

    const safeStateCommands = [

    { topic: '1/1/0', payload: false, knx: {dpt: '1.001'} }, // Группа KNX "Нагреватель 1 этаж"

    { topic: '1/1/1', payload: false, knx: {dpt: '1.001'} }, // Группа KNX "Нагреватель 2 этаж"

    { topic: '2/3/5', payload: 0, knx: {dpt: '5.001'} } // Группа KNX "Клапан вентиляции" (0%)

    ];

    // node.send() позволяет отправить несколько сообщений из одной функции.

    // Каждое сообщение выйдет из ноды и будет обработано дальше.

    node.send(safeStateCommands);

    // Мы не возвращаем ничего через return, т.к. использовали node.send().

    return null;

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

    ---

    Фильтрация ошибок и ограничение области действия

    По мере усложнения проекта, одна нода `Catch`, настроенная на `All nodes`, может стать источником "шума". Некоторые ошибки могут быть ожидаемыми или некритичными, и мы не хотим, чтобы они запускали аварийные сценарии. Здесь на помощь приходит более тонкая настройка области действия.

    > 💡 Подсказка: На реальных объектах рекомендуется иметь как минимум две ноды `Catch`: одну общую (`Scope: All nodes`) для всеобъемлющего логирования и одну или несколько специфичных (`Scope: Selected nodes`) для реакции на сбои критически важных нод.

    `All nodes` vs `Selected nodes`

    Таким образом, если произойдет сбой в ноде, опрашивающей декоративную подсветку, сработает только глобальный логгер. Но если откажет нода управления циркуляционным насосом, сработают обе ноды `Catch`: одна запишет это в лог, а вторая немедленно запустит аварийный сценарий.

    Как избежать "шума" от ожидаемых ошибок

    Представим сценарий, в котором вы сканируете шину RS-485 в поиске новых устройств. Ваш поток в цикле опрашивает все адреса с 1 по 247. Естественно, большинство этих запросов завершатся ошибкой `timeout`, так как на большинстве адресов устройств нет. Эти ошибки ожидаемы и не являются признаком сбоя.

    Если использовать глобальную ноду `Catch`, она будет завалена сотнями таких сообщений. Решение — локализованная обработка ошибок.

    У большинства нод ввода-вывода (включая `modbus-read`) есть второй выходной порт, который активируется при ошибке.

    [Inject: "start scan"] --> [Function: "Loop 1-247"] --> [modbus-read] --+--> [Success Logic]
    

    |

    +--> [Debug: "Error, expected"]

    В этом случае ошибка таймаута не будет "выброшена" на глобальный уровень и не будет поймана нодой `Catch`. Она выйдет через второй порт ноды `modbus-read` и будет обработана локально (в данном случае, просто проигнорирована или посчитана). Таким образом, мы отделяем ожидаемые ошибки от реальных, непредвиденных сбоев.

    ---

    Итоги и лучшие практики

    Мы рассмотрели один из самых важных механизмов обеспечения надежности в Node-RED. Правильное использование ноды `Catch` превращает хрупкую систему в отказоустойчивую.

    📋 Ключевые выводы:

  • Нода `Catch` — обязательный элемент любого надежного проекта. Отсутствие потока обработки ошибок является грубым нарушением методологии проектирования и не допускается в сертифицированных системах.
  • Всегда анализируйте `msg.error` для контекста. Недостаточно просто знать, что ошибка произошла. Важно знать, где (`source.name`) и почему (`message`), чтобы принять правильное решение. Всегда присваивайте осмысленные имена критически важным нодам.
  • Используйте отловленные ошибки для создания системы уведомлений. Инженер или владелец системы должен немедленно узнавать о критических сбоях. Поток, идущий от `Catch`, является идеальным местом для интеграции с Telegram, email или SMS-шлюзом.
  • Никогда не оставляйте поток, завершающийся нодой `Catch`, без дальнейшей логики. Ошибка — это не конец, а событие, требующее реакции. Она должна быть либо записана в лог для анализа, либо использована для запуска компенсирующих действий (переход в Safe-State), либо эскалирована до ответственного лица. Ошибка не должна "умирать" в отладочной консоли.
  • Что дальше?

    В следующем уроке мы углубимся в создание полноценного централизованного потока для журналирования (logging), используя базу данных MySQL на борту контроллера HI, чтобы создать исчерпывающий аудиторский след всех событий и ошибок в системе. Мы также рассмотрим, как использовать ноду `Status` для проактивного мониторинга состояния оборудования, не дожидаясь возникновения критических ошибок.