Application Event Manager
The Application Event Manager is a piece of software that supports development of consistent, modular, event-based applications. In an event-based application, parts of the application functionality are separated into isolated modules that communicate with each other using events. Events are submitted by modules and other modules can subscribe and react to them. The Application Event Manager acts as coordinator of the event-based communication.
For a usage example of the Event Manger module and event structure in the nRF Connect SDK, see the Common Application Framework (CAF).
See the Application Event Manager sample for a simple example of how to use the Application Event Manager.
Events
Events are structured data types that are defined by the application and can contain additional data.
The Application Event Manager handles the events by processing and propagating all of them to the modules (listeners) that subscribe to a specific event. Multiple modules can subscribe to the same event. As part of this communication, listeners can process events differently based on their type.
The Application Event Manager provides API for defining, creating, and subscribing events. See Implementing an event type for details about how to create custom event types.
Modules
Modules are separate source files that can subscribe to every defined event. You can use events for communication between modules.
There is no limitation as to how many events each module can subscribe to. An application can have as many modules as required.
The Application Event Manager provides an API for subscribing modules to specific events defined in the application. When a module subscribes to a specific event, it is called a listener module. Every listener is identified by a unique name.
Configuration
To use the Application Event Manager, enable it using the CONFIG_APP_EVENT_MANAGER
Kconfig option and initialize it in your main.c
file.
Initializing the Application Event Manager allows it to handle submitted events and deliver them to modules that subscribe to the specified event type.
Complete the following steps:
Enable the
CONFIG_APP_EVENT_MANAGER
Kconfig option.Include
app_event_manager.h
in yourmain.c
file.Call
app_event_manager_init()
.
Implementing events and modules
If an application module is supposed to react to an event, your application must implement an event type, submit the event, and register the module as listener. Read the following sections for details.
Implementing an event type
If you want to easily create and implement custom event types, the Application Event Manager provides macros to add a new event type in your application. Complete the following steps:
Create a header file for the event type you want to define
Create a source file for the event type
Create a header file
To create a header file for the event type you want to define:
Make sure the header file includes the Application Event Manager header file:
#include <app_event_manager.h>
Define the new event type by creating a structure that contains an
app_event_header
namedheader
as the first field.Optionally, add additional custom data fields to the structure.
Declare the event type with the
APP_EVENT_TYPE_DECLARE
macro, passing the name of the created structure as an argument.
The following code example shows a header file for the event type sample_event
:
#include <app_event_manager.h>
struct sample_event {
struct app_event_header header;
/* Custom data fields. */
int8_t value1;
int16_t value2;
int32_t value3;
};
APP_EVENT_TYPE_DECLARE(sample_event);
In some use cases, the length of the data associated with an event may vary.
You can use the APP_EVENT_TYPE_DYNDATA_DECLARE
macro instead of APP_EVENT_TYPE_DECLARE
to declare an event type with variable data size.
In such case, add the data with the variable size as the last member of the event structure.
For example, you can add variable sized data to the previously defined event by applying the following change to the code:
#include <app_event_manager.h>
struct sample_event {
struct app_event_header header;
/* Custom data fields. */
int8_t value1;
int16_t value2;
int32_t value3;
struct event_dyndata dyndata;
};
APP_EVENT_TYPE_DYNDATA_DECLARE(sample_event);
In this example, the event_dyndata
structure contains the following information:
A zero-length array that is used as a buffer with variable size (
event_dyndata.data
).A number representing the size of the buffer (
event_dyndata.size
).
Create a source file
To create a source file for the event type you defined in the header file:
Include the header file for the new event type in your source file.
Define the event type with the
APP_EVENT_TYPE_DEFINE
macro. Pass the name of the event type as declared in the header along with additional parameters. For example, you can provide a function that fills a buffer with a string version of the event data (used for logging). TheAPP_EVENT_TYPE_DEFINE
macro adds flags as a last parameter. These flags are constant and can only be set usingAPP_EVENT_FLAGS_CREATE
onAPP_EVENT_TYPE_DEFINE
macro. To not set any flag, useAPP_EVENT_FLAGS_CREATE
without any argument as shown in the below example. To get value of specific flag, useapp_event_get_type_flag()
function.
The following code example shows a source file for the event type sample_event
:
#include "sample_event.h"
static void log_sample_event(const struct app_event_header *aeh)
{
struct sample_event *event = cast_sample_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "val1=%d val2=%d val3=%d", event->value1,
event->value2, event->value3);
}
APP_EVENT_TYPE_DEFINE(sample_event, /* Unique event name. */
log_sample_event, /* Function logging event data. */
NULL, /* No event info provided. */
APP_EVENT_FLAGS_CREATE()); /* Flags managing event type. */
Note
There is a deprecated way of logging Application Event Manager events by writing a string to the provided buffer that will be supported until a future release of nRF Connect SDK.
To use the deprecated way, you need to set the CONFIG_APP_EVENT_MANAGER_USE_DEPRECATED_LOG_FUN
option.
You can then use both ways of logging events.
Application Event Manager figures out which way to be used based on the type of the logging function passed.
Submitting an event
To submit an event of a given type, for example sample_event
:
Allocate the event by calling the function with the name new_event_type_name. For example,
new_sample_event()
.Write values to the data fields.
Use
APP_EVENT_SUBMIT
to submit the event.
The following code example shows how to create and submit an event of type sample_event
that has three data fields:
/* Allocate event. */
struct sample_event *event = new_sample_event();
/* Write data to datafields. */
event->value1 = value1;
event->value2 = value2;
event->value3 = value3;
/* Submit event. */
APP_EVENT_SUBMIT(event);
If an event type also defines data with variable size, you must pass also the size of the data as an argument to the function that allocates the event.
For example, if the sample_event
also contains data with variable size, you must apply the following changes to the code:
/* Allocate event. */
struct sample_event *event = new_sample_event(my_data_size);
/* Write data to datafields. */
event->value1 = value1;
event->value2 = value2;
event->value3 = value3;
/* Write data with variable size. */
memcpy(event->dyndata.data, my_buf, my_data_size);
/* Submit event. */
APP_EVENT_SUBMIT(event);
After the event is submitted, the Application Event Manager adds it to the processing queue. When the event is processed, the Application Event Manager notifies all modules that subscribe to this event type.
Note
Events are dynamically allocated and must be submitted. If an event is not submitted, it will not be handled and the memory will not be freed.
Registering a module as listener
If you want a module to receive events managed by the Application Event Manager, you must register it as a listener and you must subscribe it to a given event type.
To turn a module into a listener for specific event types, complete the following steps:
Include the header files for the respective event types, for example,
#include "sample_event.h"
.Implement an Event handler function and define the module as a listener with the
APP_EVENT_LISTENER
macro, passing both the name of the module and the event handler function as arguments.Subscribe the listener to specific event types.
For subscribing to an event type, the Application Event Manager provides three types of subscriptions, differing in priority. They can be registered with the following macros:
APP_EVENT_SUBSCRIBE_FIRST
- notification as the first subscriberAPP_EVENT_SUBSCRIBE_EARLY
- notification before other listenersAPP_EVENT_SUBSCRIBE
- standard notificationAPP_EVENT_SUBSCRIBE_FINAL
- notification as the last, final subscriber
There is no defined order in which subscribers of the same priority are notified.
The module will receive events for the subscribed event types only.
The listener name passed to the subscribe macro must be the same one used in the macro APP_EVENT_LISTENER
.
Implementing an event handler function
The event handler function is called when any of the subscribed event types are being processed. Only one event handler function can be registered per listener. Therefore, if a listener subscribes to multiple event types, the function must handle all of them.
The event handler gets a pointer to the app_event_header
structure as the function argument.
The function should return true
to consume the event, which means that the event is not propagated to further listeners, or false
, otherwise.
To check if an event has a given type, call the function with the name is_event_type_name (for example, is_sample_event()
), passing the pointer to the application event header as the argument.
This function returns true
if the event matches the given type, or false
otherwise.
To access the event data, cast the app_event_header
structure to a proper event type, using the function with the name cast_event_type_name (for example, cast_sample_event()
), passing the pointer to the application event header as the argument.
Code example
The following code example shows how to register an event listener with an event handler function and subscribe to the event type sample_event
:
#include "sample_event.h"
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_sample_event(aeh)) {
/* Accessing event data. */
struct sample_event *event = cast_sample_event(aeh);
int8_t v1 = event->value1;
int16_t v2 = event->value2;
int32_t v3 = event->value3;
/* Actions when received given event type. */
foo(v1, v2, v3);
return false;
}
return false;
}
APP_EVENT_LISTENER(sample_module, app_event_handler);
APP_EVENT_SUBSCRIBE(sample_module, sample_event);
The variable size data is accessed in the same way as the other members of the structure defining an event.
Application Event Manager extensions
The Application Event Manager provides additional features that could be helpful when debugging event-based applications.
Initialization hook
The Application Event Manager provides an initialization hook for any module that relies on the Application Event Manager initialization before the first event is processed.
The hook function should be declared in the int hook(void)
format.
If the hook function returns a non-zero value, the initialization process is interrupted and a related error is returned.
To register the initialization hook, use the macro APP_EVENT_MANAGER_HOOK_POSTINIT_REGISTER
.
For details, refer to API documentation.
Tracing hooks
The Application Event Manager uses flexible mechanism to implement hooks when an event is submitted, before it is processed, and after its processing.
Oryginally designed to implement event tracing, the tracing hooks can be used for other purposes as well.
The registered hook function should be declared in the void hook(const struct app_event_header *aeh)
format.
The following macros are implemented to register event tracing hooks:
APP_EVENT_HOOK_ON_SUBMIT_REGISTER_FIRST
,APP_EVENT_HOOK_ON_SUBMIT_REGISTER
,APP_EVENT_HOOK_ON_SUBMIT_REGISTER_LAST
APP_EVENT_HOOK_PREPROCESS_REGISTER_FIRST
,APP_EVENT_HOOK_PREPROCESS_REGISTER
,APP_EVENT_HOOK_PREPROCESS_REGISTER_LAST
APP_EVENT_HOOK_POSTPROCESS_REGISTER_FIRST
,APP_EVENT_HOOK_POSTPROCESS_REGISTER
,APP_EVENT_HOOK_POSTPROCESS_REGISTER_LAST
For details, refer to API documentation.
Memory management hooks
The Application Event Manager implements default memory management functions using weak implementation. You can override this implementation to implement other types of memory allocation.
The following weak functions are provided by the Application Event Manager as the memory management hooks:
For details, refer to API documentation.
Shell integration
Shell integration is available to display additional information and to dynamically enable or disable logging for given event types.
The Application Event Manager is integrated with Zephyr’s Shell module. When the shell is turned on, an additional subcommand set (app_event_manager) is added.
This subcommand set contains the following commands:
- show_listeners
Show all registered listeners.
- show_subscribers
Show all registered subscribers.
- show_events
Show all registered event types. The letters “E” or “D” indicate if logging is currently enabled or disabled for a given event type.
- enable or disable
Enable or disable logging. If called without additional arguments, the command applies to all event types. To enable or disable logging for specific event types, pass the event type indexes, as displayed by show_events, as arguments.
API documentation
include/app_event_manager.h
subsys/app_event_manager/
- group app_event_manager
Application Event Manager.
Defines
-
APP_EVENT_LISTENER(lname, cb_fn)
Create an event listener object.
- Parameters
lname – Module name.
cb_fn – Pointer to the event handler function.
-
APP_EVENT_SUBSCRIBE_FIRST(lname, ename)
Subscribe a listener to an event type as first module that is being notified.
- Parameters
lname – Name of the listener.
ename – Name of the event.
-
APP_EVENT_SUBSCRIBE_EARLY(lname, ename)
Subscribe a listener to the early notification list for an event type.
- Parameters
lname – Name of the listener.
ename – Name of the event.
-
APP_EVENT_SUBSCRIBE(lname, ename)
Subscribe a listener to the normal notification list for an event type.
- Parameters
lname – Name of the listener.
ename – Name of the event.
-
APP_EVENT_SUBSCRIBE_FINAL(lname, ename)
Subscribe a listener to an event type as final module that is being notified.
- Parameters
lname – Name of the listener.
ename – Name of the event.
-
APP_EVENT_TYPE_DECLARE(ename)
Declare an event type.
This macro provides declarations required for an event to be used by other modules.
- Parameters
ename – Name of the event.
-
APP_EVENT_TYPE_DYNDATA_DECLARE(ename)
Declare an event type with dynamic data size.
This macro provides declarations required for an event to be used by other modules. Declared event will use dynamic data.
- Parameters
ename – Name of the event.
-
APP_EVENT_TYPE_DEFINE(ename, log_fn, ev_info_struct, app_event_type_flags)
Define an event type.
This macro defines an event type. In addition, it defines functions specific to the event type and the event type structure.
For every defined event, the following functions are created, where event_type is replaced with the given event type name
ename
(for example, button_event):new_event_type - Allocates an event of a given type.
is_event_type - Checks if the application event header that is provided as argument represents the given event type.
cast_event_type - Casts the application event header that is provided as argument to an event of the given type.
- Parameters
ename – Name of the event.
log_fn – Function to stringify an event of this type.
ev_info_struct – Data structure describing the event type.
app_event_type_flags – Event type flags. You should use APP_EVENT_FLAGS_CREATE to define them.
-
APP_EVENT_ASSERT_ID(id)
Verify if an event ID is valid.
The pointer to an event type structure is used as its ID. This macro validates that the provided pointer is within the range where event type structures are defined.
- Parameters
id – ID.
-
APP_EVENT_SUBMIT(event)
Submit an event.
This helper macro simplifies the event submission.
- Parameters
event – Pointer to the event object.
-
APP_EVENT_MANAGER_HOOK_POSTINIT_REGISTER(hook_fn)
Register event hook after the Application Event Manager is initialized.
The event hook called after the app_event_manager is initialized to provide some additional initialization of the modules that depends on it. The hook function should have a form
int hook(void)
. If the initialization hook returns non-zero value the initialization process is interrupted.- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_ON_SUBMIT_REGISTER_FIRST(hook_fn)
Register hook called on event submission. The hook would be called first.
The event hook called when the event is submitted. The hook function should have a form
void hook(const struct app_event_header *aeh)
. The macro makes sure that the hook provided here is called first. Only one hook can be registered with this macro.Note
The registered hook may be called from many contexts. To ensure that order of events in the queue matches the order of the registered callbacks calls, the callbacks are called under the same spinlock as adding events to the queue.
- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_ON_SUBMIT_REGISTER(hook_fn)
Register event hook on submission.
The event hook called when the event is submitted. The hook function should have a form
void hook(const struct app_event_header *aeh)
.Note
The registered hook may be called from many contexts. To ensure that order of events in the queue matches the order of the registered callbacks calls, the callbacks are called under the same spinlock as adding events to the queue.
- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_ON_SUBMIT_REGISTER_LAST(hook_fn)
Register event hook on submission. The hook would be called last.
The event hook called when the event is submitted. The hook function should have a form
void hook(const struct app_event_header *aeh)
. The macro makes sure that the hook provided here is called last. Only one hook can be registered with this macro.Note
The registered hook may be called from many contexts. To ensure that order of events in the queue matches the order of the registered callbacks calls, the callbacks are called under the same spinlock as adding events to the queue.
- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_PREPROCESS_REGISTER_FIRST(hook_fn)
Register event hook on the start of event processing. The hook would be called first.
The hook function should have a form
void hook(const struct app_event_header *aeh)
. The macro makes sure that the hook provided here is called first. Only one hook can be registered with this macro.- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_PREPROCESS_REGISTER(hook_fn)
Register event hook on the start of event processing.
The event hook called when the event is being processed. The hook function should have a form
void hook(const struct app_event_header *aeh)
.- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_PREPROCESS_REGISTER_LAST(hook_fn)
Register event hook on the start of event processing. The hook would be called last.
The event hook called when the event is being processed. The hook function should have a form
void hook(const struct app_event_header *aeh)
. The macro makes sure that the hook provided here is called last. Only one hook can be registered with this macro.- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_POSTPROCESS_REGISTER_FIRST(hook_fn)
Register event hook on the end of event processing. The hook would be called first.
The event hook called after the event is processed. The hook function should have a form
void hook(const struct app_event_header *aeh)
. The macro makes sure that the hook provided here is called first. Only one hook can be registered with this macro.- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_POSTPROCESS_REGISTER(hook_fn)
Register event hook on the end of event processing.
The event hook called after the event is processed. The hook function should have a form
void hook(const struct app_event_header *aeh)
.- Parameters
hook_fn – Hook function.
-
APP_EVENT_HOOK_POSTPROCESS_REGISTER_LAST(hook_fn)
Register event hook on the end of event processing. The hook would be called last.
The event hook called after the event is processed. The hook function should have a form
void hook(const struct app_event_header *aeh)
. The macro makes sure that the hook provided here is called last. Only one hook can be registered with this macro.- Parameters
hook_fn – Hook function.
-
APP_EVENT_MANAGER_LOG(aeh, ...)
Log event.
This helper macro simplifies event logging.
- Parameters
aeh – Pointer to the application event header of the event that is processed by app_event_manager.
... –
printf
- like format string and variadic list of arguments corresponding to the format string.
-
APP_EVENT_FLAGS_CREATE(...)
Define flags for event type.
- Parameters
... – Comma-separated list of flags which should be set. In case no flags should be set leave it empty.
Typedefs
-
typedef bool (*cb_fn)(const struct app_event_header *aeh)
Pointer to the event handler function.
- Param aeh
Pointer to the application event header of the event that is processed by app_event_manager.
- Retval True
if event was consumed and should not be propagated to other listeners, false otherwise.
Enums
-
enum app_event_type_flags
List of bits in event type flags.
Values:
-
enumerator APP_EVENT_TYPE_FLAGS_SYSTEM_START
-
enumerator APP_EVENT_TYPE_FLAGS_HAS_DYNDATA
-
enumerator APP_EVENT_TYPE_FLAGS_USER_SETTABLE_START
-
enumerator APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE
-
enumerator APP_EVENT_TYPE_FLAGS_COUNT
-
enumerator APP_EVENT_TYPE_FLAGS_USER_DEFINED_START
-
enumerator APP_EVENT_TYPE_FLAGS_SYSTEM_START
Functions
-
static inline bool app_event_get_type_flag(const struct event_type *et, enum app_event_type_flags flag)
Get event type flag’s value.
- Parameters
flag – Selected event type flag.
et – Pointer to the event type.
- Return values
Boolean – value of requested flag.
-
static inline size_t app_event_manager_event_size(const struct app_event_header *aeh)
Get the event size.
Function that calculates the event size using its header.
Note
For this function to be available the
CONFIG_APP_EVENT_MANAGER_PROVIDE_EVENT_SIZE
option needs to be enabled.- Parameters
aeh – Pointer to the application event header.
- Returns
Event size in bytes.
-
int app_event_manager_init(void)
Initialize the Application Event Manager.
- Return values
0 – If the operation was successful. Error values can be added by the hooks registered by APP_EVENT_MANAGER_HOOK_POSTINIT_REGISTER macro.
-
void *app_event_manager_alloc(size_t size)
Allocate event.
The behavior of this function depends on the actual implementation. The default implementation of this function is same as k_malloc. It is annotated as weak and can be overridden by user.
- Parameters
size – Amount of memory requested (in bytes).
- Return values
Address – of the allocated memory if successful, otherwise NULL.
-
void app_event_manager_free(void *addr)
Free memory occupied by the event.
The behavior of this function depends on the actual implementation. The default implementation of this function is same as k_free. It is annotated as weak and can be overridden by user.
- Parameters
addr – Pointer to previously allocated memory.
-
APP_EVENT_LISTENER(lname, cb_fn)