fix(multimodal): recover stalled downstream sessions
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
#endif
|
||||
|
||||
#define CONTROL_PLANE_DOWNSTREAM_PLAYBACK_GUARD_MS 220
|
||||
#define CONTROL_PLANE_DOWNSTREAM_STALL_STOP_GRACE_MS 2000
|
||||
#define CONTROL_PLANE_IDLE_TRANSPORT_SHUTDOWN_MS 30000
|
||||
#define CONTROL_PLANE_WATCHDOG_TASK_STACK_SIZE 6144
|
||||
#define CONTROL_PLANE_SERVER_SUCCESS_PROMPT_FILE "/littlefs/audio_prompts/server_connect_success.opus"
|
||||
@@ -57,6 +58,9 @@ static bool s_multimodal_watchdog_task_starting;
|
||||
static bool s_model_command_dispatched;
|
||||
static bool s_server_success_prompt_played;
|
||||
static bool s_cloud_chouqian_pending;
|
||||
static bool s_downstream_stall_recovery_pending;
|
||||
static bool s_downstream_stall_stop_sent;
|
||||
static uint32_t s_downstream_stall_recovery_started_ms;
|
||||
static control_plane_model_command_callback_t s_model_command_cb;
|
||||
static void *s_model_command_cb_user_data;
|
||||
static portMUX_TYPE s_multimodal_runtime_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
@@ -118,6 +122,55 @@ static void set_multimodal_start_flag(bool *flag, bool value)
|
||||
portEXIT_CRITICAL(&s_multimodal_runtime_lock);
|
||||
}
|
||||
|
||||
static bool control_plane_begin_downstream_stall_recovery(void)
|
||||
{
|
||||
bool started = false;
|
||||
portENTER_CRITICAL(&s_multimodal_runtime_lock);
|
||||
if (!s_downstream_stall_recovery_pending) {
|
||||
s_downstream_stall_recovery_pending = true;
|
||||
s_downstream_stall_stop_sent = false;
|
||||
s_downstream_stall_recovery_started_ms = 0U;
|
||||
started = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_multimodal_runtime_lock);
|
||||
return started;
|
||||
}
|
||||
|
||||
static bool control_plane_get_downstream_stall_recovery(bool *out_stop_sent,
|
||||
uint32_t *out_started_ms)
|
||||
{
|
||||
bool pending = false;
|
||||
portENTER_CRITICAL(&s_multimodal_runtime_lock);
|
||||
pending = s_downstream_stall_recovery_pending;
|
||||
if (out_stop_sent != NULL) {
|
||||
*out_stop_sent = s_downstream_stall_stop_sent;
|
||||
}
|
||||
if (out_started_ms != NULL) {
|
||||
*out_started_ms = s_downstream_stall_recovery_started_ms;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_multimodal_runtime_lock);
|
||||
return pending;
|
||||
}
|
||||
|
||||
static void control_plane_mark_downstream_stall_stop_sent(uint32_t now_ms)
|
||||
{
|
||||
portENTER_CRITICAL(&s_multimodal_runtime_lock);
|
||||
if (s_downstream_stall_recovery_pending) {
|
||||
s_downstream_stall_stop_sent = true;
|
||||
s_downstream_stall_recovery_started_ms = now_ms;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_multimodal_runtime_lock);
|
||||
}
|
||||
|
||||
static void control_plane_clear_downstream_stall_recovery(void)
|
||||
{
|
||||
portENTER_CRITICAL(&s_multimodal_runtime_lock);
|
||||
s_downstream_stall_recovery_pending = false;
|
||||
s_downstream_stall_stop_sent = false;
|
||||
s_downstream_stall_recovery_started_ms = 0U;
|
||||
portEXIT_CRITICAL(&s_multimodal_runtime_lock);
|
||||
}
|
||||
|
||||
static bool try_begin_multimodal_task_start(TaskHandle_t *slot, bool *start_flag)
|
||||
{
|
||||
bool can_start = false;
|
||||
@@ -454,6 +507,49 @@ static void multimodal_watchdog_task(void *arg)
|
||||
}
|
||||
|
||||
uint32_t now_ms = control_plane_now_ms();
|
||||
bool stall_stop_sent = false;
|
||||
uint32_t stall_recovery_started_ms = 0U;
|
||||
if (control_plane_get_downstream_stall_recovery(&stall_stop_sent,
|
||||
&stall_recovery_started_ms)) {
|
||||
if (!state.connected) {
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
} else if (!stall_stop_sent) {
|
||||
ESP_LOGW(TAG,
|
||||
"Downstream stall recovery started: request Stop and wait up to %u ms",
|
||||
(unsigned)CONTROL_PLANE_DOWNSTREAM_STALL_STOP_GRACE_MS);
|
||||
esp_err_t stop_err =
|
||||
control_plane_multimodal_request_downstream_stall_stop();
|
||||
if (stop_err == ESP_OK) {
|
||||
control_plane_mark_downstream_stall_stop_sent(now_ms);
|
||||
} else {
|
||||
ESP_LOGW(TAG,
|
||||
"Downstream stall Stop request failed: %s; retire transport now",
|
||||
esp_err_to_name(stop_err));
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
control_plane_multimodal_mark_transport_unavailable(
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_DOWNSTREAM_STALL,
|
||||
"downstream_stall_stop_failed");
|
||||
(void)control_plane_schedule_websocket_wait_task();
|
||||
}
|
||||
continue;
|
||||
} else if (!state.has_active_task) {
|
||||
ESP_LOGI(TAG,
|
||||
"Downstream stall recovered by server Stop acknowledgement; keep WebSocket");
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
} else if ((now_ms - stall_recovery_started_ms) >=
|
||||
CONTROL_PLANE_DOWNSTREAM_STALL_STOP_GRACE_MS) {
|
||||
ESP_LOGW(TAG,
|
||||
"Downstream stall Stop acknowledgement timed out after %u ms; retire transport",
|
||||
(unsigned)CONTROL_PLANE_DOWNSTREAM_STALL_STOP_GRACE_MS);
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
control_plane_multimodal_mark_transport_unavailable(
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_DOWNSTREAM_STALL,
|
||||
"downstream_stall_recovery");
|
||||
(void)control_plane_schedule_websocket_wait_task();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.connected && !state.has_active_task) {
|
||||
if (idle_transport_started_ms == 0U) {
|
||||
idle_transport_started_ms = now_ms;
|
||||
@@ -545,6 +641,7 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event
|
||||
}
|
||||
|
||||
case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_DISCONNECTED:
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
control_plane_clear_cloud_chouqian_pending("transport disconnected");
|
||||
control_plane_multimodal_stop_uplink();
|
||||
ESP_LOGI(TAG, "Schedule fresh-auth reconnect after transport disconnect");
|
||||
@@ -552,6 +649,7 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event
|
||||
break;
|
||||
|
||||
case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_ERROR:
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
control_plane_clear_cloud_chouqian_pending("transport error");
|
||||
control_plane_multimodal_stop_uplink();
|
||||
ESP_LOGI(TAG, "Schedule fresh-auth reconnect after transport error");
|
||||
@@ -559,6 +657,7 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event
|
||||
break;
|
||||
|
||||
case CONTROL_PLANE_MULTIMODAL_EVENT_TASK_FAILED:
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
control_plane_clear_cloud_chouqian_pending("task failed");
|
||||
control_plane_request_server_error_expression(DOMAIN_EXPRESSION_TRIGGER_SERVER_TASK_FAILED);
|
||||
control_plane_multimodal_stop_uplink();
|
||||
@@ -605,6 +704,15 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event
|
||||
control_plane_request_server_error_expression(DOMAIN_EXPRESSION_TRIGGER_SERVER_OUTPUT_ERROR);
|
||||
break;
|
||||
|
||||
case CONTROL_PLANE_MULTIMODAL_EVENT_DOWNSTREAM_STALLED:
|
||||
if (control_plane_begin_downstream_stall_recovery()) {
|
||||
ESP_LOGW(TAG,
|
||||
"Downstream stall recovery queued for watchdog worker");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Downstream stall recovery already pending");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -612,6 +720,8 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event
|
||||
|
||||
esp_err_t control_plane_start_multimodal_client(void)
|
||||
{
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
|
||||
esp_err_t prepare_err = control_plane_prepare_for_multimodal_start();
|
||||
if (prepare_err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Prepare multimodal start failed: %s", esp_err_to_name(prepare_err));
|
||||
@@ -666,6 +776,7 @@ esp_err_t control_plane_start_multimodal_client(void)
|
||||
|
||||
void control_plane_stop_multimodal_client(void)
|
||||
{
|
||||
control_plane_clear_downstream_stall_recovery();
|
||||
control_plane_clear_cloud_chouqian_pending("multimodal client stopped");
|
||||
stop_watchdog_task();
|
||||
control_plane_multimodal_stop();
|
||||
|
||||
Reference in New Issue
Block a user