feat(travel): Enhance MTR arrival handling with retry logic and improve UI update efficiency
This commit is contained in:
63
main/external/mtr/mtr.cpp
vendored
63
main/external/mtr/mtr.cpp
vendored
@@ -116,30 +116,57 @@ MtrArrivalErrorCode MTRNextTrainHandler::get_next_arrival_info(
|
|||||||
// Create HTTP client configuration
|
// Create HTTP client configuration
|
||||||
esp_http_client_config_t http_config = {};
|
esp_http_client_config_t http_config = {};
|
||||||
http_config.url = url_str.c_str();
|
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.transport_type = HTTP_TRANSPORT_OVER_SSL;
|
||||||
http_config.crt_bundle_attach = esp_crt_bundle_attach;
|
http_config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||||
|
|
||||||
// Get HTTP handler and perform request
|
// Retry logic for connection failures
|
||||||
auto http_handler = network_handler->get_http_handler(std::move(http_config));
|
constexpr int MAX_RETRIES = 2;
|
||||||
if (!http_handler) {
|
esp_err_t err = ESP_FAIL;
|
||||||
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
|
|
||||||
char* buffer = nullptr;
|
char* buffer = nullptr;
|
||||||
int total_len = 0;
|
int total_len = 0;
|
||||||
http_handler->get_body(buffer, total_len);
|
|
||||||
|
|
||||||
if (!buffer || total_len <= 0) {
|
for (int retry = 0; retry <= MAX_RETRIES; retry++) {
|
||||||
ESP_LOGE(TAG, "Empty response from MTR API");
|
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) {
|
if (buffer) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,8 +167,11 @@ void MainUI::update_arrivals(const std::vector<RouteArrivalData>& arrival_data)
|
|||||||
if (i < static_cast<int>(arrival_data.size())) {
|
if (i < static_cast<int>(arrival_data.size())) {
|
||||||
update_route_display_(route_displays_[i], arrival_data[i]);
|
update_route_display_(route_displays_[i], arrival_data[i]);
|
||||||
} else {
|
} else {
|
||||||
// Hide unused route displays
|
// Hide unused route displays if currently visible
|
||||||
lv_obj_add_flag(route_displays_[i].container, LV_OBJ_FLAG_HIDDEN);
|
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<RouteArrivalData>& arrival_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainUI::update_route_display_(RouteDisplay& display, const RouteArrivalData& 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
|
// Update header with line color
|
||||||
std::string header_text = data.route.station_name + " → " + data.route.direction_name;
|
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()) {
|
if (!data.route.line_color.empty()) {
|
||||||
lv_color_t line_color = hex_to_lv_color_(data.route.line_color);
|
lv_color_t line_color = hex_to_lv_color_(data.route.line_color);
|
||||||
lv_obj_set_style_text_color(display.header, line_color, 0);
|
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++) {
|
for (int i = 0; i < MAX_ARRIVALS_PER_ROUTE; i++) {
|
||||||
if (i < static_cast<int>(data.arrivals.size())) {
|
std::string arrival_text = "";
|
||||||
|
bool should_show = (i < static_cast<int>(data.arrivals.size()));
|
||||||
|
|
||||||
|
if (should_show) {
|
||||||
const auto& arrival = data.arrivals[i];
|
const auto& arrival = data.arrivals[i];
|
||||||
std::string arrival_text = " " + arrival.arrival_time;
|
arrival_text = " " + arrival.arrival_time;
|
||||||
if (!arrival.arrival_time_full.empty()) {
|
if (!arrival.arrival_time_full.empty()) {
|
||||||
arrival_text += " (" + arrival.arrival_time_full + ")";
|
arrival_text += " (" + arrival.arrival_time_full + ")";
|
||||||
}
|
}
|
||||||
arrival_text += " " + arrival.direction;
|
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_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 {
|
} else {
|
||||||
lv_label_set_text(display.arrival_labels[i], "");
|
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);
|
lv_obj_add_flag(display.arrival_labels[i], LV_OBJ_FLAG_HIDDEN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show error if any
|
// Show error if any
|
||||||
if (!data.is_valid && !data.error_message.empty()) {
|
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);
|
lv_obj_clear_flag(display.arrival_labels[0], LV_OBJ_FLAG_HIDDEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ private:
|
|||||||
lv_obj_t* container = nullptr;
|
lv_obj_t* container = nullptr;
|
||||||
lv_obj_t* header = nullptr;
|
lv_obj_t* header = nullptr;
|
||||||
lv_obj_t* arrival_labels[3] = {nullptr, nullptr, nullptr}; // Show up to 3 arrivals per route
|
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];
|
RouteDisplay route_displays_[5];
|
||||||
|
|
||||||
|
|||||||
@@ -258,7 +258,18 @@ std::string MainUIHandler::format_arrival_time_(const std::string& api_time) {
|
|||||||
return time_part;
|
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) {
|
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 now = std::time(nullptr);
|
||||||
auto tm = *std::localtime(&now);
|
auto tm = *std::localtime(&now);
|
||||||
|
|
||||||
char buffer[9]; // HH:MM:SS\0
|
char buffer[6]; // HH:MM\0
|
||||||
strftime(buffer, sizeof(buffer), "%H:%M:%S", &tm);
|
strftime(buffer, sizeof(buffer), "%H:%M", &tm);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user