ESP-IDF Event Loop Library |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||
Библиотека цикла событий (Event Loop Library) позволяет компонентам декларировать события, чтобы другие компоненты могли регистрировать обработчики - функции, которые выполняются, когда происходят эти события. Это дает возможность слабо связанным функционально компонентам присоединять требуемое поведение к изменениям состояния других компонентов без участия приложения. Это также упрощает обработку событий путем сериализации и переноса выполнения кода обработки событий в другой контекст. Один из общих случаев использования, это когда библиотека высокого уровня использует библиотеку Wi-Fi: она может напрямую подписаться на модель программирования библиотеки Wi-Fi ESP32 (ESP32 Wi-Fi Programming Model) и действовать в соответствии с её событиями. Замечание: различные модули стека Bluetooth поставляют события приложениям через выделенные функции обратного вызова (callback) вместо использования Event Loop Library. [Использование esp_event API] Для пользователей этой библиотеки есть два объекта, с которыми нужно будет иметь дело: events (события) и event loops (циклы событий). Event показывает важное событие, такое как успешное подключение Wi-Fi к точке доступа. При ссылке на события следует использовать двухкомпонентный идентификатор, подробнее см. далее секцию "Декларация и определение событий". Цикл событий (event loop) является мостом между событиями и обработчиками событий (event handlers). Источник событий публикует события в event loop, используя для этого специальные вызовы API библиотеки Event Loop (esp_event), и обработчики событий, зарегистрированные для event loop, отвечают за действия по определенным типам событий. Использование этой библиотеки примерно представляет собой следующую последовательность действий: 1. Пользователь определяет функцию, которая должна запуститься, когда событие публикуется в цикл событий. Эта функция называется обработчиком события (event handler), у неё должна быть такая же сигнатура, как у типа esp_event_handler_t. 2. Цикл событий (event loop) создается с использованием функции esp_event_loop_create(), которая возвратит дескриптор цикла событий типа esp_event_loop_handle_t. Циклы событий, созданные с помощью этой API-функции, называются пользовательскими циклами событий (user event loops). Однако существует специальный тип цикла событий, называемый циклом событий по умолчанию (default event loop), который обсуждается далее в секции "Default Event Loop". 3. Компоненты регистрируют обработчики событий (event handlers) для цикла событий, используя функцию esp_event_handler_register_with(). Обработчики могут быть зарегистрированы в нескольких циклах событий, см. далее секцию "Замечания по регистрации обработчика события". 4. Источники событий помещают событие в цикл, используя функцию esp_event_post_to(). 5. Компоненты, которые хотят удалить свои обработчики, чтобы они больше не вызывались, могут отменить регистрацию обработчиков в цикле событий путем вызова функции esp_event_handler_unregister_with(). 6. Циклы событий, которые больше не нужны, могут быть удалены вызовом esp_event_loop_delete(). В коде это может выглядеть следующим образом: // (1) Определение обработчика события [Декларация и определение событий] Как упоминалось ранее, события состоят из двухкомпонентных идентификаторов: event base и event ID. Здесь event base идентифицирует независимую группу событий, а event ID идентифицирует событие в этой группе. Об event base и event ID можно думать как об фамилии и имени соответственно. Фамилия идентифицирует семью, а имя идентифицирует персону в пределах этого семейства. Event Loop Library предоставляет макросы для упрощения декларации и определения event base. Декларация event base: ESP_EVENT_DECLARE_BASE(EVENT_BASE); Определение event base: ESP_EVENT_DEFINE_BASE(EVENT_BASE); Замечание: в ESP-IDF идентификаторы base для системных событий записываются в верхнем регистре, и снабжаются пост-суффиксом _EVENT. Например, base для событий Wi-Fi декларируется и определяется как WIFI_EVENT, base события Ethernet как ETHERNET_EVENT, и так далее. Цель состоит в том, чтобы базы событий выглядели как константы (хотя они являются глобальными переменными с учетом определений макросов ESP_EVENT_DECLARE_BASE и ESP_EVENT_DEFINE_BASE). Для идентификаторов событий (event ID) рекомендуется использовать декларацию в виде перечисления. И опять-таки, для видимости они обычно помещаются в публичных заголовочных файлах. Пример определения Event ID: enum {
EVENT_ID_1,
EVENT_ID_2,
EVENT_ID_3,
...
}
[Default Event Loop] Цикл событий по умолчанию (Default Event Loop) это специальный тип цикла, используемый для системных событий (например событий Wi-Fi). Дескриптор для этого цикла скрыт от пользователя, и создание, удаление цикла событий, регистрация обработчика, отмена регистрации обработчика и публикация событий осуществляется через отдельный набор API-функций, очень похожих на аналогичные функции для пользовательских циклов событий. В следующей таблице перечислены эти функции, и их эквивалент для пользовательских циклов событий.
Если вы сравните их сигнатуры, то увидите, что они почти одинаковые, за исключением того, что отсутствует спецификация для переменной дескриптора цикла у API-функций Default Event Loop. За исключением отличий в API и специального назначения, куда публикуются системные события, не существует отличий от циклов событий по умолчанию и циклов событий пользователя. Пользователь даже может публиковать свои собственные события в цикл событий по умолчанию, если пользователь примет решение не создавать свои собственные циклы событий (например из соображений экономии памяти). [Замечания по регистрации обработчика события] Можно индивидуально зарегистрировать один обработчик для нескольких событий, путем нескольких вызовов esp_event_handler_register_with(). Для этих нескольких вызовов должны быть указаны отдельные варианты комбинации event base и event ID, и для этих вызовов может быть указан один и тот же обработчик. Однако в некоторых случаях желательно, чтобы обработчик вызывался в следующих ситуациях: 1. На всех событиях, которые были опубликованы в цикл событий. 2. На всех событиях с определенным base-идентификатором. Это возможно, если указать специальный base-идентификатор ESP_EVENT_ANY_BASE и специальный event ID идентификатор ESP_EVENT_ANY_ID. Эти специальные идентификаторы могут быть переданы как аргументы для event base и event ID arguments функции esp_event_handler_register_with(). Таким образом, для функции esp_event_handler_register_with() допустимы следующие варианты аргументов идентификаторов: 1. < event base>, 2. < event base>, ESP_EVENT_ANY_ID - обработчик выполнится, когда в цикл событий опубликовано любое событие, когда base-идентификатор совпадает с < event base>. 3. ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID - обработчик выполнится, когда в цикл событий опубликовано любое событие (с любым базовым идентификатором и любым идентификатором события). Для примера предположим, что были выполнены следующие регистрации обработчиков: esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event_1, ...); esp_event_handler_register_with(loop_handle, MY_EVENT_BASE, ESP_EVENT_ANY_ID, run_on_event_2, ...); esp_event_handler_register_with(loop_handle, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, run_on_event_3, ...); Если было опубликовано гипотетическое событие MY_EVENT_BASE, MY_EVENT_ID, то выполнятся все три обработчика: run_on_event_1, run_on_event_2 и run_on_event_3. Если было опубликовано гипотетическое событие MY_EVENT_BASE, MY_OTHER_EVENT_ID, то запустятся только обработчики run_on_event_2 и run_on_event_3. Если было опубликовано гипотетическое событие MY_OTHER_EVENT_BASE, MY_OTHER_EVENT_ID, то запустится только run_on_event_3. Отмена регистрации обработчика самим собой. Обычно обработчику, выполняемому циклом событий, не разрешено выполнять какую-либо активность по регистрации или отмене регистрации в этом цикле событий. Однако есть некоторое исключение: обработчик может отменить регистрацию самого себя. Например, можно сделать следующее: void run_on_event(void* handler_arg, esp_event_base_t base, int32_t id, void* event_data) { esp_event_loop_handle_t *loop_handle = (esp_event_loop_handle_t*) handler_arg; esp_event_handler_unregister_with(*loop_handle, MY_EVENT_BASE, MY_EVENT_ID, run_on_event); } Регистрация обработчика и порядок вызова обработчиков. Общее правило заключается в том, что из обработчиков, которые соответствуют определенному объявленному событию, первыми запустятся те, которые были зарегистрированы первыми. Таким образом, пользователь может управлять порядком, в каком запускаются обработчики, если он выполнит регистрацию в указанном порядке при условии, что все регистрации будут выполнены в одной задаче. Если пользователь планирует воспользоваться преимуществами такого поведения, следует быть внимательным к ситуациям, когда несколько задач регистрируют обработчики. Хотя поведение по правилу 'первым зарегистрировался, первым выполнился' все еще сохраняется, задача, которая выполнилась первой, также и первой зарегистрирует свои обработчики. Обработчики, которые регистрируются в другой задаче, все еще сохранят свой порядок запуска относительно друг друга, но если более приоритетная задача выполнила переключение контекста на себя и выполнила свою регистрацию, то она может вставить свои зарегистрированные обработчики в список запуска; таким образом, общий порядок запуска может поменяться. [Профайлинг цикла событий] Можно разрешить опцию конфигурации CONFIG_ESP_EVENT_LOOP_PROFILING, чтобы активировать накопление статистики для всех созданных циклов событий. Для вывода накопленной статистики в файловый поток может использоваться функция esp_event_dump(). Описание информации, которая включается в дамп, можно найти в описании esp_event_dump(). [Примеры приложений] Проект system/esp_event/default_event_loop демонстрирует, как использовать системный цикл по умолчанию ESP32-C3 для публикации и обработки событий, включая декларацию и определение событий, создание default event loop, публикацию событий в цикл, регистрацию обработчиков событий и отмену их регистрации. Проект system/esp_event/user_event_loops демонстрирует, как создавать и использовать пользовательские циклы событий на ESP32-C3, включая создание и запуск циклов событий, регистрацию обработчиков и отмену их регистрации, публикацию событий, с возможностью обработать различные случаи использования за пределами цикла событий по умолчанию. Примечание: вышеупомянутые примеры можно найти в папке examples каталога установки ESP-IDF (например ~/esp/v5.4.1/esp-idf/examples). [Справочник по API библиотеки цикла событий] Заголовочные файлы библиотеки: components/esp_event/include/esp_event.h, и components/esp_event/include/esp_event_base.h, они подключается директивой #include: #include "esp_event.h" Этот заголовочный файл представляет декларацию публичного API, предоставляемого компонентом esp_event. Чтобы декларировать, что ваш компонент (например приложение) зависит от компоненты esp_event, добавьте следующее в свой CMakeLists.txt: REQUIRES esp_event .. или: PRIV_REQUIRES esp_event В следующей таблице приведено общее описание API-функций библиотеки цикла событий esp_event. Полное описание функций, их сигнатур, используемых структур, макросов и типов см. в документации [1].
Примечания: (1) Библиотека цикла событий не хранит копию event_handler_arg, поэтому пользователь должен гарантировать, что event_handler_arg все еще указывает на допустимое местоположение в момент вызова обработчика. [Ссылки] 1. ESP32-C3 Event Loop Library site:espressif.com. |