优化ln打断效果,增快反应速度
authorhcj <1224667075@qq.com>
Fri, 10 Oct 2025 09:41:49 +0000 (17:41 +0800)
committerhcj <1224667075@qq.com>
Fri, 10 Oct 2025 09:41:49 +0000 (17:41 +0800)
code/sdk/zhihui/ai_new/ai_V2.5/ai_v2_5_main.cpp
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/websocket/os_ws/haisi_websocket.c
code/sdk/zhihui/websocket/os_ws/haisi_websocket.h

index 323906e..a462d56 100644 (file)
@@ -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);
index bcc2c7c..b914290 100644 (file)
@@ -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;
 
index 5e6dc3c..1ba6c62 100644 (file)
@@ -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(&timestamp);
+    
+    // 创建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
index 2a6b0f4..178178a 100644 (file)
@@ -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 重置连接超时计时器
  */
index 8c077a2..51beba4 100644 (file)
@@ -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
 }