diff --git a/components/dataLink/DataLinkManager.cpp b/components/dataLink/DataLinkManager.cpp index 4fc17d8..75a8a1b 100644 --- a/components/dataLink/DataLinkManager.cpp +++ b/components/dataLink/DataLinkManager.cpp @@ -49,6 +49,9 @@ DataLinkManager::DataLinkManager(uint8_t board_id, uint8_t num_channels = MAX_CH init_rip(); init_scheduler(); + + // Start the periodic LUT flush task + xTaskCreate(lut_flush_task, "lut_flush", 2048, this, 2, &lut_flush_task_handle); } /** @@ -134,6 +137,10 @@ DataLinkManager::~DataLinkManager(){ vTaskDelete(send_ack_task); send_ack_task = NULL; } + if (lut_flush_task_handle != NULL){ + vTaskDelete(lut_flush_task_handle); + lut_flush_task_handle = NULL; + } } esp_err_t DataLinkManager::set_board_id(uint8_t board_id){ @@ -304,6 +311,49 @@ void DataLinkManager::lut_insert(uint32_t hash, const uint8_t* message, size_t m xSemaphoreGive(control_frame_lut_mutex); } +/** + * @brief Flush (invalidate) all entries in the control-frame LFU LUT. + * + * Acquires the LUT mutex, marks every slot as invalid and resets frequency + * counters, then releases the mutex. Safe to call from any context. + */ +void DataLinkManager::lut_flush(){ + if (xSemaphoreTake(control_frame_lut_mutex, pdMS_TO_TICKS(SEQUENCE_NUM_MAP_MUTEX_MAX_WAIT_MS)) != pdTRUE){ + ESP_LOGW(DEBUG_LINK_TAG, "LUT flush: could not acquire mutex, skipping"); + return; + } + + for (int i = 0; i < CONTROL_FRAME_LUT_SIZE; i++){ + control_frame_lut[i].valid = false; + control_frame_lut[i].frequency = 0; + control_frame_lut[i].message.clear(); + } + + xSemaphoreGive(control_frame_lut_mutex); + ESP_LOGI(DEBUG_LINK_TAG, "Control-frame LUT flushed"); +} + +/** + * @brief Periodic FreeRTOS task that flushes the control-frame LUT every + * LUT_FLUSH_INTERVAL_MS milliseconds. + * + * @param args Pointer to the owning DataLinkManager instance. + */ +[[noreturn]] void DataLinkManager::lut_flush_task(void* args){ + DataLinkManager* self = static_cast(args); + while (true){ + vTaskDelay(pdMS_TO_TICKS(LUT_FLUSH_INTERVAL_MS)); + if (self->stop_tasks){ + break; + } + self->lut_flush(); + } + // Should never reach here during normal operation; loop exits only when + // stop_tasks is set so the destructor can clean up. + vTaskDelete(NULL); + while(true) { vTaskDelay(portMAX_DELAY); } +} + /** * @brief Helper function to create a control frame * diff --git a/components/dataLink/include/DataLinkManager.h b/components/dataLink/include/DataLinkManager.h index 1f52ce8..ac79301 100644 --- a/components/dataLink/include/DataLinkManager.h +++ b/components/dataLink/include/DataLinkManager.h @@ -46,7 +46,8 @@ static const uint16_t crc16_table[256] = { #define ASYNC_QUEUE_WAIT_TICKS 100 #define SEQUENCE_NUM_MAP_MUTEX_MAX_WAIT_MS 50 #define MAX_RX_QUEUE_SIZE 100 -#define CONTROL_FRAME_LUT_SIZE 15 // Maximum number of control frames cached in the receiver-side LFU LUT +#define CONTROL_FRAME_LUT_SIZE 15 // Maximum number of control frames cached in the receiver-side LFU LUT +#define LUT_FLUSH_INTERVAL_MS 30000 // Flush (invalidate) the entire control-frame LUT every 30 seconds /** * @brief Class to represent the Data Link Layer @@ -223,8 +224,19 @@ class DataLinkManager{ */ void lut_insert(uint32_t hash, const uint8_t* message, size_t message_len, const FrameHeader& header); + /** + * @brief Flush (invalidate) all entries in the control-frame LUT. + */ + void lut_flush(); + + /** + * @brief Periodic task that flushes the control-frame LUT every LUT_FLUSH_INTERVAL_MS milliseconds. + */ + [[noreturn]] static void lut_flush_task(void* args); + ControlFrameLutEntry control_frame_lut[CONTROL_FRAME_LUT_SIZE]; SemaphoreHandle_t control_frame_lut_mutex; + TaskHandle_t lut_flush_task_handle = NULL; }; struct frame_scheduler_args {