diff --git a/main/drivers/state_tracking.cpp b/main/drivers/state_tracking.cpp index 272fa7f..50e2228 100644 --- a/main/drivers/state_tracking.cpp +++ b/main/drivers/state_tracking.cpp @@ -8,7 +8,7 @@ enum state_t { STATE_PLAYBACK = 2, }; -static std::vector event_cbs; +static std::vector replay_fns; static bool should_close_recording_stream; static FILE* recording_stream; @@ -17,6 +17,9 @@ TaskHandle_t flush_file_task_handle; static bool should_close_playback_stream; static FILE* playback_stream; +static uint32_t playback_start_time; +TaskHandle_t playback_task_handle; + static volatile state_t state = STATE_IDLE; @@ -37,12 +40,64 @@ static void flush_file_task(void* arg) { vTaskDelete(NULL); } -void register_event_cb(const char* name, void (*replay_callback)(uint32_t, const char*)) { - EventCB event = { - .name = name, - .replay_callback = replay_callback, - }; - event_cbs.push_back(event); +static void playback_task(void* arg) { + const size_t size = 16*1024; + char* buf = (char*) malloc(size*sizeof(char)); + + while (state == STATE_PLAYBACK && playback_stream != nullptr) { + fgets(buf, size, playback_stream); + + // look for a comma, indicating the end of the "ticks" part + size_t comma_pos = 0; + for (int i = 0; i < 11; i++) { + if (buf[i] == ',') { + comma_pos = i; + break; + } + } + if (comma_pos == 0) { + ESP_LOGE("playback", "Failed to find comma in playback line"); + continue; + } + + buf[comma_pos] = '\0'; + uint32_t tick = atoi(buf); + + // now look for the colon to indicate the end of the event name + size_t colon_pos = 0; + int i = comma_pos + 1; // start looking right after the comma + while (i < size) { + if (buf[i] == ':') { + colon_pos = i; + break; + } + } + if (colon_pos == 0) { + ESP_LOGE("playback", "Failed to find colon in playback line"); + continue; + } + + buf[colon_pos] = '\0'; + char* event_name = buf + (comma_pos + 1); + char* arg = buf + (colon_pos + 1); + + int32_t ticks_to_wait = ((int32_t) tick) - (int32_t)(xTaskGetTickCount() - playback_start_time); + + if (ticks_to_wait < 0) { + ESP_LOGW("playback", "Playback is behind by %ld ticks!", ticks_to_wait); + } + if (ticks_to_wait > 0) { + vTaskDelay(ticks_to_wait); + } + + for (const auto& fn : replay_fns) { + (fn)(event_name, arg); + } + } +} + +void register_event_cb(void (*replay_callback)(const char*, const char*)) { + replay_fns.push_back(replay_callback); } void event_occured(const char* name, const char* arg) { @@ -102,8 +157,13 @@ bool stop_recording() { } bool start_playback() { - // TODO: impl - return false; + if (state != STATE_IDLE) return false; + if (playback_stream == nullptr) return false; + + state = STATE_PLAYBACK; + playback_start_time = xTaskGetTickCount(); + xTaskCreate(playback_task, "playback", 2048, NULL, 2, &playback_task_handle); + return true; } bool stop_playback() { diff --git a/main/drivers/state_tracking.h b/main/drivers/state_tracking.h index 4326f20..a2e8d72 100644 --- a/main/drivers/state_tracking.h +++ b/main/drivers/state_tracking.h @@ -4,20 +4,9 @@ #include #include "esp_vfs_fat.h" -/// @brief An event that represents the physical bomb state moving. -struct EventCB { - // The name of the event. - // This should not contain whitespace or the characters ':', ',' - const char* name; - // An optional callback function for recreating the state. - // Arguments are "ticks" and the serialized argument - void (*replay_callback)(uint32_t, const char*); -}; - -/// @brief Registers a callback for a certain event to be called on replay. -/// @param name The name of the event to respond to. -/// @param replay_callback A function to call to playback this state transition. -void register_event_cb(const char* name, void (*replay_callback)(uint32_t, const char*)); +/// @brief Registers function to be called on replay. +/// @param replay_callback A function to call to playback the event. +void register_replay_fn(void (*replay_fn)(const char*, const char*)); // TODO: add one for generically responding to all events. diff --git a/main/drivers/tft.cpp b/main/drivers/tft.cpp index e3b2f12..fc9ff9b 100644 --- a/main/drivers/tft.cpp +++ b/main/drivers/tft.cpp @@ -17,6 +17,10 @@ static lv_style_t style_screen; SemaphoreHandle_t xGuiSemaphore; +static void replay_handler(const char* event, const char* arg) { + +} + static bool notify_lvgl_flush_ready( esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, @@ -224,5 +228,7 @@ void init_tft() { initialize_display(); xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 5, NULL, 1); + register_replay_fn(replay_handler); + ESP_LOGI(TAG, "TFT initialized!"); } diff --git a/main/main.cpp b/main/main.cpp index cee0cb6..5836ee3 100755 --- a/main/main.cpp +++ b/main/main.cpp @@ -36,6 +36,10 @@ extern "C" void app_main(void) { init_wires(); vTaskDelay(pdMS_TO_TICKS(1000)); + + clean_bomb(); + step0(); + // set_recording_source(stdout, false); FILE* record_file = fopen(MOUNT_POINT "/record.txt", "w"); if (record_file == nullptr) { @@ -44,10 +48,6 @@ extern "C" void app_main(void) { set_recording_source(record_file, true); start_recording(); - clean_bomb(); - step0(); - - set_game_time(initial_game_time); start_game_timer(); total_strikes = 0; @@ -55,7 +55,6 @@ extern "C" void app_main(void) { current_step = 1; if (skip_to_step <= 1) step1(); step_finish_times[current_step-1] = get_game_time(); - stop_recording(); clean_bomb(); current_step = 2; if (skip_to_step <= 2) step2(); @@ -84,5 +83,6 @@ extern "C" void app_main(void) { play_clip_wav(MOUNT_POINT "/diffuse.wav", true, false, 3, 0); display_game_results(); + stop_recording(); }