ГлавнаяАкадемияИсполнительные устройства: интерлоки, таймауты → Реализация блокировки с помощью нод Switch и Gate

Реализация блокировки с помощью нод Switch и Gate

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

Введение в логические блокировки в Node-RED

Взаимная блокировка, или интерлок (interlock), является краеугольным камнем в проектировании безопасных систем автоматизации. Ее основная задача — предотвратить одновременное выполнение взаимоисключающих действий, которые могут привести к повреждению оборудования, созданию опасной ситуации или просто к некорректной работе системы. Классический пример — управление реверсивным двигателем, где одновременная подача команд «Вверх» и «Вниз» вызывает короткое замыкание и выход из строя как самого двигателя, так и управляющего реле.

> 🔗 Связанный материал: Для полного понимания концепции, пожалуйста, ознакомьтесь с предыдущим уроком «Концепция взаимной блокировки (Interlock)». Мы будем опираться на изложенные в нем принципы.

Хотя многие исполнительные устройства, такие как приводы штор или ворот, имеют встроенные аппаратные защиты, полагаться только на них — рискованная стратегия. Аппаратная защита может отказать, а контроллер, не имеющий собственной логики блокировки, продолжит посылать некорректные команды, усугубляя ситуацию. Программная блокировка на уровне контроллера является первым и самым важным рубежом обороны. Она позволяет предотвратить саму возможность отправки конфликтующей команды, обеспечивая предсказуемость и надежность системы.

В среде Node-RED для реализации логических блокировок существует два основных инструмента, которые мы сегодня подробно разберем:

  • Нода `Switch`: Универсальный маршрутизатор сообщений, который может направлять или блокировать поток на основе любых данных, включая переменные состояния, хранящиеся в контексте.
  • Нода `Gate`: Специализированный узел, работающий как шлагбаум, который можно программно открывать и закрывать для определенного потока сообщений.
  • В рамках этого урока нашей главной задачей будет создание надежной программной блокировки для управления реверсивным двигателем, используя стандартные релейные выходы контроллера HI и логические возможности Node-RED.

    ---

    Нода Switch: Маршрутизация на основе состояния

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

    > 💡 Подсказка: Для наглядной отладки потоков с использованием `Switch` и контекстных переменных, активно используйте ноду `Debug`. Настройте ее на вывод «complete msg object». Это позволит вам видеть не только `payload`, но и то, как изменяются переменные контекста (`flow` или `global`) при прохождении сообщения через разные ветви логики.

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

    При настройке ноды `Switch` ключевым является поле «Property» (Свойство). По умолчанию там установлено `msg.payload`, но мы можем выбрать опцию «flow» (переменная потока) или «global» (глобальная переменная) и указать имя переменной, которую хотим проверить.

    Представим, что мы управляем рулонной шторой и храним ее текущее состояние в переменной `flow.shutter_state`. Эта переменная может принимать значения: `stopped`, `moving_up`, `moving_down`.

    Наша задача: заблокировать команду «Вниз», если штора уже движется («Вверх» или «Вниз»).

    Конфигурация ноды `Switch`, стоящей на пути команды «Вниз», будет выглядеть так:

  • Property: `flow.shutter_state`
  • Правила:
  • * Правило 1: `==` (строка) `stopped` -> Выход 1

    * Правило 'otherwise' (иначе): -> Выход 2

    Логика работы потока:

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

    Практический пример

    Допустим, на вход потока приходит команда в виде JSON:

    {
    

    "topic": "commands/shutter/living_room",

    "payload": {

    "action": "DOWN",

    "source": "wall_switch_3"

    }

    }

    Этот поток разделяется на две ветки: одна для `DOWN` и одна для `UP`. Рассмотрим ветку `DOWN`:

    [MQTT In] --(msg)--> [Switch: filter DOWN] --(msg)--> [Switch: Check State] --+-- (to Relay)
    

    |

    +-- (to Log)

    Нода `[Switch: Check State]` настроена, как описано выше:

    Когда штора приводится в движение, другая часть потока (например, нода `Function` после `Switch`) должна немедленно обновить состояние:

    `flow.set("shutter_state", "moving_down");`

    Теперь, если в этот момент придет еще одна команда `DOWN` или команда `UP`, соответствующие ноды `Switch` в их потоках корректно заблокируют эти команды, так как состояние системы больше не `stopped`.

    ---

    Нода Gate: Простое открытие и закрытие потока

    Если нода `Switch` — это интеллектуальный диспетчер с множеством правил, то нода `Gate` (из популярной библиотеки `node-red-contrib-simple-gate`) — это простой, но эффективный шлагбаум. Она имеет всего два состояния: `open` (открыто) и `closed` (закрыто).

    Основное преимущество `Gate` в ее явном управлении. Состояние ноды меняется не косвенно (через проверку контекста), а напрямую, с помощью специальных управляющих сообщений.

    Управление состоянием ноды Gate

    Чтобы изменить состояние `Gate`, необходимо отправить на ее вход сообщение, у которого `msg.topic` имеет одно из следующих строковых значений:

    | `msg.topic` | Действие |

    | :---------- | :-------------------------------------------------------------------- |

    | `open` | Открывает "ворота". Если в очереди было сообщение, оно отправляется. |

    | `close` | Закрывает "ворота". Все последующие сообщения будут заблокированы. |

    | `toggle` | Переключает состояние: если было открыто — закрывает, и наоборот. |

    Это позволяет строить очень понятную логику блокировки: при запуске одного действия мы явно отправляем команду на закрытие «ворот» для конфликтующего действия.

    Пример реализации с Gate

    Вернемся к задаче управления шторой. Построим логику с использованием двух нод `Gate`:

    Поток для команды «Вверх» будет выглядеть так:

               +--(команда "Вверх")------------------> [Gate-UP] ---> [Реле "Вверх"]
    

    |

    [Источник] --+--(команда "Стоп")

    |

    +--(команда "Вниз")-------------------> [Gate-DOWN] --> [Реле "Вниз"]

    Теперь добавим управляющую логику.

  • Когда приходит команда «Вверх»:
  • * Основное сообщение проходит через (изначально открытый) узел `Gate-UP` к реле.

    * Параллельно мы формируем и отправляем управляющее сообщение `{"topic": "close"}` на вход узла `Gate-DOWN`. `Gate-DOWN` немедленно закрывается.

    * Любая последующая команда «Вниз» будет заблокирована узлом `Gate-DOWN`.

  • Когда приходит команда «Вниз»:
  • * Логика зеркальна: основное сообщение идет к реле «Вниз», а управляющее сообщение `{"topic": "close"}` отправляется на `Gate-UP`.

  • Когда приходит команда «Стоп»:
  • * Команда «Стоп» отправляет управляющие сообщения `{"topic": "open"}` на оба узла: и на `Gate-UP`, и на `Gate-DOWN`.

    * Система возвращается в исходное состояние, оба направления снова доступны для команд.

    Пример управляющего сообщения, которое закрывает `Gate-DOWN`:

    {
    

    "topic": "close",

    "payload": "Блокируем направление 'Вниз', так как начато движение 'Вверх'"

    }

    Нода `Gate` проигнорирует `payload`, но отреагирует на `topic`. Использование осмысленного `payload` полезно для отладки.

    ---

    Практический пример: Блокировка двигателя рулонной шторы

    Теперь соберем все воедино и построим полноценный, безопасный поток для управления мотором рулонной шторы на базе контроллера HI. Мы будем использовать два релейных выхода: `RL-01` для движения вверх и `RL-02` для движения вниз.

    > ⚠️ Внимание: Критически важно исключить любую возможность одновременного включения реле «Вверх» и «Вниз». У большинства реверсивных приводов это приводит к короткому замыканию на стороне блока питания или самого двигателя, что может привести к физическому выходу из строя дорогостоящего оборудования. Всегда отлаживайте логику на контроллере без подключения реальной силовой нагрузки, наблюдая за срабатыванием реле по светодиодным индикаторам на контроллере.

    Сборка комплексного потока

    Задача: Обрабатывать MQTT-команды из топика `hi/office/shutter1/set` с `payload` "UP", "DOWN", "STOP". Реализовать взаимную блокировку и обеспечить автоматическую остановку через 15 секунд. ASCII-схема потока:
                                                           +--> [Function: Set state UP] ----+
    

    | |

    [MQTT In] -> [Switch: "UP"/"DOWN"/"STOP"] --(UP)------>| +--> [Gate-UP] -> [RL-01 ON] -> [Trigger 15s] -> [RL-01 OFF] -> [Func: Set stopped]

    | |

    | | (DOWN)--> [Function: Set state DOWN] --+

    | | | |

    | | +--> [Gate-DOWN] -> [RL-02 ON] -> [Trigger 15s] -> [RL-02 OFF] -> [Func: Set stopped]

    | |

    +-----(STOP)-+-------------------------------> [Function: STOP Logic] -----> [RL-01/02 OFF] & [Func: Set stopped]

    Шаг 1: Инициализация состояния

    Используя шаблон 'Inject-Once', создаем начальное состояние при старте Node-RED.

    `[Inject: once] -> [Function: Init State]`

    Код в `[Function: Init State]`:

    // Устанавливаем начальное состояние - штора остановлена
    

    flow.set("shutter_state", "stopped");

    // Убеждаемся, что оба "гейта" открыты

    node.send([

    { topic: "open", payload: "init" }, // Сообщение для Gate-UP

    { topic: "open", payload: "init" } // Сообщение для Gate-DOWN

    ]);

    return null; // Больше ничего не отправляем

    Эта нода имеет два выхода, подключенных к `Gate-UP` и `Gate-DOWN`.

    Шаг 2: Маршрутизация команд

    Нода `[Switch: "UP"/"DOWN"/"STOP"]` проверяет `msg.payload` и направляет сообщение на один из трех выходов.

    Шаг 3: Логика для команд "UP" и "DOWN" (на примере "UP")
    // Получаем текущее состояние
    

    const state = flow.get("shutter_state") || "stopped";

    // ПРОВЕРКА БЛОКИРОВКИ

    if (state !== "stopped") {

    node.warn(`Блокирована команда UP. Текущее состояние: ${state}`);

    return null; // Блокируем сообщение

    }

    // Если блокировки нет, МЕНЯЕМ СОСТОЯНИЕ

    flow.set("shutter_state", "moving_up");

    node.status({fill:"blue", shape:"dot", text:`Движение вверх...`});

    // Формируем команды

    // 1. Управляющая команда для закрытия гейта "Вниз"

    const closeGateCmd = { topic: "close" };

    // 2. Исполнительная команда для реле "Вверх"

    const driveUpCmd = { payload: true }; // Команда на включение реле

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

    return [ closeGateCmd, driveUpCmd ];

    Эта нода имеет два выхода: первый подключен к `Gate-DOWN`, второй — к `Gate-UP`.

    flow.set("shutter_state", "stopped");
    

    node.status({fill:"green", shape:"dot", text:"Остановлено"});

    // Также открываем оба гейта для следующей команды

    return [ {topic: "open"}, {topic: "open"} ];

    Эта нода также имеет два выхода для отправки команды `open` на оба `Gate`.

    Шаг 4: Логика для команды "STOP"

    `[Function: STOP Logic]` — это команда с наивысшим приоритетом.

    // Мгновенно обновляем состояние
    

    flow.set("shutter_state", "stopped");

    node.status({fill:"green", shape:"dot", text:"Остановлено (команда СТОП)"});

    // Формируем массив сообщений для выполнения всех действий

    const messages = [

    { payload: false }, // Команда на выключение RL-01

    { payload: false }, // Команда на выключение RL-02

    { topic: "open" }, // Открыть Gate-UP

    { topic: "open" } // Открыть Gate-DOWN

    ];

    return [ messages ]; // Отправляем массив сообщений

    Эта нода имеет один выход, который подключен ко всем четырем целям: `RL-01`, `RL-02`, `Gate-UP`, `Gate-DOWN`.

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

    ---

    Заключение: Выбор инструмента и лучшие практики

    Мы рассмотрели два мощных инструмента для создания программных взаимных блокировок в Node-RED: `Switch` и `Gate`. Хотя обе ноды могут решать схожие задачи, у них есть свои сильные стороны.

    | Критерий | Нода `Switch` | Нода `Gate` |

    | :---------------- | :------------------------------------------------------- | :------------------------------------------------------------- |

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

    | Основной принцип | Маршрутизация на основе проверки состояния (косвенное управление). | Прямое управление потоком через команды "open/close" (явное управление). |

    | Гибкость | Очень высокая. Может проверять любые свойства `msg` или контекста. | Ограничена двумя состояниями. |

    | Визуальная ясность | Логика может быть скрыта внутри настроек. Требует комментариев. | Состояние "шлагбаума" наглядно, управляющие потоки видны. |

    Ключевые выводы и лучшие практики:
  • Надежное управление состоянием — основа всего. Без точного и своевременного обновления переменной состояния (например, `flow.shutter_state`) любая логика блокировки становится бессмысленной и даже опасной.
  • `Switch` для сложной маршрутизации. Используйте `Switch`, когда вам нужно не просто заблокировать команду, а, например, перенаправить ее на другой обработчик, выдать специфическую ошибку в зависимости от состояния или реализовать конечный автомат с более чем двумя состояниями.
  • `Gate` для простого и явного контроля. Используйте `Gate`, когда логика сводится к простому «если X работает, то Y запрещен». Это делает поток визуально более чистым, так как управляющие связи явно видны.
  • Команда «Стоп» — высший приоритет. Всегда проектируйте логику так, чтобы команда «Стоп» имела наивысший приоритет. Она должна обходить все проверки, принудительно останавливать все движения и сбрасывать систему в безопасное, известное состояние (`stopped`).
  • Комбинируйте подходы. Как показано в практическом примере, наиболее надежные системы часто используют комбинацию этих инструментов: `Switch` для первоначальной маршрутизации команд, `Function` для проверки и изменения состояния, и `Gate` для надежной блокировки параллельных потоков.
  • Освоив эти два узла, вы сможете реализовать подавляющее большинство сценариев взаимных блокировок, необходимых для создания безопасных и профессиональных систем автоматизации на платформе HI.

    Что дальше?

    В следующем уроке мы углубимся в реализацию более сложных конечных автоматов (Finite State Machines) для управления системами с множеством состояний, таких как климат-контроль, где взаимные блокировки между режимами «Нагрев», «Охлаждение» и «Вентиляция» являются критически важными.