Add notification system for relay channel state changes
- Introduced a new notification module to handle state change listeners. - Added functions to register and unregister listeners for relay channel state changes. - Implemented a queue-based system to manage notifications and listener callbacks. - Updated core relay channel logic to utilize the new notification system. - Removed old listener management code from relay channel core. - Refactored the former listener tests to notify tests and added tests for the notification system, including handling of multiple listeners and queue overflow scenarios. - Updated CMakeLists.txt to include new source files and headers for the notification module. - Revised README.md to include warnings about callback execution context and performance considerations. Refs #1096, #1085 and closes #1097
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include "relay_chn_output.h"
|
||||
#include "relay_chn_run_info.h"
|
||||
#include "relay_chn_ctl.h"
|
||||
#include "relay_chn_notify.h"
|
||||
|
||||
#if CONFIG_RELAY_CHN_ENABLE_TILTING
|
||||
#include "relay_chn_tilt.h"
|
||||
@@ -26,15 +27,6 @@
|
||||
static const char *TAG = "RELAY_CHN_CORE";
|
||||
|
||||
|
||||
// Structure to hold a listener entry in the linked list.
|
||||
typedef struct relay_chn_listener_entry_type {
|
||||
relay_chn_state_listener_t listener; /*!< The listener function pointer */
|
||||
ListItem_t list_item; /*!< FreeRTOS list item */
|
||||
} relay_chn_listener_entry_t;
|
||||
|
||||
// The list that holds references to the registered listeners.
|
||||
static List_t relay_chn_listener_list;
|
||||
|
||||
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
|
||||
/*
|
||||
* Run limit timer callback immediately dispatches a STOP command for the
|
||||
@@ -125,9 +117,9 @@ esp_err_t relay_chn_create(const uint8_t* gpio_map, uint8_t gpio_count)
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize tilt feature");
|
||||
#endif
|
||||
|
||||
// Init the state listener list
|
||||
vListInitialise(&relay_chn_listener_list);
|
||||
|
||||
// Initialize the notify feature
|
||||
ret = relay_chn_notify_init();
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize notify feature");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -136,96 +128,15 @@ void relay_chn_destroy(void)
|
||||
#if CONFIG_RELAY_CHN_ENABLE_TILTING
|
||||
relay_chn_tilt_deinit();
|
||||
#endif
|
||||
relay_chn_notify_deinit();
|
||||
relay_chn_ctl_deinit();
|
||||
relay_chn_output_deinit();
|
||||
|
||||
#if CONFIG_RELAY_CHN_ENABLE_NVS
|
||||
relay_chn_nvs_deinit();
|
||||
#endif
|
||||
|
||||
// Free the listeners
|
||||
while (listCURRENT_LIST_LENGTH(&relay_chn_listener_list) > 0) {
|
||||
ListItem_t *pxItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
|
||||
relay_chn_listener_entry_t *entry = listGET_LIST_ITEM_OWNER(pxItem);
|
||||
uxListRemove(pxItem);
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find a listener entry in the list by its function pointer.
|
||||
*
|
||||
* This function replaces the old index-based search and is used to check
|
||||
* for the existence of a listener before registration or for finding it
|
||||
* during unregistration.
|
||||
*
|
||||
* @param listener The listener function pointer to find.
|
||||
* @return Pointer to the listener entry if found, otherwise NULL.
|
||||
*/
|
||||
static relay_chn_listener_entry_t* find_listener_entry(relay_chn_state_listener_t listener)
|
||||
{
|
||||
// Iterate through the linked list of listeners
|
||||
for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
|
||||
pxListItem != listGET_END_MARKER(&relay_chn_listener_list);
|
||||
pxListItem = listGET_NEXT(pxListItem)) {
|
||||
|
||||
relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem);
|
||||
if (entry->listener == listener) {
|
||||
// Found the listener, return the entry
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Listener was not found in the list
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(listener, ESP_ERR_INVALID_ARG, TAG, "Listener cannot be NULL");
|
||||
|
||||
// Check for duplicates
|
||||
if (find_listener_entry(listener) != NULL) {
|
||||
ESP_LOGD(TAG, "Listener %p already registered", listener);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Allocate memory for the new listener entry
|
||||
relay_chn_listener_entry_t *entry = malloc(sizeof(relay_chn_listener_entry_t));
|
||||
ESP_RETURN_ON_FALSE(entry, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for listener");
|
||||
|
||||
// Initialize and insert the new listener
|
||||
entry->listener = listener;
|
||||
vListInitialiseItem(&(entry->list_item));
|
||||
listSET_LIST_ITEM_OWNER(&(entry->list_item), (void *)entry);
|
||||
vListInsertEnd(&relay_chn_listener_list, &(entry->list_item));
|
||||
|
||||
ESP_LOGD(TAG, "Registered listener %p", listener);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void relay_chn_unregister_listener(relay_chn_state_listener_t listener)
|
||||
{
|
||||
if (listener == NULL)
|
||||
{
|
||||
ESP_LOGD(TAG, "Cannot unregister a NULL listener.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the listener entry in the list
|
||||
relay_chn_listener_entry_t *entry = find_listener_entry(listener);
|
||||
|
||||
if (entry != NULL) {
|
||||
// Remove the item from the list and free the allocated memory
|
||||
uxListRemove(&(entry->list_item));
|
||||
free(entry);
|
||||
ESP_LOGD(TAG, "Unregistered listener %p", listener);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Listener %p not found for unregistration.", listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t relay_chn_start_esp_timer_once(esp_timer_handle_t esp_timer, uint32_t time_ms)
|
||||
{
|
||||
esp_err_t ret = esp_timer_start_once(esp_timer, time_ms * 1000);
|
||||
@@ -251,16 +162,7 @@ void relay_chn_update_state(relay_chn_ctl_t *chn_ctl, relay_chn_state_t new_stat
|
||||
|
||||
chn_ctl->state = new_state;
|
||||
|
||||
// Iterate through the linked list of listeners and notify them.
|
||||
for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
|
||||
pxListItem != listGET_END_MARKER(&relay_chn_listener_list);
|
||||
pxListItem = listGET_NEXT(pxListItem)) {
|
||||
relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem);
|
||||
if (entry && entry->listener) {
|
||||
// Emit the state change to the listeners
|
||||
entry->listener(chn_ctl->id, old_state, new_state);
|
||||
}
|
||||
}
|
||||
relay_chn_notify_state_change(chn_ctl->id, old_state, new_state);
|
||||
}
|
||||
|
||||
static void relay_chn_execute_idle(relay_chn_ctl_t *chn_ctl);
|
||||
|
||||
Reference in New Issue
Block a user