| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "FreeRtosEventBus.h" | ||
| 2 | #include "esp_log.h" | ||
| 3 | #include "esp_timer.h" | ||
| 4 | |||
| 5 | static const char* TAG = "FreeRtosEventBus"; | ||
| 6 | |||
| 7 | ✗ | FreeRtosEventBus::FreeRtosEventBus(size_t queueSize) { | |
| 8 | ✗ | m_queue = xQueueCreate(queueSize, sizeof(Event)); | |
| 9 | ✗ | if (!m_queue) { | |
| 10 | ✗ | ESP_LOGE(TAG, "Failed to create event queue"); | |
| 11 | } | ||
| 12 | |||
| 13 | ✗ | m_mutex = xSemaphoreCreateMutex(); | |
| 14 | ✗ | if (!m_mutex) { | |
| 15 | ✗ | ESP_LOGE(TAG, "Failed to create mutex"); | |
| 16 | } | ||
| 17 | |||
| 18 | ✗ | ESP_LOGI(TAG, "EventBus created (queue size: %d)", queueSize); | |
| 19 | ✗ | } | |
| 20 | |||
| 21 | ✗ | FreeRtosEventBus::~FreeRtosEventBus() { | |
| 22 | // Stop event loop first to prevent access to deleted resources | ||
| 23 | ✗ | stopEventLoop(); | |
| 24 | |||
| 25 | ✗ | if (m_queue) { | |
| 26 | ✗ | vQueueDelete(m_queue); | |
| 27 | } | ||
| 28 | ✗ | if (m_mutex) { | |
| 29 | ✗ | vSemaphoreDelete(m_mutex); | |
| 30 | } | ||
| 31 | ✗ | } | |
| 32 | |||
| 33 | ✗ | void FreeRtosEventBus::subscribe(EventType type, std::function<void(const Event&)> handler) { | |
| 34 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 35 | ✗ | m_subscribers[type].push_back(std::move(handler)); | |
| 36 | ✗ | ESP_LOGI(TAG, "Subscriber added for event: %s", eventTypeToString(type)); | |
| 37 | ✗ | xSemaphoreGive(m_mutex); | |
| 38 | } | ||
| 39 | ✗ | } | |
| 40 | |||
| 41 | ✗ | void FreeRtosEventBus::publish(const Event& event) { | |
| 42 | ✗ | if (!m_queue) { | |
| 43 | ✗ | ESP_LOGE(TAG, "Cannot publish: queue not initialized"); | |
| 44 | ✗ | return; | |
| 45 | } | ||
| 46 | |||
| 47 | // Add timestamp if not set | ||
| 48 | ✗ | Event timestampedEvent = event; | |
| 49 | ✗ | if (timestampedEvent.timestamp == 0) { | |
| 50 | ✗ | timestampedEvent.timestamp = esp_timer_get_time(); | |
| 51 | } | ||
| 52 | |||
| 53 | ✗ | if (xQueueSend(m_queue, ×tampedEvent, 0) != pdTRUE) { | |
| 54 | ✗ | ESP_LOGW(TAG, "Event queue full, dropping event: %s", eventTypeToString(event.type)); | |
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | ✗ | bool FreeRtosEventBus::publishFromISR(const Event& event) { | |
| 59 | ✗ | if (!m_queue) { | |
| 60 | ✗ | return false; | |
| 61 | } | ||
| 62 | |||
| 63 | ✗ | Event timestampedEvent = event; | |
| 64 | ✗ | if (timestampedEvent.timestamp == 0) { | |
| 65 | ✗ | timestampedEvent.timestamp = esp_timer_get_time(); | |
| 66 | } | ||
| 67 | |||
| 68 | ✗ | BaseType_t xHigherPriorityTaskWoken = pdFALSE; | |
| 69 | ✗ | BaseType_t result = xQueueSendFromISR(m_queue, ×tampedEvent, &xHigherPriorityTaskWoken); | |
| 70 | |||
| 71 | ✗ | if (xHigherPriorityTaskWoken == pdTRUE) { | |
| 72 | portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||
| 73 | } | ||
| 74 | |||
| 75 | ✗ | return result == pdTRUE; | |
| 76 | } | ||
| 77 | |||
| 78 | ✗ | void FreeRtosEventBus::processAllPending() { | |
| 79 | ✗ | Event event; | |
| 80 | ✗ | while (xQueueReceive(m_queue, &event, 0) == pdTRUE) { | |
| 81 | ✗ | dispatchEvent(event); | |
| 82 | } | ||
| 83 | ✗ | } | |
| 84 | |||
| 85 | ✗ | bool FreeRtosEventBus::waitForEvent(Event& outEvent, uint32_t timeoutMs) { | |
| 86 | ✗ | if (!m_queue) { | |
| 87 | ✗ | return false; | |
| 88 | } | ||
| 89 | |||
| 90 | ✗ | TickType_t ticks = (timeoutMs == portMAX_DELAY) ? portMAX_DELAY : pdMS_TO_TICKS(timeoutMs); | |
| 91 | ✗ | if (xQueueReceive(m_queue, &outEvent, ticks) == pdTRUE) { | |
| 92 | ✗ | dispatchEvent(outEvent); | |
| 93 | ✗ | return true; | |
| 94 | } | ||
| 95 | |||
| 96 | ✗ | return false; | |
| 97 | } | ||
| 98 | |||
| 99 | ✗ | void FreeRtosEventBus::dispatchEvent(const Event& event) { | |
| 100 | ✗ | if (xSemaphoreTake(m_mutex, portMAX_DELAY) == pdTRUE) { | |
| 101 | ✗ | auto it = m_subscribers.find(event.type); | |
| 102 | ✗ | if (it != m_subscribers.end()) { | |
| 103 | ✗ | ESP_LOGD(TAG, "Dispatching event: %s to %d subscribers", | |
| 104 | eventTypeToString(event.type), it->second.size()); | ||
| 105 | |||
| 106 | ✗ | for (const auto& handler : it->second) { | |
| 107 | ✗ | if (handler) { | |
| 108 | ✗ | handler(event); | |
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | ✗ | xSemaphoreGive(m_mutex); | |
| 113 | } | ||
| 114 | ✗ | } | |
| 115 | |||
| 116 | ✗ | void FreeRtosEventBus::startEventLoop(uint32_t stackSize, UBaseType_t priority, | |
| 117 | const char* taskName) { | ||
| 118 | ✗ | if (m_eventLoopTask != nullptr) { | |
| 119 | ✗ | ESP_LOGW(TAG, "Event loop already running"); | |
| 120 | ✗ | return; | |
| 121 | } | ||
| 122 | |||
| 123 | ✗ | m_stopRequested = false; | |
| 124 | |||
| 125 | ✗ | BaseType_t result = xTaskCreate( | |
| 126 | eventLoopTask, | ||
| 127 | taskName, | ||
| 128 | stackSize, | ||
| 129 | this, | ||
| 130 | priority, | ||
| 131 | &m_eventLoopTask); | ||
| 132 | |||
| 133 | ✗ | if (result == pdPASS) { | |
| 134 | ✗ | ESP_LOGI(TAG, "Event loop task started (stack: %lu, priority: %u)", | |
| 135 | stackSize, priority); | ||
| 136 | } else { | ||
| 137 | ✗ | ESP_LOGE(TAG, "Failed to create event loop task"); | |
| 138 | ✗ | m_eventLoopTask = nullptr; | |
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | ✗ | void FreeRtosEventBus::stopEventLoop() { | |
| 143 | ✗ | if (m_eventLoopTask == nullptr) { | |
| 144 | ✗ | ESP_LOGW(TAG, "Event loop not running"); | |
| 145 | ✗ | return; | |
| 146 | } | ||
| 147 | |||
| 148 | ✗ | ESP_LOGI(TAG, "Stopping event loop..."); | |
| 149 | ✗ | m_stopRequested = true; | |
| 150 | |||
| 151 | // Give the task time to exit gracefully | ||
| 152 | ✗ | vTaskDelay(pdMS_TO_TICKS(100)); | |
| 153 | |||
| 154 | // If still running, delete it | ||
| 155 | ✗ | if (m_eventLoopTask != nullptr) { | |
| 156 | ✗ | vTaskDelete(m_eventLoopTask); | |
| 157 | ✗ | m_eventLoopTask = nullptr; | |
| 158 | } | ||
| 159 | |||
| 160 | ✗ | ESP_LOGI(TAG, "Event loop stopped"); | |
| 161 | } | ||
| 162 | |||
| 163 | ✗ | bool FreeRtosEventBus::isEventLoopRunning() const { | |
| 164 | ✗ | return m_eventLoopTask != nullptr && !m_stopRequested; | |
| 165 | } | ||
| 166 | |||
| 167 | ✗ | void FreeRtosEventBus::eventLoopTask(void* pvParameters) { | |
| 168 | ✗ | auto* self = static_cast<FreeRtosEventBus*>(pvParameters); | |
| 169 | |||
| 170 | ✗ | ESP_LOGI(TAG, "Event loop task running"); | |
| 171 | |||
| 172 | ✗ | Event event; | |
| 173 | ✗ | while (!self->m_stopRequested) { | |
| 174 | // Wait for event with timeout to check stop flag periodically | ||
| 175 | ✗ | if (self->waitForEvent(event, 100)) { | |
| 176 | ✗ | ESP_LOGD(TAG, "Event processed: %s", eventTypeToString(event.type)); | |
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | ✗ | ESP_LOGI(TAG, "Event loop task exiting"); | |
| 181 | ✗ | self->m_eventLoopTask = nullptr; | |
| 182 | ✗ | vTaskDelete(nullptr); | |
| 183 | ✗ | } | |
| 184 |