From: hcj <1224667075@qq.com> Date: Fri, 10 Oct 2025 09:41:49 +0000 (+0800) Subject: 优化ln打断效果,增快反应速度 X-Git-Tag: V1.0.9_20251128_194606_LN~250^2~20 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=c2cf744661a6db1177b58b393a88bc10ff01f1a5;p=mirrors%2FFD230.git 优化ln打断效果,增快反应速度 --- diff --git a/code/sdk/zhihui/ai_new/ai_V2.5/ai_v2_5_main.cpp b/code/sdk/zhihui/ai_new/ai_V2.5/ai_v2_5_main.cpp index 323906ee4..a462d56f3 100644 --- a/code/sdk/zhihui/ai_new/ai_V2.5/ai_v2_5_main.cpp +++ b/code/sdk/zhihui/ai_new/ai_V2.5/ai_v2_5_main.cpp @@ -126,7 +126,7 @@ static void ai_message_handler(const char *raw_data, uint32_t timestamp) * @param timestamp 消息时间戳 * @param data_len 数据长度 */ -static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestamp, uint16_t data_len) +static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestamp, uint32_t data_len) { printf("[AI] Received binary message at %u, length: %d bytes\n", timestamp, data_len); @@ -156,7 +156,7 @@ static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestam static void ai_url_handler(char *url, size_t url_size, char *header_buf, size_t header_size) { printf("[AI] Building WebSocket URL and headers\n"); - snprintf(url, url_size, "ws://118.145.158.207:22080/vc/chat"); + snprintf(url, url_size, "ws://115.190.94.208:22080/vc/chat"); } @@ -289,7 +289,7 @@ static int ai_service_init(void) return 0; } -static void AIVoiceDataCallback_(const uint8_t* data, uint32_t size, void* userData) +static void AIVoiceDataCallback_V25(const uint8_t* data, uint32_t size, void* userData) { // MEDIA_INFO_LOG("Received audio data: size=%u bytes", size); // 在这里处理音频数据,例如: @@ -347,12 +347,12 @@ int ai_service_start(void) }; AIVoiceAudioParams audioParams_out = { - .audioFormat = OPUS, + .audioFormat = PCM, .channelCount = AI_DEFAULT_CHANNEL_COUNT, .sampleRate = AI_DEFAULT_SAMPLE_RATE }; - old_Start_AIVoice(AIVoiceDataCallback_, nullptr, &audioParams_in,&audioParams_out,false); + old_Start_AIVoice(AIVoiceDataCallback_V25,nullptr, nullptr,nullptr,false); return 0; } @@ -744,7 +744,7 @@ char* ai_generate_start_config(const char* device_id, const char* bot_id, goto error; } - cJSON_AddStringToObject(outbound_cfg, "format", "pcm"); + cJSON_AddStringToObject(outbound_cfg, "format", "mp3"); cJSON_AddNumberToObject(outbound_cfg, "sample_rate", AI_DEFAULT_SAMPLE_RATE); cJSON_AddNumberToObject(outbound_cfg, "bit_rate", AI_DEFAULT_BIT_RATE); cJSON_AddNumberToObject(outbound_cfg, "channel", AI_DEFAULT_CHANNEL_COUNT); @@ -785,7 +785,7 @@ char* ai_generate_start_config(const char* device_id, const char* bot_id, goto error; } - cJSON_AddStringToObject(inbound_cfg, "format", "pcm"); + cJSON_AddStringToObject(inbound_cfg, "format", "mp3"); cJSON_AddNumberToObject(inbound_cfg, "sample_rate", AI_DEFAULT_SAMPLE_RATE); cJSON_AddNumberToObject(inbound_cfg, "bit_rate", AI_DEFAULT_BIT_RATE); cJSON_AddNumberToObject(inbound_cfg, "channel", AI_DEFAULT_CHANNEL_COUNT); diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h index bcc2c7c28..b914290ce 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h @@ -337,6 +337,7 @@ typedef enum { WS_CMD_CONNECTED, /* WebSocket连接成功 */ WS_CMD_DISCONNECTED, /* WebSocket断连 */ WS_CMD_AUDIO_PLAYBACK_END, /* 音频播放结束 */ + WS_CMD_AUDIO_RECORDING_START, /* 开始录音上传 */ WS_CMD_MAX /* 最大命令 */ } ws_command_t; diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c index 5e6dc3cbd..1ba6c62a3 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -12,6 +12,9 @@ #include "mbedtls/base64.h" #include "ai_media.h" // Added for AI_RegisterWriteAudioDataCallback +// 功能开关宏定义 +#define ENABLE_BINARY_DATA_INTERRUPT_CHECK 1 // 启用二进制数据间隔检查 + // 打印等级定义 typedef enum { LN_WS_LOG_ERROR = 0, // 错误信息 @@ -53,6 +56,7 @@ static const char *g_log_level_str[] = { #define WS_STATE_CONNECTING 1 // 连接中 #define WS_STATE_CONNECTED 2 // 连接成功 #define WS_STATE_SLEEP 3 // 休眠状态 +#define WS_STATE_RECONNECTING 4 // 重连执行中 /* WebSocket关闭状态枚举 */ typedef enum { @@ -89,7 +93,16 @@ static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_comm static void ln_ws_process_message_with_callback(const ws_data_msg_t *data_msg); static int ln_ws_close_with_status(int client_fd); static ws_close_status_t ln_ws_get_close_status(void); +static int ln_ws_check_chat_id_match(const char *chat_id, cJSON *root); +static int ln_ws_reconnect(void); static void ln_ws_reset_close_status(void); +static bool ln_ws_check_binary_data_interrupt(void); +static void ln_ws_reconnect_success_callback(void); +static int ln_ws_audio_buffer_init(void); +static void ln_ws_audio_buffer_cleanup(void); +static int ln_ws_audio_buffer_add(const uint8_t *audio_data, uint32_t audio_size, bool finished); +static void ln_ws_audio_buffer_flush(void); +static int ln_ws_send_audio_data_direct(const uint8_t *audio_data, uint32_t audio_size, bool finished); const char* ln_ws_get_connection_state_string(void); /* Base64缓存管理函数 */ @@ -117,6 +130,19 @@ static audio_chat_context_t g_audio_chat_ctx = {0}; // 音频聊天结束标志 static bool g_audio_chat_ending = false; +// 音频数据缓存结构 +typedef struct { + uint8_t *data; // 音频数据 + uint32_t size; // 数据大小 + bool finished; // 是否结束 + uint32_t timestamp; // 时间戳 +} audio_buffer_item_t; + +// 音频数据缓存 +static audio_buffer_item_t *g_audio_buffer = NULL; +static uint32_t g_audio_buffer_count = 0; +static uint32_t g_audio_buffer_capacity = 0; +static bool g_audio_buffering_enabled = false; // 当前对话匹配标志 static bool g_chat_id_matched = false; // 当前对话ID是否匹配 @@ -125,6 +151,7 @@ static bool g_chat_id_matched = false; // 当前对话ID是否匹配 static uint32_t g_last_audio_write_tick = 0; // 最后一次音频写入的tick static bool g_audio_playback_active = false; // 音频播放是否活跃 static uint32_t g_audio_timeout_ticks = 1500; // 音频超时tick数(默认1.5秒) +static uint32_t g_last_binary_data_tick = 0; // 最后一次接收二进制数据的tick // 函数声明 static void generate_chat_id(char *chat_id, size_t size); @@ -402,11 +429,22 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f return -1; } - if (g_ws_connected != WS_STATE_CONNECTED) { + // 检查连接状态 + if (g_ws_connected == WS_STATE_RECONNECTING) { + // 重连中,缓存音频数据 + LN_WS_INFO("Connection reconnecting, buffering audio data (size: %u)\n", audio_size); + return ln_ws_audio_buffer_add(audio_data, audio_size, finished); + } else if (g_ws_connected != WS_STATE_CONNECTED) { LN_WS_WARN("Cannot send audio data, connection state: %d\n", g_ws_connected); return -1; } + // 如果连接正常且有缓存数据,先刷新缓存 + if (g_audio_buffer_count > 0) { + LN_WS_INFO("Connection restored, flushing %u buffered audio data items\n", g_audio_buffer_count); + ln_ws_audio_buffer_flush(); + } + // 如果聊天未开始,生成新的聊天ID if (!g_audio_chat_ctx.is_active) { generate_chat_id(g_audio_chat_ctx.chat_id, sizeof(g_audio_chat_ctx.chat_id)); @@ -420,6 +458,16 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f // 增加消息计数器 g_audio_chat_ctx.message_counter++; + // 如果是第一条消息,触发录音开始事件 + if (g_audio_chat_ctx.message_counter == 1) { + LN_WS_INFO("First audio message, triggering recording start event\n"); + + // 触发录音开始事件 + if (g_ws_message_callback != NULL) { + g_ws_message_callback(NULL, WS_CMD_AUDIO_RECORDING_START); + } + } + // 生成消息ID char message_id[128]; generate_message_id(message_id, sizeof(message_id), g_audio_chat_ctx.chat_id, g_audio_chat_ctx.message_counter); @@ -503,7 +551,7 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f // 发送JSON字符串 int ret = uapi_ws_send_text(json_string); - LN_WS_DEBUG("Sent audio data: chatId=%s, messageId=%s, size=%u, finished=%s\n", + LN_WS_ERROR("Sent audio data: chatId=%s, messageId=%s, size=%u, finished=%s\n", g_audio_chat_ctx.chat_id, message_id, audio_size, finished ? "true" : "false"); // 清理内存 @@ -609,6 +657,9 @@ static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestam { LN_WS_DEBUG("Received binary message at %u, length: %d bytes\n", timestamp, data_len); + // 记录接收数据的时间 + g_last_binary_data_tick = osKernelGetTickCount(); + if (!is_chat_id_matched()) { LN_WS_WARN("Chat ID not matched\n"); return; @@ -769,6 +820,7 @@ int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks) g_ws_callbacks.heartbeat_callback = ln_ws_heartbeat_callback; g_ws_callbacks.binary_callback = ai_binary_message_handler; g_ws_callbacks.incomplete_callback= ln_ws_incomplete_callback; + g_ws_callbacks.reconnect_success_callback = ln_ws_reconnect_success_callback; // 启动WebSocket服务 int ret = uapi_ws_start(&g_ws_callbacks); if (ret != 0) { @@ -878,6 +930,9 @@ int ln_ws_send_voice_chat_interrupt(const char *reason) return -1; } + // 检查是否需要在1秒内有数据时触发重连 + bool need_reconnect = ln_ws_check_binary_data_interrupt(); + if (g_ws_connected != WS_STATE_CONNECTED) { LN_WS_WARN("Cannot send voice chat interrupt, connection state: %d\n", g_ws_connected); return -1; @@ -931,6 +986,14 @@ int ln_ws_send_voice_chat_interrupt(const char *reason) if (ret == 0) { LN_WS_DEBUG("VoiceChatInterrupt sent successfully\n"); + + if (need_reconnect) { + // 如果检测到频繁数据接收,直接重连 + LN_WS_INFO("Triggering reconnection due to frequent binary data reception\n"); + if (ln_ws_reconnect() != 0) { + LN_WS_ERROR("Failed to reconnect after frequent data reception\n"); + } + } } else { LN_WS_ERROR("Failed to send VoiceChatInterrupt\n"); } @@ -953,6 +1016,9 @@ int ln_ws_stop(void) // 清理Base64缓存 ln_ws_base64_cache_cleanup(); + // 清理音频缓存 + ln_ws_audio_buffer_cleanup(); + return ret; } @@ -974,6 +1040,8 @@ const char* ln_ws_get_connection_state_string(void) return "连接成功"; case WS_STATE_SLEEP: return "休眠状态"; + case WS_STATE_RECONNECTING: + return "重连执行中"; default: return "未知状态"; } @@ -1174,12 +1242,8 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0'; // 检查对话ID是否匹配当前活跃对话 - if (strcmp(g_audio_chat_ctx.chat_id, data_msg->chat_id) == 0) { - set_chat_id_matched(); - } else { - LN_WS_ERROR("VoiceChatResponse from non-active chat %s, ignoring\n", data_msg->chat_id); - cJSON_Delete(root); - return -1; // 返回错误,不处理此消息 + if (ln_ws_check_chat_id_match(data_msg->chat_id, root) != 0) { + return -1; // 不匹配,已清理JSON } } @@ -1223,12 +1287,8 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0'; // 检查对话ID是否匹配当前活跃对话 - if (strcmp(g_audio_chat_ctx.chat_id, data_msg->chat_id) == 0) { - set_chat_id_matched(); - } else { - LN_WS_WARN("ChooseStoryResponse from non-active chat %s, ignoring\n", data_msg->chat_id); - cJSON_Delete(root); - return -1; // 返回错误,不处理此消息 + if (ln_ws_check_chat_id_match(data_msg->chat_id, root) != 0) { + return -1; // 不匹配,已清理JSON } } @@ -1272,12 +1332,8 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0'; // 检查对话ID是否匹配当前活跃对话 - if (strcmp(g_audio_chat_ctx.chat_id, data_msg->chat_id) == 0) { - set_chat_id_matched(); - } else { - LN_WS_WARN("RandomStoryResponse from non-active chat %s, ignoring\n", data_msg->chat_id); - cJSON_Delete(root); - return -1; // 返回错误,不处理此消息 + if (ln_ws_check_chat_id_match(data_msg->chat_id, root) != 0) { + return -1; // 不匹配,已清理JSON } } @@ -1366,6 +1422,9 @@ static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_comm case WS_CMD_AUDIO_PLAYBACK_END: LN_WS_DEBUG("Default: Processing Audio Playback End event\n"); break; + case WS_CMD_AUDIO_RECORDING_START: + LN_WS_DEBUG("Default: Processing Audio Recording Start event\n"); + break; case WS_CMD_PONG: if (data_msg) { LN_WS_DEBUG("Default: Processing Pong message\n"); @@ -1882,4 +1941,384 @@ static void ln_ws_process_incremental_base64_data(const char *new_data, size_t n // WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] download process finished,ret=%d", ret); // return ret; -// } \ No newline at end of file +// } + +/** + * WebSocket重连函数 + * @return 0-重连成功,-1-重连失败 + */ +static int ln_ws_reconnect(void) +{ + LN_WS_INFO("Starting WebSocket reconnection\n"); + + // 设置重连执行中状态 + g_ws_connected = WS_STATE_RECONNECTING; + + // 使用强制重连API + int ret = uapi_ws_force_reconnect(); + if (ret != 0) { + LN_WS_ERROR("WebSocket force reconnection failed: %d\n", ret); + g_ws_connected = WS_STATE_DISCONNECTED; + return -1; + } + + LN_WS_INFO("WebSocket force reconnection successful\n"); + return 0; +} + +/** + * 检查二进制数据接收间隔,在1秒内有数据时触发重连 + * @return true-需要重连,false-不需要重连 + */ +static bool ln_ws_check_binary_data_interrupt(void) +{ +#if ENABLE_BINARY_DATA_INTERRUPT_CHECK + uint32_t current_tick = osKernelGetTickCount(); + + // 如果是第一次调用,记录时间但不检查 + if (g_last_binary_data_tick == 0) { + return false; + } + + // 计算时间间隔(1秒 = 1000ms) + uint32_t interval_ticks = current_tick - g_last_binary_data_tick; + uint32_t one_second_ticks = osKernelGetTickFreq(); // 1秒的tick数 + + // 如果间隔小于1秒,需要重连 + if (interval_ticks < one_second_ticks) { + LN_WS_WARN("Binary data received within 1 second interval (%u ticks), need to reconnect\n", interval_ticks); + return true; + } + + return false; +#else + // 功能被禁用,直接返回false + return false; +#endif +} + +/** + * 重连成功回调函数 + */ +static void ln_ws_reconnect_success_callback(void) +{ + LN_WS_INFO("WebSocket reconnection successful, triggering reconnect success event\n"); + + // 触发重连成功事件 + // if (g_ws_message_callback != NULL) { + // g_ws_message_callback(NULL, WS_CMD_CONNECTED); + // } + g_ws_connected = WS_STATE_CONNECTED; + + LN_WS_INFO("Reconnect success event triggered\n"); +} + +/** + * 检查对话ID是否匹配当前活跃对话 + * @param chat_id 要检查的对话ID + * @param root JSON根对象(用于错误时清理) + * @return 0-匹配成功,-1-不匹配(已清理JSON并记录错误) + */ +static int ln_ws_check_chat_id_match(const char *chat_id, cJSON *root) +{ + if (!chat_id) { + LN_WS_ERROR("Invalid chat_id parameter\n"); + return -1; + } + + // 检查对话ID是否匹配当前活跃对话 + if (strcmp(g_audio_chat_ctx.chat_id, chat_id) == 0) { + set_chat_id_matched(); + return 0; // 匹配成功 + } else { + LN_WS_ERROR("Message from non-active chat %s, triggering reconnection\n", chat_id); + cJSON_Delete(root); + + return -1; // 不匹配,已清理JSON + } +} + +/* ============================ 音频缓存管理函数 ============================ */ + +/** + * 初始化音频缓存 + * @return 0成功,-1失败 + */ +static int ln_ws_audio_buffer_init(void) +{ + if (g_audio_buffer != NULL) { + return 0; // 已经初始化 + } + + g_audio_buffer_capacity = 50; // 初始容量50个音频包 + g_audio_buffer = (audio_buffer_item_t*)malloc(sizeof(audio_buffer_item_t) * g_audio_buffer_capacity); + + if (!g_audio_buffer) { + LN_WS_ERROR("Failed to allocate audio buffer memory\n"); + g_audio_buffer_capacity = 0; + return -1; + } + + g_audio_buffer_count = 0; + g_audio_buffering_enabled = true; + LN_WS_DEBUG("Audio buffer initialized with capacity: %u\n", g_audio_buffer_capacity); + + return 0; +} + +/** + * 清理音频缓存 + */ +static void ln_ws_audio_buffer_cleanup(void) +{ + if (g_audio_buffer) { + // 释放每个缓存项的数据 + for (uint32_t i = 0; i < g_audio_buffer_count; i++) { + if (g_audio_buffer[i].data) { + free(g_audio_buffer[i].data); + } + } + + free(g_audio_buffer); + g_audio_buffer = NULL; + g_audio_buffer_count = 0; + g_audio_buffer_capacity = 0; + g_audio_buffering_enabled = false; + + LN_WS_DEBUG("Audio buffer cleaned up\n"); + } +} + +/** + * 添加音频数据到缓存 + * @param audio_data 音频数据 + * @param audio_size 数据大小 + * @param finished 是否结束 + * @return 0成功,-1失败 + */ +static int ln_ws_audio_buffer_add(const uint8_t *audio_data, uint32_t audio_size, bool finished) +{ + if (!g_audio_buffering_enabled) { + LN_WS_WARN("Audio buffering not enabled\n"); + return -1; + } + + // 确保缓存已初始化 + if (g_audio_buffer == NULL) { + if (ln_ws_audio_buffer_init() != 0) { + return -1; + } + } + + // 检查是否需要扩容 + if (g_audio_buffer_count >= g_audio_buffer_capacity) { + uint32_t new_capacity = g_audio_buffer_capacity * 2; + audio_buffer_item_t *new_buffer = (audio_buffer_item_t*)realloc(g_audio_buffer, + sizeof(audio_buffer_item_t) * new_capacity); + if (!new_buffer) { + LN_WS_ERROR("Failed to expand audio buffer\n"); + return -1; + } + + g_audio_buffer = new_buffer; + g_audio_buffer_capacity = new_capacity; + LN_WS_DEBUG("Audio buffer expanded to capacity: %u\n", g_audio_buffer_capacity); + } + + // 添加新的缓存项 + audio_buffer_item_t *item = &g_audio_buffer[g_audio_buffer_count]; + + if (audio_size > 0) { + item->data = (uint8_t*)malloc(audio_size); + if (!item->data) { + LN_WS_ERROR("Failed to allocate memory for audio buffer item\n"); + return -1; + } + memcpy(item->data, audio_data, audio_size); + } else { + item->data = NULL; + } + + item->size = audio_size; + item->finished = finished; + item->timestamp = osKernelGetTickCount(); + + g_audio_buffer_count++; + + LN_WS_DEBUG("Audio data buffered: size=%u, finished=%s, total=%u\n", + audio_size, finished ? "true" : "false", g_audio_buffer_count); + + return 0; +} + +/** + * 刷新缓存的音频数据 + */ +static void ln_ws_audio_buffer_flush(void) +{ + if (!g_audio_buffer || g_audio_buffer_count == 0) { + return; + } + + LN_WS_INFO("Flushing %u buffered audio data items\n", g_audio_buffer_count); + + // 逐个发送缓存的数据 + for (uint32_t i = 0; i < g_audio_buffer_count; i++) { + audio_buffer_item_t *item = &g_audio_buffer[i]; + + // 直接调用发送函数,跳过状态检查(因为已经连接成功) + int ret = ln_ws_send_audio_data_direct(item->data, item->size, item->finished); + if (ret != 0) { + LN_WS_ERROR("Failed to send buffered audio data item %u\n", i); + } + + // 释放数据内存 + if (item->data) { + free(item->data); + } + } + + // 重置缓存 + g_audio_buffer_count = 0; + LN_WS_INFO("Audio buffer flush completed\n"); +} + +/** + * 直接发送音频数据(跳过状态检查) + * @param audio_data 音频数据 + * @param audio_size 数据大小 + * @param finished 是否结束 + * @return 0成功,-1失败 + */ +static int ln_ws_send_audio_data_direct(const uint8_t *audio_data, uint32_t audio_size, bool finished) +{ + // 对于正常音频数据,必须有有效的数据 + if (!finished && (!audio_data || audio_size == 0)) { + LN_WS_ERROR("Invalid audio data parameters\n"); + return -1; + } + + // 如果聊天未开始,生成新的聊天ID + if (!g_audio_chat_ctx.is_active) { + generate_chat_id(g_audio_chat_ctx.chat_id, sizeof(g_audio_chat_ctx.chat_id)); + g_audio_chat_ctx.message_counter = 0; + g_audio_chat_ctx.is_active = true; + // 重置对话ID匹配标志 + reset_chat_id_match(); + LN_WS_DEBUG("New chat started: %s\n", g_audio_chat_ctx.chat_id); + } + + // 增加消息计数器 + g_audio_chat_ctx.message_counter++; + + // 如果是第一条消息,触发录音开始事件 + if (g_audio_chat_ctx.message_counter == 1) { + LN_WS_INFO("First audio message, triggering recording start event\n"); + + // 触发录音开始事件 + if (g_ws_message_callback != NULL) { + g_ws_message_callback(NULL, WS_CMD_AUDIO_RECORDING_START); + } + } + + // 生成消息ID + char message_id[128]; + generate_message_id(message_id, sizeof(message_id), g_audio_chat_ctx.chat_id, g_audio_chat_ctx.message_counter); + + // Base64编码音频数据(如果是结束消息,则使用空字符串) + char *audio_base64 = NULL; + if (audio_size == 0) { + // 结束消息,使用空字符串 + audio_base64 = (char*)malloc(1); + if (!audio_base64) { + LN_WS_ERROR("Failed to allocate memory for base64 encoding\n"); + return -1; + } + audio_base64[0] = '\0'; + } else { + // 正常音频数据,进行Base64编码 + size_t base64_size = audio_size * 2; //((audio_size + 2) / 3) * 4 + 1; + audio_base64 = (char*)malloc(base64_size); + if (!audio_base64) { + LN_WS_ERROR("Failed to allocate memory for base64 encoding\n"); + return -1; + } + + size_t output_len = 0; + int ret_encode = mbedtls_base64_encode((unsigned char*)audio_base64, base64_size - 1, &output_len, + audio_data, audio_size); + if (ret_encode != 0) { + LN_WS_DEBUG("Base64 encoding failed: %d\n", ret_encode); + free(audio_base64); + return -1; + } + audio_base64[output_len] = '\0'; + } + + // 获取当前时间戳 + uint32_t timestamp; + uapi_get_timestampe_ss(×tamp); + + // 创建JSON消息 + cJSON *json = cJSON_CreateObject(); + if (!json) { + LN_WS_ERROR("Failed to create JSON object\n"); + free(audio_base64); + return -1; + } + + // 添加基本字段 + cJSON_AddStringToObject(json, "deviceId", uapi_ln_get_device_id()); + cJSON_AddNumberToObject(json, "timestamp", (double)timestamp); + cJSON_AddStringToObject(json, "messageId", message_id); + + // 根据命令类型设置不同的command + const char *command_str = (g_audio_chat_command_type == LN_AUDIO_CHAT_VOICE_CHAT_REQUEST) ? + "VoiceChatRequest" : "ChooseStoryRequest"; + cJSON_AddStringToObject(json, "command", command_str); + + // 创建data对象 + cJSON *data_obj = cJSON_CreateObject(); + if (!data_obj) { + LN_WS_ERROR("Failed to create data object\n"); + cJSON_Delete(json); + free(audio_base64); + return -1; + } + + cJSON_AddStringToObject(data_obj, "audioBase64", audio_base64); + cJSON_AddStringToObject(data_obj, "chatId", g_audio_chat_ctx.chat_id); + cJSON_AddBoolToObject(data_obj, "finished", finished); + + cJSON_AddItemToObject(json, "data", data_obj); + + // 转换为字符串 + char *json_string = cJSON_Print(json); + if (!json_string) { + LN_WS_ERROR("Failed to print JSON\n"); + cJSON_Delete(json); + free(audio_base64); + return -1; + } + + // 发送JSON字符串 + int ret = uapi_ws_send_text(json_string); + + LN_WS_ERROR("Sent audio data: chatId=%s, messageId=%s, size=%u, finished=%s\n", + g_audio_chat_ctx.chat_id, message_id, audio_size, finished ? "true" : "false"); + + // 清理内存 + cJSON_free(json_string); + free(audio_base64); + cJSON_Delete(json); + + // 如果音频结束,重置聊天上下文 + if (finished) { + g_audio_chat_ctx.is_active = false; + LN_WS_DEBUG("Chat ended: %s\n", g_audio_chat_ctx.chat_id); + // 重置对话ID匹配标志 + reset_chat_id_match(); + } + + return ret; +} \ No newline at end of file diff --git a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c index 2a6b0f4f5..178178a24 100644 --- a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c +++ b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c @@ -60,6 +60,7 @@ static const char *g_log_level_str[] = { #define WS_STATE_CONNECTING 1 // 连接中 #define WS_STATE_CONNECTED 2 // 连接成功 #define WS_STATE_SLEEP 3 // 休眠状态 +#define WS_STATE_RECONNECTING 4 // 重连执行中 /* WebSocket关闭状态枚举 */ typedef enum { @@ -89,6 +90,7 @@ static volatile unsigned int g_ws_reconnect_count = 0; /* 断线后递增,用 static osMessageQueueId_t g_ws_msg_queue = NULL; static ws_call_backs_t g_ws_callbacks = {0}; /* WebSocket回调函数结构体 */ static volatile ws_close_status_t g_ws_close_status = WS_CLOSE_NOT_EXECUTED; /* WebSocket关闭状态标志位 */ +static volatile int g_ws_is_reconnecting = 0; /* 是否正在重连的标志位 */ /* 连接超时检测相关变量 */ static time_t g_ws_connect_start_time = 0; /* 连接开始时间 */ @@ -158,7 +160,21 @@ static void uapi_ws_on_open(int client) g_ws_connected = WS_STATE_CONNECTED; uapi_ws_reset_close_status(); // 连接成功时重置关闭状态 uapi_ws_reset_connect_timer(); // 连接成功时重置超时计时器 - HAISI_WS_DEBUG("连接状态: 连接成功, 关闭状态已重置为 NOT_EXECUTED (%d)\n", g_ws_close_status); + + HAISI_WS_INFO("连接状态: 连接成功, 关闭状态已重置为 NOT_EXECUTED (%d)\n", g_ws_close_status); + + // 检查是否是从重连状态恢复的连接 + if (g_ws_is_reconnecting) { + HAISI_WS_INFO("Reconnection successful, triggering reconnect success callback\n"); + g_ws_is_reconnecting = 0; // 重置重连标志 + + // 触发重连成功回调 + if (g_ws_callbacks.reconnect_success_callback != NULL) { + g_ws_callbacks.reconnect_success_callback(); + } else { + HAISI_WS_DEBUG("No reconnect success callback registered\n"); + } + } } /* @@ -284,68 +300,71 @@ static void uapi_ws_main_thread(void *arg) while (g_ws_running) { // 处理队列消息(非阻塞) - if (osMessageQueueGet(g_ws_msg_queue, &msg, NULL, 1000) == osOK) { - HAISI_WS_DEBUG("osMessageQueueGet: g_ws_msg_queue\n"); - switch (msg->msg_type) { - case WS_MSG_TYPE_SEND_TEXT: - if (g_ws_connected == WS_STATE_CONNECTED && g_ws_client_fd >= 0) { - // 对于文本消息,我们需要特殊处理,因为现在data是ws_data_msg_t类型 - // 这里需要重新设计文本消息的存储方式 - HAISI_WS_WARN("Text message type not supported in new structure\n"); - } else { - HAISI_WS_WARN("Cannot send text, connection state: %d\n", g_ws_connected); - } - ws_msg_free(msg); - break; - - case WS_MSG_TYPE_FORCE_RECONNECT: - HAISI_WS_INFO("Force reconnect requested\n"); - if (g_ws_client_fd >= 0) { - HAISI_WS_DEBUG("WebSocketClientClose\n"); - uapi_ws_close_with_status(g_ws_client_fd); - g_ws_client_fd=-1; - HAISI_WS_DEBUG("WebSocketClientClose end\n"); - } - g_ws_connected = WS_STATE_DISCONNECTED; - HAISI_WS_INFO("连接状态: 未连接(强制重连)\n"); - ws_msg_free(msg); - break; - - case WS_MSG_TYPE_STOP: - HAISI_WS_INFO("Stop requested from queue\n"); - g_ws_running = 0; - ws_msg_free(msg); - break; - - case WS_MSG_TYPE_RAW_MESSAGE: - { - HAISI_WS_DEBUG("Received raw message: %s, timestamp: %u, isBinary: %s\n", - msg->raw_data, msg->timestamp, msg->isBinary ? "true" : "false"); + if (g_ws_connected == WS_STATE_CONNECTED) + { + if (osMessageQueueGet(g_ws_msg_queue, &msg, NULL, 0) == osOK) { + HAISI_WS_DEBUG("osMessageQueueGet: g_ws_msg_queue\n"); + switch (msg->msg_type) { + case WS_MSG_TYPE_SEND_TEXT: + if (g_ws_connected == WS_STATE_CONNECTED && g_ws_client_fd >= 0) { + // 对于文本消息,我们需要特殊处理,因为现在data是ws_data_msg_t类型 + // 这里需要重新设计文本消息的存储方式 + HAISI_WS_WARN("Text message type not supported in new structure\n"); + } else { + HAISI_WS_WARN("Cannot send text, connection state: %d\n", g_ws_connected); + } + ws_msg_free(msg); + break; - // 根据消息类型调用不同的回调函数 - if (!msg->isBinary) { - - // 文本消息 - if (g_ws_callbacks.message_callback != NULL) { - g_ws_callbacks.message_callback(msg->raw_data, msg->timestamp); - } else { - HAISI_WS_WARN("No text message callback registered\n"); + case WS_MSG_TYPE_FORCE_RECONNECT: + HAISI_WS_INFO("Force reconnect requested\n"); + if (g_ws_client_fd >= 0) { + HAISI_WS_DEBUG("WebSocketClientClose\n"); + uapi_ws_close_with_status(g_ws_client_fd); + g_ws_client_fd=-1; + HAISI_WS_DEBUG("WebSocketClientClose end\n"); + } + g_ws_connected = WS_STATE_DISCONNECTED; + HAISI_WS_INFO("连接状态: 未连接(强制重连)\n"); + ws_msg_free(msg); + break; + + case WS_MSG_TYPE_STOP: + HAISI_WS_INFO("Stop requested from queue\n"); + g_ws_running = 0; + ws_msg_free(msg); + break; + + case WS_MSG_TYPE_RAW_MESSAGE: + { + HAISI_WS_DEBUG("Received raw message: %s, timestamp: %u, isBinary: %s\n", + msg->raw_data, msg->timestamp, msg->isBinary ? "true" : "false"); + + // 根据消息类型调用不同的回调函数 + if (!msg->isBinary) { + + // 文本消息 + if (g_ws_callbacks.message_callback != NULL) { + g_ws_callbacks.message_callback(msg->raw_data, msg->timestamp); + } else { + HAISI_WS_WARN("No text message callback registered\n"); + } } + // 处理完消息后释放内存 + ws_msg_free(msg); } - // 处理完消息后释放内存 + break; + + default: + HAISI_WS_WARN("Unknown message type: %d\n", msg->msg_type); ws_msg_free(msg); - } - break; - - default: - HAISI_WS_WARN("Unknown message type: %d\n", msg->msg_type); - ws_msg_free(msg); - break; + break; + } + } + else + { + // HAISI_WS_DEBUG("osMessageQueueGet timeout or failed\r\n"); } - } - else - { - // HAISI_WS_DEBUG("osMessageQueueGet timeout or failed\r\n"); } // 检测连接状态变化并触发事件回调 @@ -368,6 +387,20 @@ static void uapi_ws_main_thread(void *arg) continue; } + if (g_ws_connected == WS_STATE_RECONNECTING) { + // 重连执行中状态:等待断开完成,然后开始重新连接 + HAISI_WS_DEBUG("Reconnecting mode: waiting for disconnect completion\n"); + if (g_ws_client_fd < 0) { + // 断开完成,开始重新连接 + HAISI_WS_INFO("Disconnect completed, starting reconnection\n"); + g_ws_connected = WS_STATE_DISCONNECTED; + } else { + // 等待断开完成 + usleep(100 * 1000); // 等待100ms + } + continue; + } + if (g_ws_connected == WS_STATE_DISCONNECTED) { if (g_ws_client_fd >= 0) { { @@ -413,7 +446,7 @@ static void uapi_ws_main_thread(void *arg) g_ws_callbacks.url_callback(url, sizeof(url), header_buf, sizeof(header_buf)); } - HAISI_WS_DEBUG("尝试连接WebSocket: %s\n", url); + HAISI_WS_INFO("尝试连接WebSocket: %s\n", url); HAISI_WS_DEBUG("使用Header: %s\n", header_buf); int ret=0; if(strlen(header_buf)==0) @@ -491,7 +524,7 @@ static void uapi_ws_main_thread(void *arg) } /* 1秒间隔 */ - if(g_ws_connected!=WS_STATE_CONNECTED) + // if(g_ws_connected!=WS_STATE_CONNECTED) { usleep(1000 * 1000); @@ -653,22 +686,19 @@ int uapi_ws_send_text_async(const char *text) int uapi_ws_force_reconnect(void) { if (!g_ws_running) return -1; + + HAISI_WS_INFO("Starting WebSocket force reconnection\n"); - ws_msg_info_t *msg = ws_msg_create(0); - if (msg == NULL) { - HAISI_WS_ERROR("Failed to create force reconnect message\n"); - return -1; + // 设置重连执行中状态和重连标志 + g_ws_connected = WS_STATE_RECONNECTING; + g_ws_is_reconnecting = 1; // 标记正在重连 + + if (g_ws_client_fd >= 0) { + HAISI_WS_DEBUG("Disconnecting uapi_ws_force_reconnect\n"); + uapi_ws_close_with_status(g_ws_client_fd); } - msg->msg_type = WS_MSG_TYPE_FORCE_RECONNECT; - msg->timestamp = 0; // 控制消息不需要时间戳 - msg->isBinary = false; // 控制消息不是二进制数据 - - osStatus_t ret = osMessageQueuePut(g_ws_msg_queue, &msg, 0, 0); - if (ret != osOK) { - ws_msg_free(msg); - return -1; - } + HAISI_WS_INFO("WebSocket force reconnection initiated\n"); return 0; } @@ -738,6 +768,8 @@ const char* uapi_ws_get_connection_state_string(void) return "连接成功"; case WS_STATE_SLEEP: return "休眠状态"; + case WS_STATE_RECONNECTING: + return "重连执行中"; default: return "未知状态"; } @@ -991,6 +1023,20 @@ static int uapi_ws_is_connect_timeout(void) #endif } +/** + * @brief 注册重连成功回调函数(公共接口) + * @param callback 回调函数 + */ +void uapi_ws_register_reconnect_success_callback(ws_reconnect_success_callback_t callback) +{ + g_ws_callbacks.reconnect_success_callback = callback; + if (callback != NULL) { + HAISI_WS_DEBUG("Reconnect success callback registered successfully\n"); + } else { + HAISI_WS_DEBUG("Reconnect success callback unregistered\n"); + } +} + /** * @brief 重置连接超时计时器 */ diff --git a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h index 8c077a280..51beba46a 100644 --- a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h +++ b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h @@ -45,6 +45,9 @@ typedef enum { /* 连接事件回调函数类型 */ typedef void (*ws_event_callback_t)(ws_event_type_t event_type); +/* 重连成功回调函数类型 */ +typedef void (*ws_reconnect_success_callback_t)(void); + /* WebSocket回调函数结构体 */ typedef struct { ws_message_callback_t message_callback; /* 文本消息处理回调函数 */ @@ -53,6 +56,7 @@ typedef struct { ws_url_callback_t url_callback; /* URL构建回调函数 */ ws_heartbeat_callback_t heartbeat_callback; /* 心跳处理回调函数 */ ws_event_callback_t event_callback; /* 连接事件回调函数 */ + ws_reconnect_success_callback_t reconnect_success_callback; /* 重连成功回调函数 */ } ws_call_backs_t; @@ -79,6 +83,7 @@ void uapi_ws_register_incomplete_callback(ws_incomplete_message_callback_t callb void uapi_ws_register_url_callback(ws_url_callback_t callback); void uapi_ws_register_heartbeat_callback(ws_heartbeat_callback_t callback); void uapi_ws_register_event_callback(ws_event_callback_t callback); +void uapi_ws_register_reconnect_success_callback(ws_reconnect_success_callback_t callback); #ifdef __cplusplus }