Создание универсального subflow для антидребезга
Введение в Subflows: инкапсуляция логики и переиспользование
В процессе разработки сложных систем автоматизации на платформе Node-RED инженеры неизбежно сталкиваются с повторяющимися задачами: обработка данных с однотипных датчиков, управление стандартными исполнительными механизмами, реализация типовых алгоритмов. Копирование и вставка групп узлов для решения этих задач — путь к созданию запутанных, труднообслуживаемых и подверженных ошибкам проектов. Решением этой проблемы является Subflow (подпоток).
Subflow — это механизм Node-RED, позволяющий инкапсулировать, или "упаковать", последовательность узлов и их логику в единый, переиспользуемый компонент. Его можно рассматривать как аналог функции или процедуры в традиционных языках программирования. Создав Subflow один раз, вы можете многократно использовать его в разных частях вашего проекта, как если бы это был стандартный узел из палитры.> 💡 Подсказка: Используйте Subflow, если планируете применять одну и ту же логику более двух раз в проекте. Это значительно упростит дальнейшее обслуживание и обновление системы.
Преимущества использования Subflows
Структура Subflow
Subflow имеет четко определенную структуру, которая отличает его от простой визуальной группы узлов.
| Элемент | Описание |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Узел `input` | Определяет одну или несколько точек входа для сообщений (`msg`) в Subflow. Каждое входящее сообщение поступает на этот узел и передается дальше по внутренней логике. |
| Узел `output` | Определяет одну или несколько точек выхода. Когда сообщение достигает этого узла, оно покидает Subflow и передается на узлы, подключенные к выходу экземпляра Subflow в основном потоке. |
| Узел `status` | Особый узел, который позволяет экземпляру Subflow отображать свой собственный статус (цвет, форма, текст) в основном потоке, так же как это делают стандартные узлы. Незаменим для визуальной диагностики. |
| Панель свойств | Позволяет определить настраиваемые параметры для каждого экземпляра Subflow. Эти параметры называются переменными окружения (environment variables) и делают Subflow по-настоящему гибким и универсальным. |
| Вкладка `Appearance` | Содержит поля для задания иконки, цвета, имени и, что самое важное, документации для вашего Subflow. Текст, введенный здесь, будет отображаться в стандартной панели "Info" Node-RED. |
Ключевое отличие от групп: группа — это лишь визуальное объединение на холсте, она не создает нового узла и не позволяет централизованно управлять логикой. Subflow — это шаблон, на основе которого создаются независимые, но связанные с шаблоном экземпляры.
---
Проектирование универсального Subflow для антидребезга
Прежде чем приступать к созданию Subflow, необходимо четко спроектировать его внутреннюю логику и внешний интерфейс. Наша задача — создать универсальный фильтр дребезга, который можно будет применять для любых дискретных сигналов: кнопок, выключателей, герконов на дверях, реле давления и других "сухих контактов".
Как мы рассмотрели в предыдущих уроках (`LESSON-04-M03-L02` и `LESSON-04-M03-L03`), для борьбы с дребезгом можно использовать узлы `Delay` и `Trigger`. Узел `Trigger` является более гибким и мощным инструментом для этой задачи, поэтому он ляжет в основу нашего решения. Однако сам по себе он не решает проблему множественных срабатываний одного и того же состояния.
Выбор оптимальной комбинации узлов
Для создания надежного и универсального фильтра мы скомбинируем два узла, каждый из которых выполняет свою важную функцию:
Логика работы будет следующей:
`Сырой сигнал -> rbe (блокировка дублей) -> trigger (ожидание стабилизации) -> Чистый сигнал`
Определение настраиваемых параметров
Чтобы наш Subflow был универсальным, он должен быть настраиваемым. Разные физические устройства имеют разное время дребезга. Для быстрой кнопки может быть достаточно задержки в 20 мс, а для медленного контактора — 100 мс или больше.
> 📋 Ключевые понятия:
> Главным настраиваемым параметром нашего Subflow будет время задержки фильтрации (debounce time). Мы вынесем его в переменные окружения Subflow, чтобы инженер мог задавать это значение прямо в настройках узла-экземпляра, не погружаясь в его внутреннюю реализацию.
Это позволит использовать один и тот же Subflow для разных задач: один экземпляр с задержкой 50 мс для настенного выключателя, другой — с задержкой 200 мс для датчика уровня воды с поплавком.
Проектирование входов и выходов
Контракт нашего Subflow должен быть простым и понятным:
- Вход (1): Принимает "сырой" сигнал от датчика. Для универсальности будем считать, что он может приходить в любом виде (например, `true`/`false`, `1`/`0`, `"ON"`/`"OFF"`). Первый узел внутри Subflow будет приводить эти значения к стандартному формату (например, `1` и `0`).
- Выход (1): Отдает отфильтрованный, стабильный сигнал. На выходе всегда будет только одно сообщение после завершения всего процесса дребезга. Стандартизируем выходной `msg.payload` в виде чисел `1` (включено) и `0` (выключено). Это упрощает дальнейшую логику.
Дополнительно можно предусмотреть второй выход для логирования или отладки, который будет выдавать информацию о заблокированных сообщениях, но для основной версии ограничимся одним выходом для чистоты интерфейса.
---
Практика: создание Subflow с настраиваемой задержкой
Теперь перейдем от проектирования к реализации. Мы создадим Subflow `DEBOUNCE-DIGITAL-INPUT`, который реализует описанную выше логику.
Шаг 1: Создание пустого Subflow
Шаг 2: Построение внутренней логики
Перетащите из палитры и соедините узлы в следующей последовательности:
[input 1] -> [rbe] -> [trigger] -> [output 1]
Эта простая цепочка и есть скелет нашего фильтра.
Шаг 3: Настройка узлов
Теперь детально настроим каждый узел внутри Subflow.
1. Узел `rbe` (режим "block unless value changes"):- Mode: `block unless value changes`. Это стандартный режим, который нас полностью устраивает. Он будет пропускать первое сообщение, а затем все последующие, только если их `msg.payload` изменился.
- Property: Оставьте `msg.payload`.
- Name: "Блокировать дубли".
Это самый важный узел. Его настройка определяет поведение всего фильтра.
- Send: выберите `the latest msg`. Это гарантирует, что мы отправим на выход последнее, устоявшееся состояние контакта.
- then wait for: `...` (поле для времени). Здесь мы не будем вводить конкретное число. Вместо этого мы используем синтаксис для переменной окружения: `$(DEBOUNCE_MS)`.
- Extend delay if new message arrives: Установите галочку. Это ключевая настройка! Она означает, что если во время ожидания придет новое сообщение (еще один "дребезг"), таймер сбросится и начнет отсчет заново. Сигнал на выход будет отправлен только тогда, когда поток "дребезга" прекратится на время, указанное в `DEBOUNCE_MS`.
- Name: "Фильтр задержки".
Пример конечного `msg` от узла `trigger` будет содержать `payload`, который был у последнего сообщения, пришедшего на его вход.
Шаг 4: Настройка свойств Subflow
Теперь сделаем наш параметр `DEBOUNCE_MS` доступным для настройки извне.
* Property Name: `DEBOUNCE_MS`. Это имя должно точно совпадать с тем, что мы указали в узле `trigger` (`$(DEBOUNCE_MS)`).
* UI Label: `Задержка, мс`. Это та надпись, которую инженер увидит в настройках узла.
* Type: `number` (число).
* Default Value: `50`. Это значение будет использоваться, если инженер не задаст свое. 50 мс — хорошее универсальное значение для начала.
* Icon: Можете выбрать иконку, например, `fa-clock-o`.
После этого нажмите `Done`. Теперь наш Subflow готов. Переименуйте его (например, в `DEBOUNCE-DIGITAL-INPUT`) и нажмите `Close` для сохранения. В вашей палитре узлов (в категории `subflows`) появится новый узел с заданным именем.
// Пример входящего потока сообщений от дребезжащей кнопки.
// Интервал между сообщениями ~5-15 мс.
msg.payload: 1
msg.payload: 0
msg.payload: 1
msg.payload: 0
msg.payload: 1
msg.payload: 1 // Узел rbe заблокирует это сообщение
// Пример msg, который будет на выходе из Subflow DEBOUNCE-DIGITAL-INPUT
// после того, как пройдет 50 мс с момента прихода последнего сообщения.
{
"payload": 1,
"topic": "",
"_msgid": "..."
// ... другие свойства оригинального последнего сообщения
}
---
Пример: интеграция и тестирование антидребезга на 'сухом контакте'
Рассмотрим, как применить созданный Subflow для фильтрации сигнала от физической кнопки, подключенной к универсальному входу `UI-05` контроллера HI.
Шаг 1: Интеграция Subflow в основной поток
Шаг 2: Настройка экземпляра
Дважды щелкните по узлу Subflow в вашем потоке. Откроется окно его свойств. Вы увидите поле `Задержка, мс` со значением по умолчанию `50`. Для нашей кнопки, которая оказалась довольно "шумной" в ходе тестов, мы установим значение `70`.
> ⚠️ Внимание: Не устанавливайте слишком большое время задержки для датчиков, требующих быстрой реакции (например, датчик протечки). Для каждого типа сигнала подбирайте минимально необходимое значение антидребезга, используя тестовый стенд, разработанный нами в уроке `LESSON-04-M03-L04`. Слишком большая задержка может быть воспринята пользователем как "торможение" системы.
Шаг 3: Тестирование и сравнение "до" и "после"
Чтобы наглядно увидеть результат работы фильтра, построим простую тестовую схему:
+-> [debug] (Название: "Сырой сигнал")
|
[rpi gpio in] (UI-05)-+
|
+-> [DEBOUNCE-DIGITAL-INPUT] -> [debug] (Название: "Чистый сигнал")
(Задержка, мс: 70)
Вы увидите примерно следующую картину:
// Логи от "Сырой сигнал"
11.03.2024, 15:30:01.102, msg: 1
11.03.2024, 15:30:01.108, msg: 0
11.03.2024, 15:30:01.115, msg: 1
11.03.2024, 15:30:01.124, msg: 0
11.03.2024, 15:30:01.131, msg: 1
// Лог от "Чистый сигнал" (появится через 70 мс после последнего "сырого" сообщения)
11.03.2024, 15:30:01.201, msg: 1
Как видите, вместо хаотичного потока из пяти сообщений за 30 миллисекунд, наша основная логика, подключенная после Subflow, получит только одно, стабильное и достоверное сообщение `msg.payload: 1`. Задача решена. Мы создали надежный, настраиваемый и переиспользуемый компонент.
---
Итоги и лучшие практики документирования и экспорта Subflows
В этом уроке мы сделали важный шаг от простого создания потоков к профессиональной инженерной практике — инкапсуляции логики. Создание переиспользуемых компонентов (Subflows) является одним из ключевых паттернов для построения масштабируемых и поддерживаемых систем на платформе HI. Это позволяет не только экономить время, но и повышать надежность всей системы за счет использования многократно проверенных стандартных блоков.
Лучшие практики документирования
Чтобы ваши Subflows были действительно полезны для вас и вашей команды, следуйте этим простым правилам:
- Осмысленное именование: Называйте Subflow и его переменные так, чтобы их назначение было очевидно без изучения внутренней структуры. `DEBOUNCE-DIGITAL-INPUT` лучше, чем `Subflow 1`. `DEBOUNCE_MS` лучше, чем `delay`.
- Заполнение описания: Всегда заполняйте информацию на вкладке `Appearance` -> `Info`. Опишите, что делает Subflow, какие сообщения он ожидает на входе, что отправляет на выход и как работают его настраиваемые параметры. Эта документация будет доступна прямо в редакторе Node-RED и сэкономит массу времени в будущем.
- Используйте узел `status`: Внутри сложных Subflows используйте узел `status`, чтобы он отображал свое текущее состояние. Для нашего фильтра можно было бы добавить статус "filtering..." на время ожидания.
Экспорт и версионирование
- Экспорт для переиспользования: Любой Subflow можно экспортировать в JSON-файл. Для этого в меню `Subflows` выберите нужный и нажмите `export`. Полученный файл можно импортировать в любой другой проект Node-RED, мгновенно добавляя в него вашу наработку. Это основа для создания общей библиотеки стандартных компонентов (`SCN-` / `FLOW-` ID).
- Версионирование: Если вы планируете внести в Subflow значительные изменения, которые могут нарушить обратную совместимость (например, изменить количество входов/выходов или смысл payload), лучшей практикой будет не изменять текущий Subflow, а создать его копию и назвать ее с суффиксом версии, например, `DEBOUNCE-DIGITAL-INPUT-V2`. Это предотвратит "поломку" уже работающих частей проекта, которые использовали старую версию.
Что дальше?
Теперь, когда у нас есть надежный инструмент для фильтрации дискретных сигналов, мы готовы перейти к работе с более сложными типами входов. В следующем модуле мы погрузимся в мир аналоговых сигналов (0-10В, 4-20мА) и научимся их калибровать, масштабировать и фильтровать от помех.