Flush the LUT every 30 seconds to prevent broken entires from causing incontrollable robot

This commit is contained in:
2026-03-04 04:59:21 -05:00
parent 9cd7cbac3b
commit 5f6b5cc940
2 changed files with 63 additions and 1 deletions

View File

@@ -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<DataLinkManager*>(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
*

View File

@@ -47,6 +47,7 @@ static const uint16_t crc16_table[256] = {
#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 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 {