修复了WS连接稳定性问题

This commit is contained in:
CSBOOK\44744
2026-06-27 20:29:09 +08:00
parent b829cd8ebc
commit caa4a7eded
16 changed files with 2197 additions and 248 deletions
@@ -119,6 +119,8 @@
static const char *TAG = "cp_mm_session";
static bool s_started;
static bool s_starting;
static bool s_stopping;
static control_plane_multimodal_auth_info_t s_auth;
static char s_task_id[40];
@@ -167,6 +169,7 @@ static StaticSemaphore_t s_state_mutex_buf;
typedef enum {
LOCAL_RESPONDING_EVENT_STARTED = 0,
LOCAL_RESPONDING_EVENT_ENDED,
LOCAL_RESPONDING_EVENT_QUIT,
} local_responding_event_type_t;
typedef struct {
@@ -418,6 +421,8 @@ static const char *transport_loss_reason_name(control_plane_transport_loss_reaso
return "listening_timeout";
case CONTROL_PLANE_TRANSPORT_LOSS_NO_RESPONSE_TIMEOUT:
return "no_response_timeout";
case CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR:
return "protocol_error";
default:
return "unknown";
}
@@ -444,6 +449,8 @@ void control_plane_multimodal_mark_transport_unavailable(
if (lock_state()) {
s_started = false;
s_starting = false;
s_stopping = false;
s_transport_available = false;
memset(&s_auth, 0, sizeof(s_auth));
reset_session_state_locked();
@@ -451,6 +458,19 @@ void control_plane_multimodal_mark_transport_unavailable(
}
control_plane_multimodal_stop_uplink();
bool close_transport = reason == CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR ||
reason == CONTROL_PLANE_TRANSPORT_LOSS_SEND_TEXT_FAILURE ||
reason == CONTROL_PLANE_TRANSPORT_LOSS_LISTENING_TIMEOUT ||
reason == CONTROL_PLANE_TRANSPORT_LOSS_NO_RESPONSE_TIMEOUT;
if (close_transport) {
esp_err_t stop_err = domain_runtime_multimodal_transport_stop(0);
if (stop_err != ESP_OK) {
ESP_LOGW(TAG,
"Async transport stop after unavailable ended with %s",
esp_err_to_name(stop_err));
}
}
}
static void reset_session_state(void)
@@ -523,7 +543,19 @@ static void local_responding_event_worker(void *arg)
idle_ticks = 1;
}
if (xQueueReceive(s_local_responding_event_queue, &msg, idle_ticks) != pdTRUE) {
s_local_responding_event_task = NULL;
if (lock_state()) {
s_local_responding_event_task = NULL;
unlock_state();
}
vTaskDelete(NULL);
return;
}
if (msg.type == LOCAL_RESPONDING_EVENT_QUIT) {
if (lock_state()) {
s_local_responding_event_task = NULL;
unlock_state();
}
vTaskDelete(NULL);
return;
}
@@ -594,6 +626,50 @@ static esp_err_t ensure_local_responding_event_worker(void)
return ESP_OK;
}
static void stop_local_responding_event_worker(void)
{
QueueHandle_t queue = NULL;
TaskHandle_t task = NULL;
if (lock_state()) {
queue = s_local_responding_event_queue;
task = s_local_responding_event_task;
s_local_responding_started_send_pending = false;
s_local_responding_ended_send_pending = false;
unlock_state();
}
if (queue != NULL && task != NULL) {
local_responding_event_msg_t quit = {
.type = LOCAL_RESPONDING_EVENT_QUIT,
};
(void)xQueueSend(queue, &quit, 0);
TickType_t start_tick = xTaskGetTickCount();
TickType_t wait_ticks = pdMS_TO_TICKS(300);
if (wait_ticks == 0) {
wait_ticks = 1;
}
while ((xTaskGetTickCount() - start_tick) < wait_ticks) {
bool running = false;
if (lock_state()) {
running = s_local_responding_event_task != NULL;
unlock_state();
}
if (!running) {
break;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
if (lock_state()) {
s_local_responding_event_task = NULL;
if (s_local_responding_event_queue != NULL) {
xQueueReset(s_local_responding_event_queue);
}
unlock_state();
}
}
static void queue_local_responding_event(local_responding_event_type_t type)
{
char task_id[sizeof(s_task_id)] = {0};
@@ -1198,7 +1274,12 @@ static void process_text_json(const char *json_text)
request_id,
dialog_id[0] ? dialog_id : "-");
if (cJSON_IsString(failed_task_id) &&
bool protocol_error = strcmp(error_code, "InvalidParameter") == 0 ||
strcmp(error_name, "InvalidParameter") == 0 ||
strstr(error_message, "Invalid action") != NULL;
if (!protocol_error &&
cJSON_IsString(failed_task_id) &&
failed_task_id->valuestring != NULL &&
failed_task_id->valuestring[0] != '\0') {
bool matches_active_task = false;
@@ -1218,7 +1299,13 @@ static void process_text_json(const char *json_text)
}
stop_local_downstream_playback();
reset_session_state_if_task_matches(task_id);
if (protocol_error) {
control_plane_multimodal_mark_transport_unavailable(
CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR,
__func__);
} else {
reset_session_state_if_task_matches(task_id);
}
notify_event(CONTROL_PLANE_MULTIMODAL_EVENT_TASK_FAILED);
} else if (strcmp(event->valuestring, "task-started") == 0) {
notify_event(CONTROL_PLANE_MULTIMODAL_EVENT_TASK_STARTED);
@@ -1378,8 +1465,10 @@ static void process_text_json(const char *json_text)
cJSON *error_message = cJSON_GetObjectItemCaseSensitive(output, "error_message");
cJSON *llm_request_id = cJSON_GetObjectItemCaseSensitive(output, "llm_request_id");
char dialog_id[sizeof(s_dialog_id)] = {0};
char task_id[sizeof(s_task_id)] = {0};
if (lock_state()) {
s_remote_responding = false;
strlcpy(task_id, s_task_id, sizeof(task_id));
strlcpy(dialog_id, s_dialog_id, sizeof(dialog_id));
unlock_state();
}
@@ -1390,6 +1479,11 @@ static void process_text_json(const char *json_text)
cJSON_IsString(error_message) && error_message->valuestring ? error_message->valuestring : "unknown",
cJSON_IsString(llm_request_id) && llm_request_id->valuestring ? llm_request_id->valuestring : "-",
dialog_id[0] ? dialog_id : "-");
control_plane_multimodal_stop_uplink();
stop_local_downstream_playback();
control_plane_multimodal_mark_transport_unavailable(
CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR,
__func__);
notify_event(CONTROL_PLANE_MULTIMODAL_EVENT_OUTPUT_ERROR);
}
}
@@ -1409,6 +1503,9 @@ static void transport_event_bridge(const domain_multimodal_transport_event_t *ev
switch (event->type) {
case DOMAIN_MULTIMODAL_TRANSPORT_EVENT_CONNECTED:
if (lock_state()) {
s_started = true;
s_starting = false;
s_stopping = false;
s_transport_available = true;
s_last_heartbeat_tick = xTaskGetTickCount();
s_last_downstream_tick = s_last_heartbeat_tick;
@@ -1538,17 +1635,29 @@ esp_err_t control_plane_multimodal_start(void)
#endif
if (lock_state()) {
bool already_started = s_started;
unlock_state();
bool already_started = s_started || s_auth.token[0] != '\0';
bool starting = s_starting;
bool stopping = s_stopping;
if (already_started) {
unlock_state();
return ESP_OK;
}
if (starting || stopping) {
unlock_state();
return ESP_ERR_INVALID_STATE;
}
s_starting = true;
unlock_state();
}
control_plane_multimodal_auth_info_t auth = {0};
esp_err_t err = control_plane_multimodal_auth_fetch(&auth);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Auth fetch failed: %s", esp_err_to_name(err));
if (lock_state()) {
s_starting = false;
unlock_state();
}
return err;
}
@@ -1557,6 +1666,10 @@ esp_err_t control_plane_multimodal_start(void)
NULL);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Register downstream playback callback failed: %s", esp_err_to_name(err));
if (lock_state()) {
s_starting = false;
unlock_state();
}
return err;
}
@@ -1564,12 +1677,15 @@ esp_err_t control_plane_multimodal_start(void)
if (err != ESP_OK) {
ESP_LOGW(TAG, "Transport start failed: %s", esp_err_to_name(err));
(void)domain_runtime_multimodal_audio_register_downstream_playback_callback(NULL, NULL);
if (lock_state()) {
s_starting = false;
unlock_state();
}
return err;
}
if (lock_state()) {
s_auth = auth;
s_started = true;
unlock_state();
}
return ESP_OK;
@@ -1577,7 +1693,22 @@ esp_err_t control_plane_multimodal_start(void)
void control_plane_multimodal_stop(void)
{
bool already_stopping = false;
if (lock_state()) {
already_stopping = s_stopping;
if (!s_stopping) {
s_stopping = true;
}
s_starting = false;
unlock_state();
}
if (already_stopping) {
ESP_LOGI(TAG, "Multimodal stop already in progress");
return;
}
control_plane_multimodal_stop_uplink();
stop_local_responding_event_worker();
(void)control_plane_multimodal_register_downstream_audio_callback(NULL, NULL);
(void)control_plane_multimodal_register_event_callback(NULL, NULL);
(void)domain_runtime_multimodal_audio_register_downstream_playback_callback(NULL, NULL);
@@ -1589,6 +1720,9 @@ void control_plane_multimodal_stop(void)
if (lock_state()) {
s_started = false;
s_starting = false;
s_stopping = false;
s_transport_available = false;
memset(&s_auth, 0, sizeof(s_auth));
reset_session_state_locked();
unlock_state();
@@ -2123,6 +2257,8 @@ esp_err_t control_plane_multimodal_get_runtime_state(control_plane_multimodal_ru
return ESP_ERR_TIMEOUT;
}
out_state->connected = transport_available_locked();
out_state->starting = s_starting;
out_state->stopping = s_stopping;
out_state->has_active_task = s_task_id[0] != '\0';
out_state->server_listening = s_server_listening;
out_state->remote_responding = s_remote_responding;
@@ -2227,6 +2363,9 @@ esp_err_t control_plane_multimodal_start_uplink(void)
void control_plane_multimodal_stop_uplink(void)
{
if (!domain_runtime_multimodal_audio_is_uplink_running()) {
return;
}
esp_err_t err = domain_runtime_multimodal_audio_stop_uplink(CONTROL_PLANE_UPLINK_READY_WAIT_MS);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Mic uplink stop ended with %s", esp_err_to_name(err));