From 6c4050e9d48fa52b7d76cec08f8a248606e47823 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:49:42 +0800 Subject: [PATCH] feat(travel): Enhance MTR arrival handling with retry logic and improve UI update efficiency --- main/external/mtr/mtr.cpp | 63 ++++++++++++++++++------- main/ui/apps/travel/ui/main.cpp | 47 +++++++++++++----- main/ui/apps/travel/ui/main.h | 5 ++ main/ui/apps/travel/ui/main_handler.cpp | 17 +++++-- 4 files changed, 100 insertions(+), 32 deletions(-) diff --git a/main/external/mtr/mtr.cpp b/main/external/mtr/mtr.cpp index 62c8024..1905eea 100644 --- a/main/external/mtr/mtr.cpp +++ b/main/external/mtr/mtr.cpp @@ -116,30 +116,57 @@ MtrArrivalErrorCode MTRNextTrainHandler::get_next_arrival_info( // Create HTTP client configuration esp_http_client_config_t http_config = {}; http_config.url = url_str.c_str(); - http_config.timeout_ms = 10000; + http_config.timeout_ms = 15000; http_config.transport_type = HTTP_TRANSPORT_OVER_SSL; http_config.crt_bundle_attach = esp_crt_bundle_attach; - // Get HTTP handler and perform request - auto http_handler = network_handler->get_http_handler(std::move(http_config)); - if (!http_handler) { - ESP_LOGE(TAG, "Failed to create HTTP handler"); - return MtrArrivalErrorCode::UNKNOWN; - } - - esp_err_t err = http_handler->perform_request(); - if (err != ESP_OK) { - ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); - return MtrArrivalErrorCode::NO_ARRIVAL_INFO; - } - - // Get response body + // Retry logic for connection failures + constexpr int MAX_RETRIES = 2; + esp_err_t err = ESP_FAIL; char* buffer = nullptr; int total_len = 0; - http_handler->get_body(buffer, total_len); - if (!buffer || total_len <= 0) { - ESP_LOGE(TAG, "Empty response from MTR API"); + for (int retry = 0; retry <= MAX_RETRIES; retry++) { + if (retry > 0) { + ESP_LOGW(TAG, "Retrying HTTP request (%d/%d)", retry, MAX_RETRIES); + vTaskDelay(pdMS_TO_TICKS(500)); + } + + // Create HTTP client configuration for each attempt + esp_http_client_config_t http_config = {}; + http_config.url = url_str.c_str(); + http_config.timeout_ms = 15000; + http_config.transport_type = HTTP_TRANSPORT_OVER_SSL; + http_config.crt_bundle_attach = esp_crt_bundle_attach; + + // Get HTTP handler and perform request + auto http_handler = network_handler->get_http_handler(std::move(http_config)); + if (!http_handler) { + ESP_LOGE(TAG, "Failed to create HTTP handler"); + continue; + } + + err = http_handler->perform_request(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); + continue; + } + + // Get response body + http_handler->get_body(buffer, total_len); + + if (buffer && total_len > 0) { + break; + } + + if (buffer) { + free(buffer); + buffer = nullptr; + } + } + + if (err != ESP_OK || !buffer || total_len <= 0) { + ESP_LOGE(TAG, "Failed to get response after retries"); if (buffer) { free(buffer); } diff --git a/main/ui/apps/travel/ui/main.cpp b/main/ui/apps/travel/ui/main.cpp index 2bfb435..77190ae 100644 --- a/main/ui/apps/travel/ui/main.cpp +++ b/main/ui/apps/travel/ui/main.cpp @@ -167,8 +167,11 @@ void MainUI::update_arrivals(const std::vector& arrival_data) if (i < static_cast(arrival_data.size())) { update_route_display_(route_displays_[i], arrival_data[i]); } else { - // Hide unused route displays - lv_obj_add_flag(route_displays_[i].container, LV_OBJ_FLAG_HIDDEN); + // Hide unused route displays if currently visible + if (route_displays_[i].cached_visible) { + lv_obj_add_flag(route_displays_[i].container, LV_OBJ_FLAG_HIDDEN); + route_displays_[i].cached_visible = false; + } } } @@ -176,37 +179,59 @@ void MainUI::update_arrivals(const std::vector& arrival_data) } void MainUI::update_route_display_(RouteDisplay& display, const RouteArrivalData& data) { - lv_obj_clear_flag(display.container, LV_OBJ_FLAG_HIDDEN); + // Show container if hidden + if (!display.cached_visible) { + lv_obj_clear_flag(display.container, LV_OBJ_FLAG_HIDDEN); + display.cached_visible = true; + } // Update header with line color std::string header_text = data.route.station_name + " → " + data.route.direction_name; - lv_label_set_text(display.header, header_text.c_str()); + if (header_text != display.cached_header_text) { + lv_label_set_text(display.header, header_text.c_str()); + display.cached_header_text = header_text; + } if (!data.route.line_color.empty()) { lv_color_t line_color = hex_to_lv_color_(data.route.line_color); lv_obj_set_style_text_color(display.header, line_color, 0); } - // Update arrival labels + // Update arrival labels - only if text changed for (int i = 0; i < MAX_ARRIVALS_PER_ROUTE; i++) { - if (i < static_cast(data.arrivals.size())) { + std::string arrival_text = ""; + bool should_show = (i < static_cast(data.arrivals.size())); + + if (should_show) { const auto& arrival = data.arrivals[i]; - std::string arrival_text = " " + arrival.arrival_time; + arrival_text = " " + arrival.arrival_time; if (!arrival.arrival_time_full.empty()) { arrival_text += " (" + arrival.arrival_time_full + ")"; } arrival_text += " " + arrival.direction; + } + + if (arrival_text != display.cached_arrival_texts[i]) { lv_label_set_text(display.arrival_labels[i], arrival_text.c_str()); - lv_obj_clear_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN); + display.cached_arrival_texts[i] = arrival_text; + } + + // Handle visibility + if (should_show) { + if (lv_obj_has_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN)) { + lv_obj_clear_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN); + } } else { - lv_label_set_text(display.arrival_labels[i], ""); - lv_obj_add_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN); + if (!lv_obj_has_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN)) { + lv_obj_add_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN); + } } } // Show error if any if (!data.is_valid && !data.error_message.empty()) { - lv_label_set_text(display.arrival_labels[0], (" 錯誤: " + data.error_message).c_str()); + std::string error_text = " 錯誤: " + data.error_message; + lv_label_set_text(display.arrival_labels[0], error_text.c_str()); lv_obj_clear_flag(display.arrival_labels[0], LV_OBJ_FLAG_HIDDEN); } } diff --git a/main/ui/apps/travel/ui/main.h b/main/ui/apps/travel/ui/main.h index cd879a4..d2fa996 100644 --- a/main/ui/apps/travel/ui/main.h +++ b/main/ui/apps/travel/ui/main.h @@ -43,6 +43,11 @@ private: lv_obj_t* container = nullptr; lv_obj_t* header = nullptr; lv_obj_t* arrival_labels[3] = {nullptr, nullptr, nullptr}; // Show up to 3 arrivals per route + + // Cached values for change detection + std::string cached_header_text; + std::string cached_arrival_texts[3]; + bool cached_visible = false; }; RouteDisplay route_displays_[5]; diff --git a/main/ui/apps/travel/ui/main_handler.cpp b/main/ui/apps/travel/ui/main_handler.cpp index b96535f..4866cf6 100644 --- a/main/ui/apps/travel/ui/main_handler.cpp +++ b/main/ui/apps/travel/ui/main_handler.cpp @@ -258,7 +258,18 @@ std::string MainUIHandler::format_arrival_time_(const std::string& api_time) { return time_part; } - return api_time; + // Try to find HH:MM pattern in the string + for (size_t i = 0; i < api_time.length() - 5; i++) { + if (api_time[i] == ':' && i > 0 && i < api_time.length() - 3) { + std::string candidate = api_time.substr(i - 2, 5); + if (isdigit(candidate[0]) && isdigit(candidate[1]) && candidate[2] == ':' && + isdigit(candidate[3]) && isdigit(candidate[4])) { + return candidate; + } + } + } + + return ""; // Return empty instead of raw input } std::string MainUIHandler::format_arrival_time_full_(const std::string& api_time) { @@ -280,8 +291,8 @@ std::string MainUIHandler::get_current_time_string_() { auto now = std::time(nullptr); auto tm = *std::localtime(&now); - char buffer[9]; // HH:MM:SS\0 - strftime(buffer, sizeof(buffer), "%H:%M:%S", &tm); + char buffer[6]; // HH:MM\0 + strftime(buffer, sizeof(buffer), "%H:%M", &tm); return std::string(buffer); }