ai中间版本
author孙春阳 <snickersun@163.com>
Thu, 4 Sep 2025 13:07:44 +0000 (21:07 +0800)
committer孙春阳 <snickersun@163.com>
Thu, 4 Sep 2025 13:07:44 +0000 (21:07 +0800)
19 files changed:
code/sdk/zhihui/ai/CMakeLists.txt
code/sdk/zhihui/ai/Makefile
code/sdk/zhihui/ai/example/ai_at_cmd.c
code/sdk/zhihui/ai/include/ai_asr_service.h
code/sdk/zhihui/ai/include/ai_chat_service.h
code/sdk/zhihui/ai/include/ai_connection_manager.h
code/sdk/zhihui/ai/include/ai_core_types.h
code/sdk/zhihui/ai/include/ai_services.h
code/sdk/zhihui/ai/include/ai_tts_service.h
code/sdk/zhihui/ai/src/ai_asr_service.c
code/sdk/zhihui/ai/src/ai_chat_service.c
code/sdk/zhihui/ai/src/ai_connection_manager.c
code/sdk/zhihui/ai/src/ai_event_manager.c [new file with mode: 0644]
code/sdk/zhihui/ai/src/ai_services.c
code/sdk/zhihui/ai/src/ai_tts_service.c
code/sdk/zhihui/ai/src/dev_authentication.c
code/sdk/zhihui/websocket/src/websocket_parser.c
code/sdk/zhihui/websocket/src/zhihui_websocket.c
code/sdk/zhihui/websocket/test/ws_multi_test.c

index 693d10c..4cd9974 100644 (file)
@@ -24,6 +24,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/src/ai_services.c
 ${CMAKE_CURRENT_SOURCE_DIR}/src/ai_asr_service.c
 ${CMAKE_CURRENT_SOURCE_DIR}/src/ai_chat_service.c
 ${CMAKE_CURRENT_SOURCE_DIR}/src/ai_tts_service.c
+${CMAKE_CURRENT_SOURCE_DIR}/src/ai_event_manager.c
+${CMAKE_CURRENT_SOURCE_DIR}/src/dev_authentication.c
 ${CMAKE_CURRENT_SOURCE_DIR}/example/ai_at_cmd.c
 # ${CMAKE_CURRENT_SOURCE_DIR}/example/ai_demo.c
 )
index b08c024..8312c43 100644 (file)
@@ -19,7 +19,8 @@ SOURCES = $(SRC_DIR)/ai_connection_manager.c \
           $(SRC_DIR)/ai_services.c \
           $(SRC_DIR)/ai_asr_service.c \
           $(SRC_DIR)/ai_chat_service.c \
-          $(SRC_DIR)/ai_tts_service.c
+          $(SRC_DIR)/ai_tts_service.c \
+          $(SRC_DIR)/ai_event_manager.c
 
 # 示例文件
 EXAMPLE_SOURCES = $(EXAMPLE_DIR)/ai_demo.c \
index b3fd990..bab8638 100644 (file)
@@ -53,9 +53,9 @@ extern "C" {
 #define AI_AT_DEMO_CHAT_URL       "ws://amp.raisound.com/chat/ws"
 #define AI_AT_DEMO_TTS_URL        "ws://amp.raisound.com/tts/ws"
 #else
-#define AI_AT_DEMO_ASR_URL        "ws://192.168.1.100:8080/asr/ws"
-#define AI_AT_DEMO_CHAT_URL       "ws://192.168.1.100:8080/chat/ws"
-#define AI_AT_DEMO_TTS_URL        "ws://192.168.1.100:8080/tts/ws"
+#define AI_AT_DEMO_ASR_URL        "ws://118.145.158.207:22080/asr/ws"
+#define AI_AT_DEMO_CHAT_URL       "ws://118.145.158.207:22080/chat/ws"
+#define AI_AT_DEMO_TTS_URL        "ws://118.145.158.207:22080/tts/ws"
 #endif
 #define AI_AT_DEMO_AUDIO_SIZE     1024
 #define AI_AT_DEMO_CHAT_MESSAGE   "你好,我是AI助手AT命令测试程序"
@@ -208,6 +208,11 @@ static uint32_t at_ai_init(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
     strncpy(conn_config.connection_config.chat_url, AI_AT_DEMO_CHAT_URL, AI_MAX_URL_LENGTH - 1);
     strncpy(conn_config.connection_config.tts_url, AI_AT_DEMO_TTS_URL, AI_MAX_URL_LENGTH - 1);
     
+    // 🆕 关键修复:设置事件回调
+    extern void connection_event_handler(const ai_event_t *event, void *user_data);
+    conn_config.event_callback = connection_event_handler;
+    conn_config.user_data = &g_ai_services_context;
+    
     ret = ai_connection_manager_init(&conn_config);
     if (ret != AI_OK) {
         printf("[AI_AT] 连接管理器初始化失败: %d\n", ret);
@@ -513,8 +518,42 @@ static uint32_t at_ai_tts_test(uint8_t *para, uint32_t paraLen, char *strCommonC
     ai_connection_state_t state = ai_connection_manager_get_connection_state(AI_SERVICE_TTS);
     if (state != AI_CONN_STATE_CONNECTED) {
         printf("[AI_AT] TTS服务未连接,状态: %d\n", state);
-        g_test_stats.tests_failed++;
-        return ERRCODE_FAIL;
+        
+        // 如果是错误状态,尝试重连
+        if (state == AI_CONN_STATE_ERROR) {
+            printf("[AI_AT] 检测到TTS连接错误,尝试重连...\n");
+            ai_error_t reconnect_ret = ai_connection_manager_reconnect(AI_SERVICE_TTS);
+            if (reconnect_ret == AI_OK) {
+                printf("[AI_AT] TTS重连请求已发送,等待连接建立...\n");
+                
+                // 等待连接建立
+                for (int i = 0; i < 10; i++) {  // 最多等待5秒
+#ifndef __TEST_MODE__
+                    osDelay(500);  // 等待500ms
+#endif
+                    state = ai_connection_manager_get_connection_state(AI_SERVICE_TTS);
+                    if (state == AI_CONN_STATE_CONNECTED) {
+                        printf("[AI_AT] TTS重连成功!\n");
+                        break;
+                    }
+                }
+                
+                // 再次检查连接状态
+                if (state != AI_CONN_STATE_CONNECTED) {
+                    printf("[AI_AT] TTS重连失败,当前状态: %d\n", state);
+                    g_test_stats.tests_failed++;
+                    return ERRCODE_FAIL;
+                }
+            } else {
+                printf("[AI_AT] TTS重连请求失败: %d\n", reconnect_ret);
+                g_test_stats.tests_failed++;
+                return ERRCODE_FAIL;
+            }
+        } else {
+            printf("[AI_AT] TTS服务状态异常,无法执行测试\n");
+            g_test_stats.tests_failed++;
+            return ERRCODE_FAIL;
+        }
     }
     
     char* session_id = generate_session_id();
@@ -753,6 +792,84 @@ static uint32_t at_ai_cleanup(uint8_t *para, uint32_t paraLen, char *strCommonCm
     return ERRCODE_SUCC;
 }
 
+/**
+ * @brief AT^AIRECONNECT - 重连指定AI服务
+ * 格式: AT^AIRECONNECT=service_type
+ * 示例: AT^AIRECONNECT=2 (重连TTS服务)
+ *       AT^AIRECONNECT=0 (重连ASR服务)
+ *       AT^AIRECONNECT=1 (重连Chat服务)
+ */
+static uint32_t at_ai_reconnect(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    UNUSED(strCommonCmd);
+    
+    printf("[AI_AT] 执行 AT^AIRECONNECT - 重连AI服务\n");
+    
+    if (!g_ai_at_initialized) {
+        printf("[AI_AT] AI服务未初始化\n");
+        return ERRCODE_FAIL;
+    }
+    
+    ai_service_type_t service_type = AI_SERVICE_TTS;  // 默认重连TTS
+    
+    // 解析参数
+    if (para != NULL && paraLen > 0) {
+        uint8_t argv[AI_AT_MAX_ARGC][AI_AT_MAX_ARGV_LEN] = { 0 };
+        uint32_t argc = 0;
+        
+        int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+        if (ret == AI_AT_SUCCESS && argc > 0) {
+            int service_id = atoi((char*)argv[0]);
+            if (service_id >= 0 && service_id < AI_SERVICE_COUNT) {
+                service_type = (ai_service_type_t)service_id;
+            } else {
+                printf("[AI_AT] 无效的服务类型: %d (有效范围: 0-2)\n", service_id);
+                return ERRCODE_FAIL;
+            }
+        }
+    }
+    
+    const char* service_names[] = {"ASR", "Chat", "TTS"};
+    printf("[AI_AT] 重连服务: %s (类型: %d)\n", service_names[service_type], service_type);
+    
+    // 获取当前连接状态
+    ai_connection_state_t current_state = ai_connection_manager_get_connection_state(service_type);
+    printf("[AI_AT] 当前连接状态: %d\n", current_state);
+    
+    // 执行重连
+    ai_error_t ret = ai_connection_manager_reconnect(service_type);
+    if (ret != AI_OK) {
+        printf("[AI_AT] 重连请求失败: %d\n", ret);
+        return ERRCODE_FAIL;
+    }
+    
+    printf("[AI_AT] 重连请求已发送,等待连接建立...\n");
+    
+    // 等待连接建立
+    bool connected = false;
+    for (int i = 0; i < 20; i++) {  // 最多等待10秒
+#ifndef __TEST_MODE__
+        osDelay(500);  // 等待500ms
+#endif
+        ai_connection_state_t state = ai_connection_manager_get_connection_state(service_type);
+        printf("[AI_AT] 等待中... 状态: %d\n", state);
+        
+        if (state == AI_CONN_STATE_CONNECTED) {
+            connected = true;
+            break;
+        }
+    }
+    
+    if (connected) {
+        printf("[AI_AT] %s服务重连成功!\n", service_names[service_type]);
+        return ERRCODE_SUCC;
+    } else {
+        ai_connection_state_t final_state = ai_connection_manager_get_connection_state(service_type);
+        printf("[AI_AT] %s服务重连失败,最终状态: %d\n", service_names[service_type], final_state);
+        return ERRCODE_FAIL;
+    }
+}
+
 /* ================================ AT命令表定义 ================================ */
 
 static const at_cmd_table_t g_ai_cmd[] = {
@@ -767,6 +884,7 @@ static const at_cmd_table_t g_ai_cmd[] = {
     { (char *)"AT^AIALLTEST",    at_ai_all_test, (char *)"Run comprehensive test" },
     { (char *)"AT^AISTATS",      at_ai_stats, (char *)"Show test statistics" },
     { (char *)"AT^AICLEANUP",    at_ai_cleanup, (char *)"Cleanup AI services" },
+    { (char *)"AT^AIRECONNECT",  at_ai_reconnect, (char *)"service_type (0=ASR,1=Chat,2=TTS)" },
 };
 
 /* ================================ 公共接口 ================================ */
@@ -824,9 +942,9 @@ int main(void)
     
     printf("\n6. 单独测试各个服务:\n");
     
-    printf("\n6.1 测试 AT^AIASR=3000 - ASR语音识别\n");
-    uint8_t asr_para[] = "3000";
-    result = at_ai_asr_test(asr_para, sizeof(asr_para) - 1, NULL);
+    printf("\n6.1 测试 AT^AIASR=5000 - 详细ASR测试\n");
+    uint8_t asr_para1[] = "5000";
+    result = at_ai_asr_test(asr_para1, sizeof(asr_para1) - 1, NULL);
     printf("   结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
     
     printf("\n6.2 测试 AT^AICHAT - Chat对话服务\n");
index 29bc749..3eea651 100644 (file)
@@ -32,6 +32,7 @@ typedef enum {
  */
 typedef struct {
     char language[16];              // 语言代码,如"zh-CN"
+    char format[16];                // 音频格式,如"pcm"
     bool enable_vad;                // 是否启用语音活动检测
     bool enable_punctuation;        // 是否启用标点符号
     bool enable_partial_results;    // 是否启用部分结果
@@ -69,6 +70,16 @@ typedef struct {
 
 /* ================================ ASR服务接口 ================================ */
 
+/**
+ * @brief 初始化ASR WebSocket URL
+ * @param url_buf 输出缓冲区,存储构建的URL
+ * @param server_url 服务器URL
+ * @return bool true表示成功,false表示失败
+ * @note 从设备令牌构建完整的ASR WebSocket URL
+ *       格式:ws://amp.raisound.com/asr/ws?dt={token}&platform=aliyun
+ */
+bool asr_api_ws_url_init(char *url_buf,const char *server_url);
+
 /**
  * @brief 初始化ASR服务
  * @param context ASR服务上下文
index bed5fa3..3bd6f67 100644 (file)
@@ -70,6 +70,16 @@ typedef struct {
 
 /* ================================ Chat服务接口 ================================ */
 
+/**
+ * @brief 初始化Chat WebSocket URL
+ * @param url_buf 输出缓冲区,存储构建的URL
+ * @param server_url 服务器URL
+ * @param type_ai 服务类型
+ * @return bool true表示成功,false表示失败
+ * @note 从设备令牌构建完整的Chat WebSocket URL
+ */
+bool chat_api_ws_url_init(char * url_buf,const char *server_url,uint8_t type_ai);
+
 /**
  * @brief 初始化Chat服务
  * @param context Chat服务上下文
index cd5112c..778c0bd 100644 (file)
@@ -140,6 +140,16 @@ ai_error_t ai_connection_manager_stop_all(void);
 ai_error_t ai_connection_manager_send_data(ai_service_type_t service_type, 
                                           const char *data, size_t length);
 
+/**
+ * @brief 发送原始数据到指定服务
+ * @param service_type 服务类型
+ * @param data 数据指针
+ * @param length 数据长度
+ * @return ai_error_t 错误码
+ */
+ai_error_t ai_connection_manager_send_rawdata(ai_service_type_t service_type, 
+                                              const void *data, size_t length);
+
 /**
  * @brief 获取全局状态
  * @return ai_global_state_t 全局状态
index a950342..49b3d50 100644 (file)
@@ -26,7 +26,32 @@ extern "C" {
 #include "cmsis_os2.h"
 /* WebSocket相关头文件 */
 #ifdef SUPPORT_ZHIHUI_AI_SERVICE
+// 宏定义:USE_ZHIHUI_WEBSOCKET = 1 使用新的zhihui接口,= 0 使用海思原有接口
+#define USE_ZHIHUI_WEBSOCKET 0
+#ifndef USE_ZHIHUI_WEBSOCKET
+#define USE_ZHIHUI_WEBSOCKET 0  // 默认使用原有接口
+#endif
+
+#if USE_ZHIHUI_WEBSOCKET
+    #include "zhihui_websocket.h"
+// 兼容net_websocket.h中的函数定义 #include "net_websocket.h"
+    // 适配宏:将net_websocket.h的API映射到zhihui_websocket.h
+    #define WebSocketClientConstructor         zhihui_WebSocketClientConstructor
+    #define WebSocketClientSetProtocol         zhihui_WebSocketClientSetProtocol
+    #define WebSocketClientConnect             zhihui_WebSocketClientConnect
+    #define WebSocketClientSendText            zhihui_WebSocketClientSendText
+    #define WebSocketClientSendBinStart        zhihui_WebSocketClientSendBinStart
+    #define WebSocketClientSendBinContinue     zhihui_WebSocketClientSendBinContinue
+    #define WebSocketClientSendBinEnd          zhihui_WebSocketClientSendBinEnd
+    #define WebSocketClientClose               zhihui_WebSocketClientClose
+    #define WebSocketClientIsConnected         zhihui_WebSocketClientIsConnected
+    #ifdef WEBSOCKET_SSL_ENABLE
+        #define WebSocketClientSetServerCert   zhihui_WebSocketClientSetServerCert
+    #endif
+#else
 #include "net_websocket.h"
+#endif
+
 /* 定义WebSocket客户端类型为文件描述符 */
 typedef int WebSocketClient_t;
 #else
@@ -50,7 +75,7 @@ struct WebSocketErrorResult {
 /* ================================ 基础常量定义 ================================ */
 
 /* 内存优化常量 - LiteOS-M适配 */
-#define AI_MAX_URL_LENGTH           128     // URL最大长度
+#define AI_MAX_URL_LENGTH           (128+64)     // URL最大长度
 #define AI_MAX_SESSION_ID_LENGTH    32      // 会话ID最大长度
 #define AI_MAX_TEXT_LENGTH          512     // 文本最大长度(优化)
 #define AI_MAX_ERROR_MSG_LENGTH     64      // 错误消息最大长度
@@ -90,7 +115,9 @@ typedef enum {
     AI_ERROR_SERVICE_UNAVAILABLE = -13,     // 服务不可用
     AI_ERROR_BUFFER_OVERFLOW = -14,         // 缓冲区溢出
     AI_ERROR_STATE_INVALID = -15,           // 状态无效
-    AI_ERROR_MUTEX_FAILED = -16             // 互斥锁操作失败
+    AI_ERROR_MUTEX_FAILED = -16,            // 互斥锁操作失败
+    AI_ERROR_MEDIA_INIT = -17,              // 媒体服务初始化失败
+    AI_ERROR_RESOURCE_LIMIT = -18           // 资源限制错误
 } ai_error_t;
 
 /* ================================ 服务类型定义 ================================ */
@@ -99,9 +126,18 @@ typedef enum {
     AI_SERVICE_ASR = 0,                     // 语音识别服务
     AI_SERVICE_CHAT = 1,                    // 对话服务
     AI_SERVICE_TTS = 2,                     // 语音合成服务
-    AI_SERVICE_COUNT = 3                    // 服务总数
+    AI_SERVICE_COUNT = 3,                   // 服务总数
+    AI_SERVICE_GLOBAL = 99                  // 全局服务事件(用于系统级状态变更)
 } ai_service_type_t;
 
+typedef enum {
+    AI_TYPE_JIU = 0,
+    AI_TYPE_DOU = 1,
+    AI_TYPE_WEN = 2,
+    AI_TYPE_KIMI = 3,
+    AI_TYPE_DE = 4,
+} ai_type_t;
+
 /* ================================ 连接状态定义 ================================ */
 
 typedef enum {
@@ -201,16 +237,22 @@ typedef struct {
 typedef enum {
     AI_EVENT_CONNECTION_ESTABLISHED = 0,    // 连接建立
     AI_EVENT_CONNECTION_LOST = 1,           // 连接丢失
-    AI_EVENT_DATA_RECEIVED = 2,             // 数据接收
-    AI_EVENT_ERROR_OCCURRED = 3,            // 错误发生
-    AI_EVENT_STATE_CHANGED = 4,             // 状态变更
-    AI_EVENT_AUDIO_START = 5,               // 音频开始
-    AI_EVENT_AUDIO_END = 6,                 // 音频结束
-    AI_EVENT_RECOGNITION_RESULT = 7,        // 识别结果
-    AI_EVENT_CHAT_RESPONSE = 8,             // 对话响应
-    AI_EVENT_TTS_RESULT = 9                 // 合成结果
+    AI_EVENT_TEXT_DATA_RECEIVED = 2,        // 文本数据接收(JSON等)
+    AI_EVENT_BIN_DATA_RECEIVED = 3,         // 二进制数据接收(音频等)
+    AI_EVENT_ERROR_OCCURRED = 4,            // 错误发生
+    AI_EVENT_STATE_CHANGED = 5,             // 状态变更
+    AI_EVENT_AUDIO_START = 6,               // 音频开始
+    AI_EVENT_AUDIO_END = 7,                 // 音频结束
+    AI_EVENT_RECOGNITION_RESULT = 8,        // 识别结果
+    AI_EVENT_CHAT_RESPONSE = 9,             // 对话响应
+    AI_EVENT_TTS_RESULT = 10                // 合成结果
 } ai_event_type_t;
 
+/* ================================ 事件数据结构 ================================ */
+
+/* 最大事件数据长度 -  控制最大长度限制 */
+#define AI_MAX_EVENT_DATA_SIZE    4096
+
 typedef struct {
     ai_event_type_t type;                   // 事件类型
     ai_service_type_t service;              // 相关服务
@@ -222,8 +264,11 @@ typedef struct {
             int error_code;                 // 错误码
         } connection;
         struct {
-            const char *data;               // 数据指针
+            uint8_t *data;                  //  动态分配的数据指针
             size_t length;                  // 数据长度
+            size_t allocated_size;          //  实际分配的内存大小
+            bool is_finished;               // 是否为完整消息
+            bool is_final_fragment;         // 是否为最后分片
         } data;
         struct {
             ai_global_state_t old_state;   // 旧状态
@@ -236,6 +281,43 @@ typedef struct {
     } payload;
 } ai_event_t;
 
+/* ================================ 事件管理函数 ================================ */
+
+/**
+ * @brief 创建数据事件(动态分配内存)
+ * @param type 事件类型
+ * @param service 服务类型
+ * @param data 数据指针
+ * @param length 数据长度
+ * @param is_finished 是否完成
+ * @param is_final_fragment 是否最后分片
+ * @return ai_event_t* 创建的事件指针,失败返回NULL
+ */
+ai_event_t* ai_event_create_data(ai_event_type_t type, ai_service_type_t service, 
+                                const uint8_t *data, size_t length, 
+                                bool is_finished, bool is_final_fragment);
+
+/**
+ * @brief 创建控制事件(无数据分配)
+ * @param type 事件类型
+ * @param service 服务类型
+ * @return ai_event_t* 创建的事件指针,失败返回NULL
+ */
+ai_event_t* ai_event_create_control(ai_event_type_t type, ai_service_type_t service);
+
+/**
+ * @brief 销毁事件并释放内存
+ * @param event 事件指针
+ */
+void ai_event_destroy(ai_event_t *event);
+
+/**
+ * @brief 复制事件(深拷贝)
+ * @param src 源事件
+ * @return ai_event_t* 复制的事件指针,失败返回NULL
+ */
+ai_event_t* ai_event_copy(const ai_event_t *src);
+
 /* 事件回调函数类型 */
 typedef void (*ai_event_callback_t)(const ai_event_t *event, void *user_data);
 
index 5a91b55..bc1d647 100644 (file)
@@ -246,6 +246,28 @@ const char* ai_tts_get_voice_name(tts_voice_type_t voice_type);
  */
 const char* ai_services_get_state_string(ai_service_type_t service_type, ai_services_context_t *context);
 
+/* ================================ 音频服务控制接口 ================================ */
+
+/**
+ * @brief 启用ASR录音数据发送
+ * @param context 服务上下文
+ * @return ai_error_t 错误码
+ */
+ai_error_t ai_services_enable_asr_recording(ai_services_context_t *context);
+
+/**
+ * @brief 禁用ASR录音数据发送
+ * @param context 服务上下文
+ * @return ai_error_t 错误码
+ */
+ai_error_t ai_services_disable_asr_recording(ai_services_context_t *context);
+
+/**
+ * @brief 获取ASR录音状态
+ * @return bool 录音状态
+ */
+bool ai_services_is_asr_recording_enabled(void);
+
 #ifdef __cplusplus
 }
 #endif
index 6f24bfa..c372216 100644 (file)
@@ -43,6 +43,7 @@ typedef enum {
  */
 typedef struct {
     tts_voice_type_t voice_type;    // 语音类型
+    char format[16];                // 音频格式,如"pcm"
     uint8_t volume;                 // 音量(0-100)
     uint8_t speed;                  // 语速(0-100)
     uint8_t pitch;                  // 音调(0-100)
@@ -53,15 +54,17 @@ typedef struct {
 /**
  * @brief TTS合成结果结构体
  */
-typedef struct {
+typedef struct tts_result {
     char text[256];                 // 合成文本
-    uint8_t *audio_data;            // 音频数据
+    const uint8_t *audio_data;      // 🔧 音频数据(const修饰)
     uint32_t audio_size;            // 音频大小
+    size_t audio_length;            // 🆕 音频数据长度
     uint32_t sample_rate;           // 采样率
     uint16_t channels;              // 声道数
     uint16_t bits_per_sample;       // 位深度
     char session_id[32];            // 会话ID
     uint32_t duration_ms;           // 音频时长(毫秒)
+    uint32_t timestamp;             // 🆕 时间戳
     bool is_final;                  // 是否为最终结果
 } tts_result_t;
 
@@ -166,10 +169,23 @@ void ai_tts_service_create_default_config(tts_config_t *config);
 /**
  * @brief 处理TTS服务响应
  * @param context TTS服务上下文
- * @param json_data JSON响应数据
+ * @param response JSON响应字符串
+ * @return ai_error_t 错误码
+ */
+ai_error_t ai_tts_service_handle_response(tts_service_context_t *context, const char *response);
+
+/**
+ * @brief 处理TTS音频数据
+ * @param context TTS服务上下文
+ * @param audio_data 音频数据
+ * @param length 数据长度
+ * @param is_finished 是否为完整音频
  * @return ai_error_t 错误码
  */
-ai_error_t ai_tts_service_handle_response(tts_service_context_t *context, const char *json_data);
+ai_error_t ai_tts_service_handle_audio_data(tts_service_context_t *context,
+                                          const uint8_t *audio_data,
+                                          size_t length,
+                                          bool is_finished);
 
 /**
  * @brief 构建TTS请求
@@ -192,6 +208,17 @@ ai_error_t ai_tts_service_build_request(const char *text, const char *session_id
  */
 ai_error_t ai_tts_service_build_stop_message(const char *session_id, char *json_buffer, size_t buffer_size);
 
+
+
+/**
+ * @brief 初始化TTS服务URL
+ * @param url_buf 输出缓冲区,存储构建的URL
+ * @param server_url 服务器URL
+ * @return bool true表示成功,false表示失败
+ * @note 从设备令牌构建完整的TTS服务URL
+ */
+bool tts_api_ws_url_init(char * url_buf,const char *server_url);
+
 #ifdef __cplusplus
 }
 #endif
index 6b60541..f6ec9d8 100644 (file)
@@ -12,6 +12,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
+#include "cJSON.h"
 
 #ifndef __TEST_MODE__
 #include "wearable_log.h"
@@ -44,6 +45,24 @@ static ai_error_t parse_asr_response(const char *json_data, asr_result_t *result
 
 /* ================================ 公共接口实现 ================================ */
 
+/**
+ * @brief 初始化ASR WebSocket URL
+ * @param put_buf 输出缓冲区,存储构建的URL
+ * @return bool true表示成功,false表示失败
+ * @note 从设备令牌构建完整的ASR WebSocket URL
+ *       格式:ws://amp.raisound.com/asr/ws?dt={token}&platform=aliyun
+ */
+bool asr_api_ws_url_init(char *url_buf,const char *server_url)
+{
+    char token[64] = {0};
+    if (read_dev_token(token)) {
+        snprintf(url_buf, AI_MAX_URL_LENGTH, "%s?dt=%s&platform=aliyun", server_url, token);
+    } else {
+        snprintf(url_buf, AI_MAX_URL_LENGTH, "%s?platform=aliyun", server_url);
+    }
+    return true;
+}
+
 ai_error_t ai_asr_service_init(asr_service_context_t *context)
 {
     if (context == NULL) {
@@ -101,8 +120,9 @@ ai_error_t ai_asr_service_configure(asr_service_context_t *context, const asr_co
     }
     
     memcpy(&context->config, config, sizeof(asr_config_t));
-    AI_LOGI("ASR configured: language=%s, vad=%s, punctuation=%s", 
+    AI_LOGI("ASR configured: language=%s, format=%s, vad=%s, punctuation=%s", 
            config->language, 
+           config->format,
            config->enable_vad ? "enabled" : "disabled",
            config->enable_punctuation ? "enabled" : "disabled");
     
@@ -185,50 +205,53 @@ ai_error_t ai_asr_service_send_audio(asr_service_context_t *context, const uint8
         return AI_ERROR_STATE_INVALID;
     }
     
-    /* 检查数据大小 */
-    if (data_size > MAX_AUDIO_CHUNK_SIZE) {
-        AI_LOGW("Audio chunk too large: %u bytes, splitting", data_size);
+    /* 🔧 使用迭代而不是递归来处理大数据分块发送 */
+    uint32_t total_offset = 0;
+    bool state_changed = false;
+    
+    while (total_offset < data_size) {
+        /* 计算当前块的大小 */
+        uint32_t remaining = data_size - total_offset;
+        uint32_t current_chunk_size = (remaining > MAX_AUDIO_CHUNK_SIZE) ? 
+                                    MAX_AUDIO_CHUNK_SIZE : remaining;
         
-        /* 分块发送 */
-        uint32_t offset = 0;
-        while (offset < data_size) {
-            uint32_t chunk_size = (data_size - offset > MAX_AUDIO_CHUNK_SIZE) ? 
-                                 MAX_AUDIO_CHUNK_SIZE : (data_size - offset);
-            
-            ai_error_t ret = ai_asr_service_send_audio(context, audio_data + offset, chunk_size);
-            if (ret != AI_OK) {
-                return ret;
-            }
-            offset += chunk_size;
+        if (remaining > MAX_AUDIO_CHUNK_SIZE) {
+            AI_LOGW("Audio chunk too large: %u bytes remaining, sending %u bytes", 
+                   remaining, current_chunk_size);
         }
-        return AI_OK;
-    }
-    
-    /* 构建音频数据消息 */
-    ai_buffer_t *buffer = ai_memory_pool_alloc_network(AI_SERVICE_ASR, data_size + 64);
-    if (buffer == NULL) {
-        AI_LOGE("Failed to allocate network buffer for audio data");
-        return AI_ERROR_MEMORY_ALLOC;
-    }
-    
-    /* 简化的二进制数据发送(实际实现中可能需要Base64编码) */
-    memcpy(buffer->buffer, audio_data, data_size);
-    buffer->used = data_size;
-    
-    ai_error_t ret = ai_connection_manager_send_data(AI_SERVICE_ASR, (const char*)buffer->buffer, buffer->used);
-    
-    ai_memory_pool_free(buffer);
-    
-    if (ret != AI_OK) {
-        AI_LOGE("Failed to send audio data");
-        return ret;
-    }
-    
-    /* 更新状态 */
-    if (context->state == ASR_STATE_LISTENING) {
-        change_asr_state(context, ASR_STATE_RECOGNIZING);
+        
+        /* 构建音频数据消息 */
+        ai_buffer_t *buffer = ai_memory_pool_alloc_network(AI_SERVICE_ASR, current_chunk_size + 64);
+        if (buffer == NULL) {
+            AI_LOGE("Failed to allocate network buffer for audio data");
+            return AI_ERROR_MEMORY_ALLOC;
+        }
+        
+        /* 简化的二进制数据发送(实际实现中可能需要Base64编码) */
+        memcpy(buffer->buffer, audio_data + total_offset, current_chunk_size);
+        buffer->used = current_chunk_size;
+        
+        ai_error_t ret = ai_connection_manager_send_rawdata(AI_SERVICE_ASR, 
+                                                          (const char*)buffer->buffer, 
+                                                          buffer->used);
+        
+        ai_memory_pool_free(buffer);
+        
+        if (ret != AI_OK) {
+            AI_LOGE("Failed to send audio data chunk");
+            return ret;
+        }
+        
+        /* 更新状态(只在第一个块时更新) */
+        if (!state_changed && context->state == ASR_STATE_LISTENING) {
+            change_asr_state(context, ASR_STATE_RECOGNIZING);
+            state_changed = true;
+        }
+        
+        total_offset += current_chunk_size;
     }
     
+    /* 更新活动时间 */
     context->last_activity_time = get_current_timestamp();
     
     return AI_OK;
@@ -289,6 +312,7 @@ void ai_asr_service_create_default_config(asr_config_t *config)
     config->enable_partial_results = true;
     config->silence_timeout_ms = 3000;      // 3秒静音超时
     config->max_duration_ms = 30000;        // 30秒最大录音时长
+    strncpy(config->format, "mp3", sizeof(config->format) - 1);
     strncpy(config->language, "zh-CN", sizeof(config->language) - 1);
 }
 
@@ -299,7 +323,7 @@ ai_error_t ai_asr_service_handle_response(asr_service_context_t *context, const
     }
     
     AI_LOGI("Handling ASR response: %.100s...", json_data);
-    
+        
     /* 解析ASR响应 */
     asr_result_t result;
     ai_error_t ret = parse_asr_response(json_data, &result);
@@ -326,7 +350,39 @@ ai_error_t ai_asr_service_handle_response(asr_service_context_t *context, const
     
     return AI_OK;
 }
+/***
+ * - StartTranscription (开启asr识别)
+
+  - ```JSON
+    {
+        "header": {
+            "event": "StartTranscription", // 开启事件
+            "trace_id": "3fa6b9e6a30b407ea38ad7ae69e6c291" // 随机数,客户端生成 
+        },
+        "payload": { // 目前支持 16 bit 16k 1(mono) pcm 音频流
+            "format": "pcm", // 音频格式
+            "sample_rate": 16000 // 采样率
+        }
+    }
+    ```
+
+- 音频发送
+
+  - ```Markdown
+    BinaryFrame 二进制帧 发送音频流
+    ```
 
+- StopTranscription (停止音频发送)
+
+  - ```JSON
+    {
+        "header": {
+            "event": "StopTranscription",
+            "trace_id": "3fa6b9e6a30b407ea38ad7ae69e6c291"
+        }
+    }
+    ```
+ */
 ai_error_t ai_asr_service_build_start_message(const char *session_id, const asr_config_t *config, 
                                              char *json_buffer, size_t buffer_size)
 {
@@ -335,27 +391,41 @@ ai_error_t ai_asr_service_build_start_message(const char *session_id, const asr_
     }
     
     /* 构建ASR启动消息 */
+  
     int ret = snprintf(json_buffer, buffer_size,
         "{"
-        "\"type\":\"start\","
-        "\"session_id\":\"%s\","
-        "\"config\":{"
-            "\"language\":\"%s\","
-            "\"enable_vad\":%s,"
-            "\"enable_punctuation\":%s,"
-            "\"enable_partial_results\":%s,"
-            "\"silence_timeout_ms\":%d,"
-            "\"max_duration_ms\":%d"
-        "}"
+            "\"header\":{"
+                "\"event\":\"StartTranscription\","
+                "\"trace_id\":\"%s\""
+            "},"
+            "\"payload\":{"
+                "\"format\":\"pcm\","
+                "\"sample_rate\":16000"
+            "}"
         "}",
-        session_id,
-        config->language,
-        config->enable_vad ? "true" : "false",
-        config->enable_punctuation ? "true" : "false",
-        config->enable_partial_results ? "true" : "false",
-        config->silence_timeout_ms,
-        config->max_duration_ms
+        session_id
     );
+    // int ret = snprintf(json_buffer, buffer_size,
+    //     "{"
+    //     "\"type\":\"start\","
+    //     "\"session_id\":\"%s\","
+    //     "\"config\":{"
+    //         "\"language\":\"%s\","
+    //         "\"enable_vad\":%s,"
+    //         "\"enable_punctuation\":%s,"
+    //         "\"enable_partial_results\":%s,"
+    //         "\"silence_timeout_ms\":%d,"
+    //         "\"max_duration_ms\":%d"
+    //     "}"
+    //     "}",
+    //     session_id,
+    //     config->language,
+    //     config->enable_vad ? "true" : "false",
+    //     config->enable_punctuation ? "true" : "false",
+    //     config->enable_partial_results ? "true" : "false",
+    //     config->silence_timeout_ms,
+    //     config->max_duration_ms
+    // );
     
     if (ret < 0 || ret >= (int)buffer_size) {
         AI_LOGE("JSON buffer too small for ASR start message");
@@ -370,9 +440,17 @@ ai_error_t ai_asr_service_build_stop_message(const char *session_id, char *json_
     if (session_id == NULL || json_buffer == NULL) {
         return AI_ERROR_INVALID_PARAM;
     }
-    
-    int ret = snprintf(json_buffer, buffer_size, 
-                      "{\"type\":\"stop\",\"session_id\":\"%s\"}", session_id);
+    int ret = snprintf(json_buffer, buffer_size,
+        "{"
+            "\"header\":{"
+                "\"event\":\"StopTranscription\","
+                "\"trace_id\":\"%s\""
+            "}"
+        "}",
+        session_id
+    );
+    // int ret = snprintf(json_buffer, buffer_size, 
+    //                   "{\"type\":\"stop\",\"session_id\":\"%s\"}", session_id);
     
     if (ret < 0 || ret >= (int)buffer_size) {
         AI_LOGE("JSON buffer too small for ASR stop message");
@@ -415,7 +493,79 @@ static ai_error_t change_asr_state(asr_service_context_t *context, asr_state_t n
     
     return AI_OK;
 }
+/*
+
+- TranscriptionStarted (开启成功)
+
+  - ```JSON
+    {
+        "status": 20000000,
+        "msg": "success",
+        "task_id": "80228c9f62821ffe3f867e1a5042d887",
+        "event": "TranscriptionStarted",
+        "result": null
+    }
+    ```
+
+- Changed (转写临时结果)
+
+  - ```JSON
+    {
+        "status": 20000000,
+        "msg": "success",
+        "task_id": "80228c9f62821ffe3f867e1a5042d887",
+        "event": "Changed",
+        "result": {
+            "result": "以", // 识别内容
+            "is_end": false, 
+            "sentence_id": "9df197b9a57acc331ae4c334d4c02732",
+            "start_time": 10290,
+            "end_time": 10760
+        }
+    }
+    ```
+
+- SentenceEnd (一句话识别结束)
+
+  - ```JSON
+    {
+        "status": 20000000,
+        "msg": "success",
+        "task_id": "80228c9f62821ffe3f867e1a5042d887",
+        "event": "SentenceEnd",
+        "result": {
+            "result": "嗯。",
+            "is_end": true,
+            "sentence_id": "d7e62c6fb0ee847b85d674828797f3f3",
+            "start_time": 13610,
+            "end_time": 14520
+        }
+    }
+    ```
+
+- TranscriptionCompleted (本次合成结束)
+
+  - ```JSON
+    {
+        "status": 20000000,
+        "msg": "success",
+        "task_id": "8d12cc56aa5fcd66a4c8667e20b5db25",
+        "event": "TranscriptionCompleted",
+        "result": null
+    }
+    ```
+
+- Failed (异常事件 收到后需要重新开启)
 
+  - ```JSON
+    {
+        "status": 40000000,
+        "event": "Failed",
+        "msg":     "request timeout after 23 seconds.",
+        "task_id": "5b47d64b3553f75fb84d51e21144bd72"
+    }
+    ```
+*/
 static ai_error_t parse_asr_response(const char *json_data, asr_result_t *result)
 {
     if (json_data == NULL || result == NULL) {
@@ -427,35 +577,84 @@ static ai_error_t parse_asr_response(const char *json_data, asr_result_t *result
     
     /* 在实际实现中,这里应该使用完整的JSON解析库 */
     /* 这里只是一个简化的示例 */
-    
-    /* 查找文本字段 */
-    const char *text_start = strstr(json_data, "\"text\":\"");
-    if (text_start != NULL) {
-        text_start += 8; // 跳过 "text":"
-        const char *text_end = strchr(text_start, '"');
-        if (text_end != NULL) {
-            size_t text_len = text_end - text_start;
-            if (text_len < sizeof(result->text)) {
-                strncpy(result->text, text_start, text_len);
-                result->text[text_len] = '\0';
-            }
-        }
-    }
-    
-    /* 查找置信度字段 */
-    const char *confidence_start = strstr(json_data, "\"confidence\":");
-    if (confidence_start != NULL) {
-        result->confidence = atof(confidence_start + 13);
-    } else {
-        result->confidence = 0.9f; // 默认置信度
+    // 使用cJSON解析ASR协议
+    cJSON *root = cJSON_Parse(json_data);
+    if (!root) {
+        AI_LOGE("Failed to parse ASR JSON response");
+        return AI_ERROR_JSON_PARSE;
     }
-    
-    /* 查找是否为最终结果 */
-    result->is_final = (strstr(json_data, "\"is_final\":true") != NULL);
-    
-    /* 设置时间戳 */
+
+    // 解析event字段
+    cJSON *event_item = cJSON_GetObjectItem(root, "event");
+    const char *event = event_item && cJSON_IsString(event_item) ? event_item->valuestring : NULL;
+
+    // 解析status字段
+    cJSON *status_item = cJSON_GetObjectItem(root, "status");
+    int status = status_item && cJSON_IsNumber(status_item) ? status_item->valueint : 0;
+
+    // 解析result对象
+    cJSON *result_obj = cJSON_GetObjectItem(root, "result");
+
+    // 默认赋值
+    result->text[0] = '\0';
+    result->confidence = 0.9f;
+    result->is_final = false;
     result->start_time_ms = 0;
     result->end_time_ms = get_current_timestamp();
+
+    if (event) {
+        if (strcmp(event, "Changed") == 0 || strcmp(event, "SentenceEnd") == 0) {
+            if (result_obj && cJSON_IsObject(result_obj)) {
+                // 解析result字段
+                cJSON *text_item = cJSON_GetObjectItem(result_obj, "result");
+                if (text_item && cJSON_IsString(text_item)) {
+                    strncpy(result->text, text_item->valuestring, sizeof(result->text) - 1);
+                    result->text[sizeof(result->text) - 1] = '\0';
+                }
+                // 解析is_end字段
+                cJSON *is_end_item = cJSON_GetObjectItem(result_obj, "is_end");
+                if (is_end_item && cJSON_IsBool(is_end_item)) {
+                    result->is_final = cJSON_IsTrue(is_end_item);
+                }
+                // 解析start_time和end_time
+                cJSON *start_time_item = cJSON_GetObjectItem(result_obj, "start_time");
+                cJSON *end_time_item = cJSON_GetObjectItem(result_obj, "end_time");
+                if (start_time_item && cJSON_IsNumber(start_time_item)) {
+                    result->start_time_ms = start_time_item->valueint;
+                }
+                if (end_time_item && cJSON_IsNumber(end_time_item)) {
+                    result->end_time_ms = end_time_item->valueint;
+                }
+            }
+        } else if (strcmp(event, "TranscriptionStarted") == 0) {
+            // 启动事件,无识别内容
+            result->text[0] = '\0';
+            result->is_final = false;
+        } else if (strcmp(event, "TranscriptionCompleted") == 0) {
+            // 识别完成,无内容
+            result->text[0] = '\0';
+            result->is_final = true;
+        } else if (strcmp(event, "Failed") == 0) {
+            // 失败事件
+            result->text[0] = '\0';
+            result->is_final = true;
+        }
+    }
+
+    // // 可选:解析置信度字段(如果协议有)
+    // cJSON *confidence_item = NULL;
+    // if (result_obj && cJSON_IsObject(result_obj)) {
+    //     confidence_item = cJSON_GetObjectItem(result_obj, "confidence");
+    // }
+    // if (confidence_item && cJSON_IsNumber(confidence_item)) {
+    //     result->confidence = (float)confidence_item->valuedouble;
+    // } else {
+    //     result->confidence = 0.9f;
+    // }
+
+    cJSON_Delete(root);
+    /* 查找文本字段 */
+   
     
     AI_LOGI("Parsed ASR result: text='%s', confidence=%.2f, final=%s", 
            result->text, result->confidence, result->is_final ? "true" : "false");
index 1808b1e..5471c04 100644 (file)
@@ -44,6 +44,76 @@ static ai_error_t parse_chat_response(const char *json_data, chat_message_t *mes
 
 /* ================================ 公共接口实现 ================================ */
 
+
+bool chat_api_ws_url_init(char * url_buf,const char *server_url,uint8_t type_ai)
+{
+    char token[64]={0};
+    if(read_dev_token(token))
+    {
+        if(type_ai==AI_TYPE_JIU)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=chinamobiledevice&bot_type=jiutian-lan",server_url,token);
+        }
+        else if(type_ai==AI_TYPE_DOU)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=huoshan",server_url,token);
+        }
+        else if(type_ai==AI_TYPE_WEN)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=baidu",server_url,token);
+        }
+        else if(type_ai==AI_TYPE_KIMI)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=moonshot",server_url,token);
+        }
+            else if(type_ai==AI_TYPE_DE)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=huoshan&model=ep-20250205103849-582m7",server_url,token);
+        }
+        else 
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=aliyun",server_url,token);
+        }
+
+    }
+    else
+    {
+        char * Service_url="118.145.158.207:22080";
+
+        char device_id[128]={0};
+        get_dev_id(device_id);
+    
+        char * product_id="1927646275585744898";
+        
+        if(type_ai==AI_TYPE_JIU)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?device_id=%s&product_id=%s&platform=chinamobiledevice&bot_type=jiutian-lan",server_url,device_id,product_id);
+        }
+        else if(type_ai==AI_TYPE_DOU)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?platform=huoshan",server_url);
+        }
+        else if(type_ai==AI_TYPE_WEN)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?platform=baidu",server_url);
+        }
+        else if(type_ai==AI_TYPE_KIMI)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?platform=moonshot",server_url);
+        }
+            else if(type_ai==AI_TYPE_DE)
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?platform=huoshan&model=ep-20250205103849-582m7",server_url);
+        }
+        else 
+        {
+            snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?platform=aliyun",server_url);
+        }
+    }
+    
+    return true;
+  
+}
 ai_error_t ai_chat_service_init(chat_service_context_t *context)
 {
     if (context == NULL) {
index 8097b1b..f7d7385 100644 (file)
 static ai_connection_manager_t g_connection_manager = {0};
 static bool g_manager_initialized = false;
 
+/* WebSocket连接计数器和限制 */
+static int g_active_websocket_count = 0;
+#define MAX_CONCURRENT_WEBSOCKETS 4  // 最大并发WebSocket连接数
+
 /* 服务名称映射表 */
 static const char* g_service_names[AI_SERVICE_COUNT] = {
     "ASR", "Chat", "TTS"
@@ -49,12 +53,17 @@ static ai_error_t connection_thread_create(ai_service_type_t service_type);
 static void connection_thread_entry(void *argument);
 static void worker_thread_entry(void *argument);
 static ai_error_t websocket_connect(ai_connection_info_t *conn_info);
+static uint32_t get_current_timestamp(void);
+static ai_connection_info_t* find_connection_by_websocket(int websocket_fd);
 static void websocket_on_connected(int client);
 static void websocket_on_message(int client, const char *data, size_t length, bool isFinished);
 static void websocket_on_error(int client, struct WebSocketErrorResult errorResult);
 static void websocket_on_disconnected(int client);
 static ai_error_t update_global_state(void);
 static void send_event(ai_event_type_t type, ai_service_type_t service, const void *data);
+static void send_data_event(ai_event_type_t type, ai_service_type_t service, const char *data, size_t length, bool is_finished, bool is_final_fragment); // 🆕
+static bool detect_json_data(const char *data, size_t length); // 🆕
+static void handle_fragmented_message(ai_connection_info_t *conn_info, const char *data, size_t length, ai_event_type_t event_type); // 🆕
 static uint32_t get_current_timestamp(void);
 /* static ai_error_t build_service_url(ai_service_type_t service_type, char *url_buffer, size_t buffer_size); */
 
@@ -62,6 +71,56 @@ static uint32_t get_current_timestamp(void);
 static void heartbeat_timer_callback(void *argument);
 #endif
 
+/* WebSocket连接管理辅助函数 */
+static bool can_create_websocket_connection(void);
+static void cleanup_websocket_connection(ai_connection_info_t *conn_info);
+
+/* 在连接失败或断开时,确保完全清理资源 */
+static void cleanup_websocket_connection(ai_connection_info_t *conn_info)
+{
+    if (!conn_info) {
+        return;
+    }
+    
+    if (conn_info->websocket >= 0) {
+        AI_LOGI("Cleaning up WebSocket %d for service %s", 
+               conn_info->websocket, g_service_names[conn_info->service_type]);
+        
+#ifndef __TEST_MODE__
+        // 关闭WebSocket连接
+        WebSocketClientClose(conn_info->websocket);
+        
+        // 等待资源释放 - 关键步骤!
+        osDelay(200);  // 200ms延迟让系统释放socket资源
+#endif
+        
+        // 重置连接信息
+        conn_info->websocket = -1;
+        conn_info->state = AI_CONN_STATE_DISCONNECTED;
+        
+        // 减少活跃连接计数
+        if (g_active_websocket_count > 0) {
+            g_active_websocket_count--;
+            AI_LOGI("Active WebSocket connections: %d/%d", 
+                   g_active_websocket_count, MAX_CONCURRENT_WEBSOCKETS);
+        }
+    }
+}
+
+/* 检查是否可以创建新的WebSocket连接 */
+static bool can_create_websocket_connection(void)
+{
+    if (g_active_websocket_count >= MAX_CONCURRENT_WEBSOCKETS) {
+        AI_LOGW("Too many active WebSocket connections: %d/%d", 
+               g_active_websocket_count, MAX_CONCURRENT_WEBSOCKETS);
+        return false;
+    }
+    
+    AI_LOGI("WebSocket connection check passed: %d/%d active connections", 
+           g_active_websocket_count, MAX_CONCURRENT_WEBSOCKETS);
+    return true;
+}
+
 /* ================================ 公共接口实现 ================================ */
 
 ai_error_t ai_connection_manager_init(const ai_connection_manager_config_t *config)
@@ -96,8 +155,8 @@ ai_error_t ai_connection_manager_init(const ai_connection_manager_config_t *conf
         return AI_ERROR_MEMORY_ALLOC;
     }
     
-    /* 创建事件队列 */
-    g_connection_manager.event_queue = osMessageQueueNew(16, sizeof(ai_event_t), NULL);
+    /* 创建事件队列 - 🆕 现在存储事件指针,大幅减少内存使用 */
+    g_connection_manager.event_queue = osMessageQueueNew(16, sizeof(ai_event_t*), NULL);
     if (g_connection_manager.event_queue == NULL) {
         AI_LOGE("Failed to create event queue");
         osMutexDelete(g_connection_manager.global_mutex);
@@ -123,13 +182,19 @@ ai_error_t ai_connection_manager_init(const ai_connection_manager_config_t *conf
         /* 设置服务URL */
         switch (i) {
             case AI_SERVICE_ASR:
-                strncpy(conn->url, config->connection_config.asr_url, AI_MAX_URL_LENGTH - 1);
+                asr_api_ws_url_init(conn->url,config->connection_config.asr_url);
+                // strncpy(conn->url, config->connection_config.asr_url, AI_MAX_URL_LENGTH - 1);
+                printf("asr_url: %s\n", conn->url);
                 break;
             case AI_SERVICE_CHAT:
-                strncpy(conn->url, config->connection_config.chat_url, AI_MAX_URL_LENGTH - 1);
+                chat_api_ws_url_init(conn->url,config->connection_config.chat_url,AI_TYPE_JIU);
+                // strncpy(conn->url, config->connection_config.chat_url, AI_MAX_URL_LENGTH - 1);
+                printf("chat_url: %s\n", conn->url);
                 break;
             case AI_SERVICE_TTS:
-                strncpy(conn->url, config->connection_config.tts_url, AI_MAX_URL_LENGTH - 1);
+                tts_api_ws_url_init(conn->url,config->connection_config.tts_url);
+                // strncpy(conn->url, config->connection_config.tts_url, AI_MAX_URL_LENGTH - 1);
+                printf("tts_url: %s\n", conn->url);
                 break;
         }
         
@@ -255,13 +320,8 @@ ai_error_t ai_connection_manager_destroy(void)
         }
 #endif
         
-        /* 关闭WebSocket连接 */
-        if (conn->websocket >= 0) {
-#ifndef __TEST_MODE__
-                    WebSocketClientClose(conn->websocket);
-#endif
-            conn->websocket = -1;
-        }
+        /* 清理所有WebSocket连接 */
+        cleanup_websocket_connection(conn);
     }
     
 #ifndef __TEST_MODE__
@@ -278,6 +338,10 @@ ai_error_t ai_connection_manager_destroy(void)
     
     /* 重置状态 */
     memset(&g_connection_manager, 0, sizeof(g_connection_manager));
+    
+    /* 重置连接计数器 */
+    g_active_websocket_count = 0;
+    
     g_manager_initialized = false;
     
     AI_LOGI("Connection manager destroyed");
@@ -292,25 +356,16 @@ ai_error_t ai_connection_manager_start_all(void)
     
     AI_LOGI("[DEBUG] ai_connection_manager_start_all: Entry point");
     
-    AI_LOGI("[DEBUG] Setting global state to CONNECTING...");
     g_connection_manager.global_state = AI_STATE_CONNECTING;
-    AI_LOGI("[DEBUG] Global state set successfully");
-    
-    AI_LOGI("[DEBUG] Setting is_running to true...");
     g_connection_manager.is_running = true;
-    AI_LOGI("[DEBUG] is_running set successfully");
     
     /* 记录连接开始时间 */
-    AI_LOGI("[DEBUG] Getting current timestamp...");
     uint32_t start_time = get_current_timestamp();
     AI_LOGI("[DEBUG] Current timestamp: %u", start_time);
     
-    AI_LOGI("[DEBUG] Setting last_connect_timestamp...");
     g_connection_manager.metrics.last_connect_timestamp = start_time;
-    AI_LOGI("[DEBUG] last_connect_timestamp set successfully");
     
     /* 并发启动所有连接线程 */
-    AI_LOGI("[DEBUG] Starting connection thread creation loop...");
     AI_LOGI("[DEBUG] AI_SERVICE_COUNT = %d", AI_SERVICE_COUNT);
     
     for (int i = 0; i < AI_SERVICE_COUNT; i++) {
@@ -327,14 +382,11 @@ ai_error_t ai_connection_manager_start_all(void)
         AI_LOGI("[DEBUG] Thread created successfully for service %s", g_service_names[i]);
         
 #ifndef __TEST_MODE__
-        AI_LOGI("[DEBUG] Calling osDelay(100)...");
         /* 给线程一些时间启动,避免竞态条件 */
         osDelay(100); // 100ms延迟
-        AI_LOGI("[DEBUG] osDelay(100) completed");
 #endif
     }
     
-    AI_LOGI("[DEBUG] All connection threads created, checking heartbeat timer...");
     
 #ifndef __TEST_MODE__
     /* 启动心跳定时器 */
@@ -344,13 +396,9 @@ ai_error_t ai_connection_manager_start_all(void)
                 g_connection_manager.config.keepalive_interval_ms);
         osTimerStart(g_connection_manager.heartbeat_timer, 
                     g_connection_manager.config.keepalive_interval_ms);
-        AI_LOGI("[DEBUG] Heartbeat timer started successfully");
-    } else {
-        AI_LOGI("[DEBUG] Heartbeat timer is NULL, skipping");
-    }
+    } 
 #endif
     
-    AI_LOGI("[DEBUG] ai_connection_manager_start_all: About to return AI_OK");
     AI_LOGI("All connection threads started");
     return AI_OK;
 }
@@ -399,6 +447,65 @@ ai_error_t ai_connection_manager_stop_all(void)
     AI_LOGI("All connections stopped");
     return AI_OK;
 }
+ai_error_t ai_connection_manager_send_rawdata(ai_service_type_t service_type, 
+                                              const void *data, size_t length)
+{
+    if (!g_manager_initialized) {
+        return AI_ERROR_NOT_INITIALIZED;
+    }
+
+    if (service_type >= AI_SERVICE_COUNT || data == NULL || length == 0) {
+        return AI_ERROR_INVALID_PARAM;
+    }
+
+    ai_connection_info_t *conn = &g_connection_manager.connections[service_type];
+
+#ifndef __TEST_MODE__
+    if (conn->mutex != NULL) {
+        osMutexAcquire(conn->mutex, osWaitForever);
+    }
+#endif
+
+    ai_error_t result = AI_OK;
+
+    if (conn->state != AI_CONN_STATE_CONNECTED || conn->websocket < 0) {
+        AI_LOGW("Service %s not connected, cannot send raw data", g_service_names[service_type]);
+        result = AI_ERROR_SERVICE_UNAVAILABLE;
+    } else {
+        // 检查缓冲区空间
+        if (conn->send_buffer != NULL && length <= conn->send_buffer->size) {
+            memcpy(conn->send_buffer->buffer, data, length);
+            conn->send_buffer->used = length;
+
+#ifndef __TEST_MODE__
+            // 发送二进制数据
+            int ret = WebSocketClientSendBinStart(conn->websocket, (const char*)conn->send_buffer->buffer, length);
+            if (ret < 0) {
+                AI_LOGE("Failed to send raw data to service %s", g_service_names[service_type]);
+                result = AI_ERROR_WEBSOCKET;
+            } else {
+                // 更新统计
+                g_connection_manager.metrics.total_bytes_sent += length;
+                conn->last_activity_time = get_current_timestamp();
+            }
+#else
+            AI_LOGI("TEST: Sending %lu bytes (raw) to service %s", (unsigned long)length, g_service_names[service_type]);
+            g_connection_manager.metrics.total_bytes_sent += length;
+#endif
+        } else {
+            AI_LOGE("Send buffer too small for service %s (raw)", g_service_names[service_type]);
+            result = AI_ERROR_BUFFER_OVERFLOW;
+        }
+    }
+
+#ifndef __TEST_MODE__
+    if (conn->mutex != NULL) {
+        osMutexRelease(conn->mutex);
+    }
+#endif
+
+    return result;
+}
 
 ai_error_t ai_connection_manager_send_data(ai_service_type_t service_type, 
                                           const char *data, size_t length)
@@ -442,7 +549,7 @@ ai_error_t ai_connection_manager_send_data(ai_service_type_t service_type,
                 conn->last_activity_time = get_current_timestamp();
             }
 #else
-            AI_LOGI("TEST: Sending %zu bytes to service %s", length, g_service_names[service_type]);
+            AI_LOGI("TEST: Sending %lu bytes to service %s", (unsigned long)length, g_service_names[service_type]);
             g_connection_manager.metrics.total_bytes_sent += length;
 #endif
         } else {
@@ -498,6 +605,53 @@ const ai_connect_metrics_t* ai_connection_manager_get_metrics(void)
     return &g_connection_manager.metrics;
 }
 
+ai_error_t ai_connection_manager_reconnect(ai_service_type_t service_type)
+{
+    if (!g_manager_initialized) {
+        return AI_ERROR_NOT_INITIALIZED;
+    }
+    
+    if (service_type >= AI_SERVICE_COUNT) {
+        return AI_ERROR_INVALID_PARAM;
+    }
+    
+    ai_connection_info_t *conn = &g_connection_manager.connections[service_type];
+    
+    AI_LOGI("Manual reconnect requested for service %s", g_service_names[service_type]);
+    
+    /* 清理现有连接 */
+    cleanup_websocket_connection(conn);
+    
+    /* 重置重试计数器 */
+    conn->retry_count = 0;
+    
+    /* 重置连接状态为断开,让连接线程重新尝试 */
+    conn->state = AI_CONN_STATE_DISCONNECTED;
+    
+    /* 更新连接状态矩阵 */
+    switch (service_type) {
+        case AI_SERVICE_ASR:
+            g_connection_manager.connection_matrix.asr_connected = 0;
+            break;
+        case AI_SERVICE_CHAT:
+            g_connection_manager.connection_matrix.chat_connected = 0;
+            break;
+        case AI_SERVICE_TTS:
+            g_connection_manager.connection_matrix.tts_connected = 0;
+            break;
+        case AI_SERVICE_COUNT:
+        case AI_SERVICE_GLOBAL:
+            break;
+    }
+    
+    /* 更新全局状态 */
+    update_global_state();
+    
+    AI_LOGI("Service %s reset for reconnection", g_service_names[service_type]);
+    
+    return AI_OK;
+}
+
 ai_error_t ai_connection_manager_wait_ready(ai_service_type_t service_type, uint32_t timeout_ms)
 {
     if (!g_manager_initialized || service_type >= AI_SERVICE_COUNT) {
@@ -627,11 +781,10 @@ static ai_error_t connection_thread_create(ai_service_type_t service_type)
     AI_LOGI("[DEBUG] Connection info pointer: %p", conn);
     
 #ifndef __TEST_MODE__
-    AI_LOGI("[DEBUG] Creating osThreadAttr_t structure...");
     
     /* 使用更保守的线程配置以避免创建失败 */
     osThreadAttr_t thread_attr = {
-        .stack_size = 2048,  // 减少栈大小到2KB
+        .stack_size = 8192,  // 🔧 增加栈大小到8KB以确保WebSocket操作有足够空间
         .name = "connection_thread",
         .priority = osPriorityNormal  // 使用标准优先级
     };
@@ -649,12 +802,10 @@ static ai_error_t connection_thread_create(ai_service_type_t service_type)
             thread_attr.stack_size, (int)thread_attr.priority);
     AI_LOGI("[DEBUG] Original config priority was: %d", g_connection_manager.config.connection_thread_priority);
     
-    AI_LOGI("[DEBUG] Skipping thread name setting to avoid exception...");
     AI_LOGI("[DEBUG] service_type=%d, service_name=%s", service_type, g_service_names[service_type]);
     
     /* 临时禁用线程名称设置以避免异常 */
     
-    AI_LOGI("[DEBUG] Calling osThreadNew...");
     AI_LOGI("[DEBUG] Parameters: entry=%p, argument=%p, attr=%p", 
             connection_thread_entry, (void*)service_type, &thread_attr);
     
@@ -665,8 +816,6 @@ static ai_error_t connection_thread_create(ai_service_type_t service_type)
     AI_LOGI("[DEBUG] osThreadNew returned: %p", conn->thread_id);
     
     if (conn->thread_id == NULL) {
-        AI_LOGE("[DEBUG] Thread creation failed, trying with minimal configuration...");
-        
         /* 尝试使用最小配置 */
         osThreadAttr_t minimal_attr = {
             .stack_size = 1024,  // 最小栈大小1KB
@@ -714,11 +863,13 @@ static void connection_thread_entry(void *argument)
 {
     AI_LOGI("[DEBUG] connection_thread_entry: Entry, argument=%p", argument);
     
-    AI_LOGI("[DEBUG] Converting argument to service_type...");
+    /* 🔧 栈使用监控 - 在栈上分配一个标记来检测栈使用 */
+    volatile uint32_t stack_marker = 0xDEADBEEF;
+    AI_LOGI("[DEBUG] Stack marker address: %p", &stack_marker);
+    
     ai_service_type_t service_type = (ai_service_type_t)(uintptr_t)argument;
     AI_LOGI("[DEBUG] service_type=%d", service_type);
     
-    AI_LOGI("[DEBUG] Getting connection info...");
     ai_connection_info_t *conn = &g_connection_manager.connections[service_type];
     AI_LOGI("[DEBUG] Connection info pointer: %p", conn);
     
@@ -751,6 +902,9 @@ static void connection_thread_entry(void *argument)
                     case AI_SERVICE_COUNT:
                         /* 不应该到达这里 */
                         break;
+                    case AI_SERVICE_GLOBAL:
+                        /* 全局服务不需要更新连接矩阵 */
+                        break;
                 }
                 
                 update_global_state();
@@ -771,6 +925,9 @@ static void connection_thread_entry(void *argument)
                        conn->retry_count, 
                        g_connection_manager.config.max_retry_count);
                 
+                /* 使用新的清理函数替换原有的清理逻辑 */
+                cleanup_websocket_connection(conn);
+                
                 /* 为了安全,限制重试次数为1次 */
                 if (conn->retry_count >= 1) {
                     AI_LOGW("Service %s connection disabled for safety after %d attempts", 
@@ -806,14 +963,17 @@ static void worker_thread_entry(void *argument)
     
     while (g_connection_manager.is_running) {
 #ifndef __TEST_MODE__
-        ai_event_t event;
-        osStatus_t status = osMessageQueueGet(g_connection_manager.event_queue, &event, NULL, 1000);
+        ai_event_t *event_ptr; // 声明一个指针来接收事件
+        osStatus_t status = osMessageQueueGet(g_connection_manager.event_queue, &event_ptr, NULL, 1000);
         
         if (status == osOK) {
             /* 处理事件 */
             if (g_connection_manager.config.event_callback != NULL) {
-                g_connection_manager.config.event_callback(&event, g_connection_manager.config.user_data);
+                // 调用回调函数,传入事件指针
+                g_connection_manager.config.event_callback(event_ptr, g_connection_manager.config.user_data);
             }
+            // 释放事件内存
+            ai_event_destroy(event_ptr);
         }
 #else
         /* 测试模式下简单延时 */
@@ -842,6 +1002,12 @@ static ai_error_t websocket_connect(ai_connection_info_t *conn_info)
         return AI_ERROR_NOT_INITIALIZED;
     }
     
+    /* 检查是否可以创建新连接 */
+    if (!can_create_websocket_connection()) {
+        AI_LOGE("Cannot create WebSocket connection: too many active connections");
+        return AI_ERROR_RESOURCE_LIMIT;
+    }
+    
     AI_LOGI("Connecting to %s: %s", g_service_names[conn_info->service_type], conn_info->url);
     
 #ifndef __TEST_MODE__
@@ -855,15 +1021,21 @@ static ai_error_t websocket_connect(ai_connection_info_t *conn_info)
         return AI_ERROR_MEMORY_ALLOC;
     }
     
-    AI_LOGI("WebSocket client created successfully, fd=%d", conn_info->websocket);
+    /* 增加活跃连接计数 */
+    g_active_websocket_count++;
+    AI_LOGI("WebSocket client created successfully, fd=%d for service %s (active: %d/%d)", 
+           conn_info->websocket, g_service_names[conn_info->service_type],
+           g_active_websocket_count, MAX_CONCURRENT_WEBSOCKETS);
+    
     conn_info->state = AI_CONN_STATE_CONNECTING;
     
     int ret = WebSocketClientConnect(conn_info->websocket, conn_info->url);
     if (ret != 0) {
         AI_LOGE("WebSocket connection failed: %d", ret);
-        WebSocketClientClose(conn_info->websocket);
-        conn_info->websocket = -1;
-        conn_info->state = AI_CONN_STATE_DISCONNECTED;
+        
+        /* 使用新的清理函数 */
+        cleanup_websocket_connection(conn_info);
+        
         return AI_ERROR_CONNECTION_FAILED;
     }
     
@@ -872,24 +1044,30 @@ static ai_error_t websocket_connect(ai_connection_info_t *conn_info)
     /* 测试模式下模拟连接成功 */
     conn_info->websocket = 1; // 模拟WebSocket文件描述符
     conn_info->state = AI_CONN_STATE_CONNECTED;
+    g_active_websocket_count++;  // 测试模式也要计数
     AI_LOGI("TEST: Connected to %s", conn_info->url);
 #endif
     
-    conn_info->connect_time_ms = get_current_timestamp();
-    conn_info->last_activity_time = conn_info->connect_time_ms;
-    conn_info->retry_count = 0;
-    
     return AI_OK;
 }
 
 /* 通过WebSocket客户端fd查找连接信息 */
 static ai_connection_info_t* find_connection_by_websocket(int websocket_fd)
 {
+    AI_LOGI("[DEBUG] Looking for connection with WebSocket fd=%d", websocket_fd);
+    
     for (int i = 0; i < AI_SERVICE_COUNT; i++) {
+        AI_LOGI("[DEBUG] Checking service %d (%s): websocket=%d, state=%d", 
+               i, g_service_names[i], g_connection_manager.connections[i].websocket,
+               g_connection_manager.connections[i].state);
+        
         if (g_connection_manager.connections[i].websocket == websocket_fd) {
+            AI_LOGI("[DEBUG] Found matching connection for service %s", g_service_names[i]);
             return &g_connection_manager.connections[i];
         }
     }
+    
+    AI_LOGE("[DEBUG] No connection found for WebSocket fd=%d", websocket_fd);
     return NULL;
 }
 
@@ -933,8 +1111,6 @@ static void websocket_on_connected(int client)
 
 static void websocket_on_message(int client, const char *data, size_t length, bool isFinished)
 {
-    (void)isFinished; // 暂时不处理分片消息
-    
     /* 安全检查:确保管理器已初始化 */
     if (!g_manager_initialized) {
         return;
@@ -951,22 +1127,26 @@ static void websocket_on_message(int client, const char *data, size_t length, bo
         return;
     }
     
-    AI_LOGI("Received %zu bytes from service %s", length, g_service_names[conn_info->service_type]);
+    /* 🆕 数据类型检测 */
+    bool is_json = detect_json_data(data, length);
+    ai_event_type_t event_type = is_json ? AI_EVENT_TEXT_DATA_RECEIVED : AI_EVENT_BIN_DATA_RECEIVED;
+    
+    AI_LOGI("Received %lu bytes from service %s, type=%s, finished=%s", 
+           (unsigned long)length, g_service_names[conn_info->service_type],
+           is_json ? "JSON" : "BINARY", isFinished ? "true" : "false");
+    
+    /* 🆕 处理分片消息 */
+    if (!isFinished) {
+        handle_fragmented_message(conn_info, data, length, event_type);
+        return;
+    }
     
     /* 更新统计 */
     g_connection_manager.metrics.total_bytes_received += length;
     conn_info->last_activity_time = get_current_timestamp();
     
-    /* 发送数据接收事件 */
-    ai_event_t event = {
-        .type = AI_EVENT_DATA_RECEIVED,
-        .service = conn_info->service_type,
-        .timestamp = get_current_timestamp()
-    };
-    event.payload.data.data = data;
-    event.payload.data.length = length;
-    
-    send_event(AI_EVENT_DATA_RECEIVED, conn_info->service_type, &event.payload.data);
+    /* 🆕 发送带实际数据的事件 */
+    send_data_event(event_type, conn_info->service_type, data, length, isFinished, true);
 }
 
 static void websocket_on_error(int client, struct WebSocketErrorResult errorResult)
@@ -1017,7 +1197,8 @@ static void websocket_on_disconnected(int client)
     
     AI_LOGW("WebSocket disconnected for service %s", g_service_names[conn_info->service_type]);
     
-    conn_info->state = AI_CONN_STATE_DISCONNECTED;
+    /* 使用新的清理函数 */
+    cleanup_websocket_connection(conn_info);
     
     /* 更新连接状态矩阵 */
     switch (conn_info->service_type) {
@@ -1033,6 +1214,9 @@ static void websocket_on_disconnected(int client)
         case AI_SERVICE_COUNT:
             /* 不应该到达这里 */
             break;
+        case AI_SERVICE_GLOBAL:
+            /* 全局服务不需要更新连接矩阵 */
+            break;
     }
     
     update_global_state();
@@ -1068,13 +1252,13 @@ static ai_error_t update_global_state(void)
         /* 发送状态变更事件 */
         ai_event_t event = {
             .type = AI_EVENT_STATE_CHANGED,
-            .service = AI_SERVICE_COUNT, // 表示全局事件
+            .service = AI_SERVICE_GLOBAL, // 🔧 使用全局服务类型而不是AI_SERVICE_COUNT
             .timestamp = get_current_timestamp()
         };
         event.payload.state_change.old_state = old_state;
         event.payload.state_change.new_state = new_state;
         
-        send_event(AI_EVENT_STATE_CHANGED, AI_SERVICE_COUNT, &event.payload.state_change);
+        send_event(AI_EVENT_STATE_CHANGED, AI_SERVICE_GLOBAL, &event.payload.state_change);
     }
     
     return AI_OK;
@@ -1086,23 +1270,36 @@ static void send_event(ai_event_type_t type, ai_service_type_t service, const vo
         return;
     }
     
-    ai_event_t event = {
-        .type = type,
-        .service = service,
-        .timestamp = get_current_timestamp()
-    };
-    
-    /* 根据事件类型复制数据 */
+    /* 🆕 使用控制事件创建函数 */
+    ai_event_t *event = ai_event_create_control(type, service);
+    if (event == NULL) {
+        AI_LOGE("Failed to create control event");
+        return;
+    }
+
+    /* 根据事件类型复制数据到相应的payload */
     if (data != NULL) {
         switch (type) {
-            case AI_EVENT_DATA_RECEIVED:
-                memcpy(&event.payload.data, data, sizeof(event.payload.data));
+            case AI_EVENT_TEXT_DATA_RECEIVED:
+            case AI_EVENT_BIN_DATA_RECEIVED:
+                /* 🆕 数据事件现在使用 send_data_event 函数 */
+                AI_LOGW("Data events should use send_data_event function");
                 break;
             case AI_EVENT_ERROR_OCCURRED:
-                event.payload.error.error_code = *(int*)data;
+                if (data != NULL) {
+                    event->payload.error.error_code = *(int*)data;
+                }
                 break;
             case AI_EVENT_STATE_CHANGED:
-                memcpy(&event.payload.state_change, data, sizeof(event.payload.state_change));
+                if (data != NULL) {
+                    memcpy(&event->payload.state_change, data, sizeof(event->payload.state_change));
+                }
+                break;
+            case AI_EVENT_CONNECTION_ESTABLISHED:
+            case AI_EVENT_CONNECTION_LOST:
+                if (data != NULL) {
+                    memcpy(&event->payload.connection, data, sizeof(event->payload.connection));
+                }
                 break;
             default:
                 break;
@@ -1112,14 +1309,122 @@ static void send_event(ai_event_type_t type, ai_service_type_t service, const vo
 #ifndef __TEST_MODE__
     /* 发送到事件队列 */
     if (g_connection_manager.event_queue != NULL) {
+        /* 注意:队列中存储的是事件指针,接收方需要负责释放内存 */
         osMessageQueuePut(g_connection_manager.event_queue, &event, 0, 0);
     }
 #endif
+
+    /* 直接调用回调函数 */
+    if (g_connection_manager.config.event_callback != NULL) {
+        g_connection_manager.config.event_callback(event, g_connection_manager.config.user_data);
+    }
     
+    /* 🆕 如果没有队列或回调函数,需要释放事件内存 */
+#ifdef __TEST_MODE__
+    ai_event_destroy(event);
+#else
+    if (g_connection_manager.event_queue == NULL && g_connection_manager.config.event_callback == NULL) {
+        ai_event_destroy(event);
+    }
+#endif
+}
+
+static void send_data_event(ai_event_type_t type, ai_service_type_t service, const char *data, size_t length, bool is_finished, bool is_final_fragment)
+{
+    if (!g_connection_manager.is_running) {
+        return;
+    }
+
+    /* 🆕 使用动态分配创建事件 */
+    ai_event_t *event = ai_event_create_data(type, service, (const uint8_t*)data, length, is_finished, is_final_fragment);
+    if (event == NULL) {
+        AI_LOGE("Failed to create data event");
+        return;
+    }
+
+    /* 设置会话ID(如果需要) */
+    // TODO: 根据服务类型设置会话ID
+    
+#ifndef __TEST_MODE__
+    /* 发送到事件队列 */
+    if (g_connection_manager.event_queue != NULL) {
+        /* 注意:队列中存储的是事件指针,接收方需要负责释放内存 */
+        osMessageQueuePut(g_connection_manager.event_queue, &event, 0, 0);
+    }
+#endif
+
     /* 直接调用回调函数 */
     if (g_connection_manager.config.event_callback != NULL) {
-        g_connection_manager.config.event_callback(&event, g_connection_manager.config.user_data);
+        g_connection_manager.config.event_callback(event, g_connection_manager.config.user_data);
+    }
+    
+    /* 🆕 如果没有队列或回调函数,需要释放事件内存 */
+#ifdef __TEST_MODE__
+    ai_event_destroy(event);
+#else
+    if (g_connection_manager.event_queue == NULL && g_connection_manager.config.event_callback == NULL) {
+        ai_event_destroy(event);
+    }
+#endif
+}
+/*
+ * 判断数据是否为json 最早的20字节是否为json
+ * @param data: 数据
+ * @param length: 数据长度
+ * @return: 是否为json
+ */
+static bool detect_json_data(const char *data, size_t length)
+{
+    if (data == NULL || length == 0) {
+        return false;
+    }
+
+    // 只检查最早的20字节以判定是否为json
+    size_t i = 0;
+    size_t max_check = length < 20 ? length : 20;
+    // 跳过前导空白字符,查找第一个非空字符
+    while (i < max_check && (data[i] == ' ' || data[i] == '\t' || data[i] == '\r' || data[i] == '\n')) {
+        i++;
+    }
+    if (i >= max_check || data[i] != '{') {
+        return false;
     }
+    // 找到第一个非空字符为 '{',继续查找下一个非空字符
+    i++;
+    while (i < max_check && (data[i] == ' ' || data[i] == '\t' || data[i] == '\r' || data[i] == '\n')) {
+        i++;
+    }
+    if (i < max_check && data[i] == '"') {
+        return true;
+    }
+    return false;
+}
+
+static void handle_fragmented_message(ai_connection_info_t *conn_info, const char *data, size_t length, ai_event_type_t event_type)
+{
+    // 在实际应用中,这里需要将分片数据拼接起来,直到收到 isFinished = true
+    // 对于简单的测试,我们可以直接发送一个带有部分数据的虚拟事件
+    // 在实际场景中,需要一个缓冲区来存储分片数据
+    AI_LOGI("Received fragmented message for service %s, type=%s, length=%lu", 
+           g_service_names[conn_info->service_type], 
+           event_type == AI_EVENT_TEXT_DATA_RECEIVED ? "JSON" : "BINARY", (unsigned long)length);
+
+    // 在测试模式下,我们直接发送一个带有部分数据的虚拟事件
+    // 实际应用中,这里需要将分片数据拼接起来
+    // 例如:
+    // static char *fragmented_data = NULL;
+    // static size_t fragmented_length = 0;
+    // fragmented_length += length;
+    // fragmented_data = (char*)realloc(fragmented_data, fragmented_length);
+    // if (fragmented_data != NULL) {
+    //     memcpy(fragmented_data + fragmented_length - length, data, length);
+    // }
+    // if (isFinished) {
+    //     send_data_event(event_type, conn_info->service_type, fragmented_data, fragmented_length, true, true);
+    //     free(fragmented_data);
+    //     fragmented_data = NULL;
+    //     fragmented_length = 0;
+    // }
 }
 
 static uint32_t get_current_timestamp(void)
@@ -1153,7 +1458,8 @@ static void heartbeat_timer_callback(void *argument)
                 
                 /* 发送心跳包 */
                 const char *heartbeat = "{\"type\":\"heartbeat\"}";
-                ai_connection_manager_send_data((ai_service_type_t)i, heartbeat, strlen(heartbeat));
+                printf("send dumy heartbeat: %s\n", heartbeat);
+                // ai_connection_manager_send_data((ai_service_type_t)i, heartbeat, strlen(heartbeat));
             }
         }
     }
diff --git a/code/sdk/zhihui/ai/src/ai_event_manager.c b/code/sdk/zhihui/ai/src/ai_event_manager.c
new file mode 100644 (file)
index 0000000..85f754f
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * @file ai_event_manager.c
+ * @brief AI事件管理器实现
+ * @author AI Assistant
+ * @date 2024
+ */
+
+#include "ai_core_types.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __TEST_MODE__
+#include "wearable_log.h"
+#define AI_LOGI(fmt, ...) WEARABLE_LOGI("AI_EVENT", fmt, ##__VA_ARGS__)
+#define AI_LOGW(fmt, ...) WEARABLE_LOGW("AI_EVENT", fmt, ##__VA_ARGS__)
+#define AI_LOGE(fmt, ...) WEARABLE_LOGE("AI_EVENT", fmt, ##__VA_ARGS__)
+#else
+#include <stdio.h>
+#define AI_LOGI(fmt, ...) printf("[AI_EVENT] " fmt "\n", ##__VA_ARGS__)
+#define AI_LOGW(fmt, ...) printf("[AI_EVENT] " fmt "\n", ##__VA_ARGS__)
+#define AI_LOGE(fmt, ...) printf("[AI_EVENT] " fmt "\n", ##__VA_ARGS__)
+#endif
+
+/* ================================ 内部函数 ================================ */
+
+/**
+ * @brief 获取当前时间戳
+ */
+static uint32_t get_current_timestamp(void)
+{
+#ifndef __TEST_MODE__
+    /* 在实际系统中使用系统时间函数 */
+    extern uint32_t get_current_timestamp(void);
+    static uint32_t counter = 0;
+    return ++counter; // 临时使用计数器,避免链接错误
+#else
+    /* 测试模式下使用简单计数器 */
+    static uint32_t counter = 0;
+    return ++counter;
+#endif
+}
+
+/* ================================ 公共函数实现 ================================ */
+
+ai_event_t* ai_event_create_data(ai_event_type_t type, ai_service_type_t service, 
+                                const uint8_t *data, size_t length, 
+                                bool is_finished, bool is_final_fragment)
+{
+    /* 参数验证 */
+    if (data == NULL && length > 0) {
+        AI_LOGE("Invalid parameters: data is NULL but length > 0");
+        return NULL;
+    }
+    
+    if (length > AI_MAX_EVENT_DATA_SIZE) {
+        AI_LOGE("Data length %lu exceeds maximum %d", (unsigned long)length, AI_MAX_EVENT_DATA_SIZE);
+        return NULL;
+    }
+    
+    /* 分配事件结构体 */
+    ai_event_t *event = (ai_event_t*)malloc(sizeof(ai_event_t));
+    if (event == NULL) {
+        AI_LOGE("Failed to allocate memory for event");
+        return NULL;
+    }
+    
+    /* 初始化事件 */
+    memset(event, 0, sizeof(ai_event_t));
+    event->type = type;
+    event->service = service;
+    event->timestamp = get_current_timestamp();
+    
+    /* 分配数据内存 */
+    if (length > 0) {
+        event->payload.data.data = (uint8_t*)malloc(length);
+        if (event->payload.data.data == NULL) {
+            AI_LOGE("Failed to allocate %lu bytes for event data", (unsigned long)length);
+            free(event);
+            return NULL;
+        }
+        
+        /* 复制数据 */
+        memcpy(event->payload.data.data, data, length);
+        event->payload.data.length = length;
+        event->payload.data.allocated_size = length;
+    } else {
+        event->payload.data.data = NULL;
+        event->payload.data.length = 0;
+        event->payload.data.allocated_size = 0;
+    }
+    
+    event->payload.data.is_finished = is_finished;
+    event->payload.data.is_final_fragment = is_final_fragment;
+    
+    AI_LOGI("Created data event: type=%d, service=%d, length=%lu", type, service, (unsigned long)length);
+    return event;
+}
+
+ai_event_t* ai_event_create_control(ai_event_type_t type, ai_service_type_t service)
+{
+    /* 分配事件结构体 */
+    ai_event_t *event = (ai_event_t*)malloc(sizeof(ai_event_t));
+    if (event == NULL) {
+        AI_LOGE("Failed to allocate memory for control event");
+        return NULL;
+    }
+    
+    /* 初始化事件 */
+    memset(event, 0, sizeof(ai_event_t));
+    event->type = type;
+    event->service = service;
+    event->timestamp = get_current_timestamp();
+    
+    AI_LOGI("Created control event: type=%d, service=%d", type, service);
+    return event;
+}
+
+void ai_event_destroy(ai_event_t *event)
+{
+    if (event == NULL) {
+        return;
+    }
+    
+    /* 释放数据内存 */
+    if ((event->type == AI_EVENT_TEXT_DATA_RECEIVED || event->type == AI_EVENT_BIN_DATA_RECEIVED) &&
+        event->payload.data.data != NULL) {
+        AI_LOGI("Freeing event data: %lu bytes", (unsigned long)event->payload.data.allocated_size);
+        free(event->payload.data.data);
+        event->payload.data.data = NULL;
+        event->payload.data.length = 0;
+        event->payload.data.allocated_size = 0;
+    }
+    
+    /* 释放事件结构体 */
+    free(event);
+}
+
+ai_event_t* ai_event_copy(const ai_event_t *src)
+{
+    if (src == NULL) {
+        return NULL;
+    }
+    
+    /* 分配新事件 */
+    ai_event_t *dst = (ai_event_t*)malloc(sizeof(ai_event_t));
+    if (dst == NULL) {
+        AI_LOGE("Failed to allocate memory for event copy");
+        return NULL;
+    }
+    
+    /* 复制基本信息 */
+    memcpy(dst, src, sizeof(ai_event_t));
+    
+    /* 深拷贝数据部分 */
+    if ((src->type == AI_EVENT_TEXT_DATA_RECEIVED || src->type == AI_EVENT_BIN_DATA_RECEIVED) &&
+        src->payload.data.data != NULL && src->payload.data.length > 0) {
+        
+        dst->payload.data.data = (uint8_t*)malloc(src->payload.data.length);
+        if (dst->payload.data.data == NULL) {
+            AI_LOGE("Failed to allocate memory for event data copy");
+            free(dst);
+            return NULL;
+        }
+        
+        memcpy(dst->payload.data.data, src->payload.data.data, src->payload.data.length);
+        dst->payload.data.allocated_size = src->payload.data.length;
+        
+        AI_LOGI("Copied event with %lu bytes of data", (unsigned long)src->payload.data.length);
+    } else {
+        dst->payload.data.data = NULL;
+        dst->payload.data.length = 0;
+        dst->payload.data.allocated_size = 0;
+    }
+    
+    return dst;
+} 
\ No newline at end of file
index 8f4139f..263466a 100644 (file)
@@ -11,6 +11,7 @@
 #include "ai_tts_service.h"
 #include "ai_connection_manager.h"
 #include "ai_memory_pool.h"
+#include "ai_media.h"
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #define AI_LOGW(fmt, ...) printf("[AI_SERVICES] WARN: " fmt "\n", ##__VA_ARGS__)
 #endif
 
+/* ================================ 全局变量 ================================ */
+
+/* 音频服务状态 */
+static bool g_asr_recording_enabled = false;  // ASR录音服务启动标识
+static ai_services_context_t *g_services_context = NULL;  // 全局服务上下文
+
 /* ================================ 内部函数声明 ================================ */
 
 static uint32_t get_current_timestamp(void);
-static void connection_event_handler(const ai_event_t *event, void *user_data);
+static void audio_record_callback(uint8_t *data, uint32_t size);
+void connection_event_handler(const ai_event_t *event, void *user_data);
 
 /* ================================ 公共接口实现 ================================ */
 
@@ -53,6 +61,9 @@ ai_error_t ai_services_init(ai_services_context_t *context)
     /* 初始化上下文 */
     memset(context, 0, sizeof(ai_services_context_t));
     
+    /* 设置全局上下文指针 */
+    g_services_context = context;
+    
     /* 初始化各个服务 */
     ai_error_t ret = ai_asr_service_init(&context->asr_context);
     if (ret != AI_OK) {
@@ -75,13 +86,35 @@ ai_error_t ai_services_init(ai_services_context_t *context)
         return ret;
     }
     
+    /* 🆕 初始化音频服务 */
+    AI_LOGI("Initializing audio media service...");
+    int32_t media_ret = ai_media_service_init();
+    if (media_ret != 0) {
+        AI_LOGE("Failed to initialize media service: %d", media_ret);
+        ai_asr_service_destroy(&context->asr_context);
+        ai_chat_service_destroy(&context->chat_context);
+        ai_tts_service_destroy(&context->tts_context);
+        return AI_ERROR_MEDIA_INIT;
+    }
+    
+    /* 🆕 设置录音回调 */
+    ai_media_set_record_callback(audio_record_callback);
+    
+    /* 🆕 启动录音服务 */
+    AI_LOGI("Starting audio recording...");
+    ai_media_record_start(&context->asr_context.config.format);  // 使用PCM格式
+    
+    /* 🆕 启动播放服务 */
+    AI_LOGI("Starting audio playback...");
+    ai_media_play_start(&context->tts_context.config.format);  // 使用PCM格式
+    
     /* 生成统一会话ID */
     ai_services_generate_session_id(context->current_session_id, sizeof(context->current_session_id));
     
     context->last_activity_time = get_current_timestamp();
     context->is_initialized = true;
     
-    AI_LOGI("AI services initialized successfully");
+    AI_LOGI("AI services initialized successfully with audio support");
     return AI_OK;
 }
 
@@ -97,11 +130,21 @@ ai_error_t ai_services_destroy(ai_services_context_t *context)
     
     AI_LOGI("Destroying AI services...");
     
+    /* 🆕 停止音频服务 */
+    AI_LOGI("Stopping audio services...");
+    ai_media_record_stop();
+    ai_media_play_stop();
+    ai_media_service_deinit();
+    
     /* 销毁各个服务 */
     ai_asr_service_destroy(&context->asr_context);
     ai_chat_service_destroy(&context->chat_context);
     ai_tts_service_destroy(&context->tts_context);
     
+    /* 重置全局上下文指针 */
+    g_services_context = NULL;
+    g_asr_recording_enabled = false;
+    
     /* 重置上下文 */
     memset(context, 0, sizeof(ai_services_context_t));
     
@@ -154,7 +197,14 @@ ai_error_t ai_asr_start(ai_services_context_t *context, const char *session_id)
     /* 使用统一会话ID或提供的会话ID */
     const char *use_session_id = session_id ? session_id : context->current_session_id;
     
-    return ai_asr_service_start(&context->asr_context, use_session_id);
+    ai_error_t ret = ai_asr_service_start(&context->asr_context, use_session_id);
+    if (ret == AI_OK) {
+        /* 🆕 启动ASR服务成功后,开启录音数据发送 */
+        g_asr_recording_enabled = true;
+        AI_LOGI("ASR recording enabled");
+    }
+    
+    return ret;
 }
 
 ai_error_t ai_asr_send_audio(ai_services_context_t *context, const uint8_t *audio_data, uint32_t data_size)
@@ -180,6 +230,10 @@ ai_error_t ai_asr_stop(ai_services_context_t *context)
         return AI_ERROR_NOT_INITIALIZED;
     }
     
+    /* 🆕 停止ASR服务前,关闭录音数据发送 */
+    g_asr_recording_enabled = false;
+    AI_LOGI("ASR recording disabled");
+    
     return ai_asr_service_stop(&context->asr_context);
 }
 
@@ -372,6 +426,49 @@ const char* ai_services_get_state_string(ai_service_type_t service_type, ai_serv
     }
 }
 
+/* ================================ 音频服务控制接口 ================================ */
+
+/**
+ * @brief 启用ASR录音数据发送
+ * @param context 服务上下文
+ * @return ai_error_t 错误码
+ */
+ai_error_t ai_services_enable_asr_recording(ai_services_context_t *context)
+{
+    if (context == NULL || !context->is_initialized) {
+        return AI_ERROR_NOT_INITIALIZED;
+    }
+    
+    g_asr_recording_enabled = true;
+    AI_LOGI("ASR recording enabled manually");
+    return AI_OK;
+}
+
+/**
+ * @brief 禁用ASR录音数据发送
+ * @param context 服务上下文
+ * @return ai_error_t 错误码
+ */
+ai_error_t ai_services_disable_asr_recording(ai_services_context_t *context)
+{
+    if (context == NULL || !context->is_initialized) {
+        return AI_ERROR_NOT_INITIALIZED;
+    }
+    
+    g_asr_recording_enabled = false;
+    AI_LOGI("ASR recording disabled manually");
+    return AI_OK;
+}
+
+/**
+ * @brief 获取ASR录音状态
+ * @return bool 录音状态
+ */
+bool ai_services_is_asr_recording_enabled(void)
+{
+    return g_asr_recording_enabled;
+}
+
 /* ================================ 内部函数实现 ================================ */
 
 static uint32_t get_current_timestamp(void)
@@ -383,7 +480,27 @@ static uint32_t get_current_timestamp(void)
 #endif
 }
 
-static void connection_event_handler(const ai_event_t *event, void *user_data)
+static void audio_record_callback(uint8_t *data, uint32_t size)
+{
+    if (g_services_context == NULL || !g_services_context->is_initialized) {
+        AI_LOGE("Services context not initialized");
+        return;
+    }
+    AI_LOGI("Audio record callback, size: %d", size);
+    
+    /* 🆕 根据ASR服务启动标识决定是否发送音频数据 */
+    if (g_asr_recording_enabled) {
+        AI_LOGI("Sending %u bytes audio data to ASR service", size);
+        
+        /* 通过连接管理器发送原始音频数据到ASR服务 */
+        ai_error_t ret = ai_connection_manager_send_rawdata(AI_SERVICE_ASR, (const void*)data, (size_t)size);
+        if (ret != AI_OK) {
+            AI_LOGW("Failed to send audio data to ASR service: %d", ret);
+        }
+    }
+}
+
+void connection_event_handler(const ai_event_t *event, void *user_data)
 {
     if (event == NULL) {
         return;
@@ -394,25 +511,77 @@ static void connection_event_handler(const ai_event_t *event, void *user_data)
         return;
     }
     
-    AI_LOGI("Connection event: service=%d, type=%d", event->service, event->type);
+    AI_LOGI("Connection event: service=%d, type=%d, data_length=%lu", 
+           event->service, event->type, (unsigned long)event->payload.data.length);
     
     /* 根据事件类型和服务类型处理响应 */
     switch (event->service) {
         case AI_SERVICE_ASR:
-            if (event->type == AI_EVENT_DATA_RECEIVED && event->payload.data.data != NULL) {
-                ai_asr_service_handle_response(&context->asr_context, (const char*)event->payload.data.data);
+            if (event->type == AI_EVENT_TEXT_DATA_RECEIVED && event->payload.data.length > 0) {
+                /* ASR服务只处理JSON文本数据 */
+                char *json_str = (char*)malloc(event->payload.data.length + 1);
+                if (json_str != NULL) {
+                    memcpy(json_str, event->payload.data.data, event->payload.data.length);
+                    json_str[event->payload.data.length] = '\0';
+                    ai_asr_service_handle_response(&context->asr_context, json_str);
+                    free(json_str);
+                }
+            } else if (event->type == AI_EVENT_BIN_DATA_RECEIVED) {
+                AI_LOGW("ASR service received binary data, ignoring");
             }
             break;
             
         case AI_SERVICE_CHAT:
-            if (event->type == AI_EVENT_DATA_RECEIVED && event->payload.data.data != NULL) {
-                ai_chat_service_handle_response(&context->chat_context, (const char*)event->payload.data.data);
+            if (event->type == AI_EVENT_TEXT_DATA_RECEIVED && event->payload.data.length > 0) {
+                /* Chat服务只处理JSON文本数据 */
+                char *json_str = (char*)malloc(event->payload.data.length + 1);
+                if (json_str != NULL) {
+                    memcpy(json_str, event->payload.data.data, event->payload.data.length);
+                    json_str[event->payload.data.length] = '\0';
+                    ai_chat_service_handle_response(&context->chat_context, json_str);
+                    free(json_str);
+                }
+            } else if (event->type == AI_EVENT_BIN_DATA_RECEIVED) {
+                AI_LOGW("Chat service received binary data, ignoring");
             }
             break;
             
         case AI_SERVICE_TTS:
-            if (event->type == AI_EVENT_DATA_RECEIVED && event->payload.data.data != NULL) {
-                ai_tts_service_handle_response(&context->tts_context, (const char*)event->payload.data.data);
+            if (event->type == AI_EVENT_TEXT_DATA_RECEIVED && event->payload.data.length > 0) {
+                /* TTS服务处理JSON文本数据 */
+                char *json_str = (char*)malloc(event->payload.data.length + 1);
+                if (json_str != NULL) {
+                    memcpy(json_str, event->payload.data.data, event->payload.data.length);
+                    json_str[event->payload.data.length] = '\0';
+                    ai_tts_service_handle_response(&context->tts_context, json_str);
+                    free(json_str);
+                }
+            } else if (event->type == AI_EVENT_BIN_DATA_RECEIVED && event->payload.data.length > 0) {
+                /* 🆕 TTS服务处理二进制音频数据 - 根据播放状态决定是否直接播放 */
+                uint8_t play_status = ai_media_get_play_status();
+                if (play_status != 0) {  // 播放服务已启动
+                    AI_LOGI("Playing TTS audio data: %lu bytes", (unsigned long)event->payload.data.length);
+                    ai_media_play_buffer((uint8_t*)event->payload.data.data, (uint32_t)event->payload.data.length);
+                } else {
+                    AI_LOGW("Play service not ready, skipping TTS audio playback");
+                }
+                
+                /* 同时传递给TTS服务进行状态管理 */
+                ai_tts_service_handle_audio_data(&context->tts_context, 
+                                               event->payload.data.data,
+                                               event->payload.data.length,
+                                               event->payload.data.is_finished);
+            }
+            break;
+            
+        case AI_SERVICE_GLOBAL:
+            /* 🆕 处理全局服务事件(如系统状态变更) */
+            if (event->type == AI_EVENT_STATE_CHANGED) {
+                AI_LOGI("Global state changed: %d -> %d", 
+                       event->payload.state_change.old_state, 
+                       event->payload.state_change.new_state);
+            } else {
+                AI_LOGI("Global service event: type=%d", event->type);
             }
             break;
             
index 9c9cf8b..bf2fc91 100644 (file)
@@ -48,6 +48,28 @@ static ai_error_t parse_tts_response(const char *json_data, tts_result_t *result
 
 /* ================================ 公共接口实现 ================================ */
 
+/*
+ * @brief 初始化TTS服务URL
+ * @note 根据设备token初始化TTS服务URL
+ */
+bool tts_api_ws_url_init(char *url_buf,const char *server_url)
+{
+    // 测试用例
+    // tts_client = init_websocket_client("ws://118.145.158.207:22080/tts/stream?audio_buffer_size=512", NULL, 0);
+
+    char token[64]={0};
+    if(read_dev_token(token))
+    {
+        // /tts/stream?audio_buffer_size=1024
+        snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?dt=%s&platform=aliyun&noCache=1",server_url,token);
+    }
+    else
+    {
+        snprintf(url_buf,AI_MAX_URL_LENGTH,"%s?platform=aliyun&noCache=1",server_url);
+    }
+    return true;
+   
+}
 ai_error_t ai_tts_service_init(tts_service_context_t *context)
 {
     if (context == NULL) {
@@ -243,6 +265,7 @@ void ai_tts_service_create_default_config(tts_config_t *config)
     config->pitch = 50;                     // 正常音调
     config->auto_play = true;
     config->timeout_ms = 10000;             // 10秒超时
+    strncpy(config->format, "mp3", sizeof(config->format) - 1);
 }
 
 ai_error_t ai_tts_service_handle_response(tts_service_context_t *context, const char *json_data)
@@ -287,6 +310,41 @@ ai_error_t ai_tts_service_handle_response(tts_service_context_t *context, const
     return AI_OK;
 }
 
+ai_error_t ai_tts_service_handle_audio_data(tts_service_context_t *context,
+                                          const uint8_t *audio_data,
+                                          size_t length,
+                                          bool is_finished)
+{
+    if (context == NULL || audio_data == NULL || length == 0) {
+        return AI_ERROR_INVALID_PARAM;
+    }
+    
+    AI_LOGI("TTS audio data received: %u bytes, finished=%s", 
+           length, is_finished ? "true" : "false");
+    
+    /* 构造TTS音频结果 */
+    tts_result_t result = {0};
+    strncpy(result.session_id, context->current_session_id, sizeof(result.session_id) - 1);
+    result.audio_data = audio_data;
+    result.audio_length = length;
+    result.is_final = is_finished;
+    result.timestamp = get_current_timestamp();
+    
+    /* 更新状态 */
+    if (is_finished) {
+        change_tts_state(context, TTS_STATE_COMPLETED);
+    } else {
+        change_tts_state(context, TTS_STATE_PLAYING);
+    }
+    
+    /* 触发音频数据回调 */
+    if (context->result_cb != NULL) {
+        context->result_cb(&result, context->user_data);
+    }
+    
+    return AI_OK;
+}
+
 ai_error_t ai_tts_service_build_request(const char *text, const char *session_id, const tts_config_t *config,
                                        char *json_buffer, size_t buffer_size)
 {
index ee3b1fb..0a32d07 100644 (file)
@@ -36,21 +36,21 @@ void ase_encrypt_demo(void);
 // #define free free
 // static char * dev_key = "uKLrChwrxA1LklgBc3VwawYjUkbT3BgX" ;
 // static char * dev_pid = "cqxJZi5CNgzjS3mX21Zw";
-int get_dev_pass_MAX_len(void)//获取设备密码最大长度,注意要防止内存越界
-{
+// int get_dev_pass_MAX_len(void)//获取设备密码最大长度,注意要防止内存越界
+// {
     // return sizeof(vc_cfg.dev_pass);
-}
+// }
 void save_dev_pass(char *dev_pass)//保存设备密码,dev_pass字符串
 {
     // memset(vc_cfg.dev_pass,0,sizeof(vc_cfg.dev_pass));
     // strncpy(vc_cfg.dev_pass,dev_pass,get_dev_pass_MAX_len());
     // save_VC_cfg();
 }
-char *get_dev_pass(void)//获取设备密码,字符串的方式,返回字符串长度为0,即没密码
-{
+// char *get_dev_pass(void)//获取设备密码,字符串的方式,返回字符串长度为0,即没密码
+// {
 
     // return vc_cfg.dev_pass;
-}
+// }
 
 static char aut_token[50] ={0};
 int str_count(char *data,char type)
@@ -473,6 +473,17 @@ extern bool read_dev_pass(char *data_pass);
 extern uint32_t post_data_by_get_file(char *data,uint16_t data_len);
 bool read_dev_token(char *data_token);
 
+void get_dev_id(char *device_id_str)
+{
+    if(device_id_str==NULL)
+    {
+        return;
+    }
+    unsigned char device_id[6] = {0};
+    bluetooth_get_local_addr(&device_id[0],6);
+    sprintf(device_id_str,"%02X%02x%02X%02X%02X%02x",device_id[5],device_id[4],device_id[3],device_id[2],device_id[1],device_id[0]);
+}
+
 uint32_t aut_login_time=0;
 
 bool aut_login(void)//登录
@@ -827,6 +838,9 @@ bool pre_AI_init(void)
     if(read_dev_token(NULL)==false)
     {
         return aut_login();
+    }else
+    {
+        return true;
     }
 }
 
index 0d08579..baf5577 100644 (file)
@@ -291,7 +291,7 @@ int ws_parse_byte(ws_parser_t* parser, uint8_t byte) {
             // 累加长度数据
             static uint8_t len_bytes[8];
             
-            WS_LOGD("WS: Reading payload length byte: 0x%02X, remaining: %zu", 
+            WS_LOGD("WS: Reading payload length byte: 0x%02X, remaining: %u", 
                           byte, parser->payload_remaining);
 
             if (parser->header.payload_len == 126) {
@@ -330,7 +330,7 @@ int ws_parse_byte(ws_parser_t* parser, uint8_t byte) {
                     
                     // 检查缓存是否足够
                     if (parser->buffer_pos + parser->header.payload_len > WS_BUFFER_SIZE) {
-                        WS_LOGE("WS: Buffer overflow: current=%zu, adding=%d, max=%d", 
+                        WS_LOGE("WS: Buffer overflow: current=%u, adding=%d, max=%d", 
                             parser->buffer_pos, parser->header.payload_len, WS_BUFFER_SIZE);
                         parser->state = WS_STATE_ERROR;
                         return -1;
@@ -385,7 +385,7 @@ int ws_parse_byte(ws_parser_t* parser, uint8_t byte) {
                     
                     // 检查缓存是否足够
                     if (parser->buffer_pos + parser->header.payload_len > WS_BUFFER_SIZE) {
-                        WS_LOGE("WS: Buffer overflow: current=%zu, adding=%llu, max=%d", 
+                        WS_LOGE("WS: Buffer overflow: current=%u, adding=%llu, max=%d", 
                             parser->buffer_pos, (unsigned long long)parser->header.payload_len, WS_BUFFER_SIZE);
                         parser->state = WS_STATE_ERROR;
                         return -1;
@@ -447,7 +447,7 @@ int ws_parse_byte(ws_parser_t* parser, uint8_t byte) {
                 
                 if (parser->payload_remaining == 0) {
                     // 负载读取完成
-                    WS_LOGI("WS: Payload read complete (%zu bytes)", parser->header.payload_len);
+                    WS_LOGI("WS: Payload read complete (%u bytes)", parser->header.payload_len);
                     
                     // 如果有掩码,应用掩码解码
                     if (parser->header.mask) {
@@ -493,7 +493,7 @@ int ws_parse_stream(ws_parser_t* parser, const uint8_t* data, size_t len) {
         return -1;
     }
     
-    WS_LOGI("WS: Parsing stream data (%zu bytes), current state: %d, buffer_pos: %zu", 
+    WS_LOGI("WS: Parsing stream data (%u bytes), current state: %d, buffer_pos: %u", 
             len, parser->state, parser->buffer_pos);
     
     // 如果解析器已经完成一个帧,重置状态
@@ -510,12 +510,12 @@ int ws_parse_stream(ws_parser_t* parser, const uint8_t* data, size_t len) {
         
         if (result == 1) {
             // 帧解析完成
-            WS_LOGI("WS: Complete frame parsed (%zu bytes total) opcode: %d, payload_len: %llu", 
+            WS_LOGI("WS: Complete frame parsed (%u bytes total) opcode: %d, payload_len: %llu", 
                     parser->buffer_pos, parser->header.opcode, (unsigned long long)parser->header.payload_len);
             break;
         } else if (result == -1) {
             // 解析错误
-            WS_LOGE("WS: Parsing error at byte %zu", i);
+            WS_LOGE("WS: Parsing error at byte %u", i);
             ws_parser_reset(parser);
             break; // 让上层根据 total_processed 前进
         }
@@ -528,7 +528,7 @@ int ws_parse_stream(ws_parser_t* parser, const uint8_t* data, size_t len) {
         WS_LOGI(" WS_STATE_ERROR at %d\n",total_processed);
     } else {
         // 数据不完整,继续等待更多数据
-        WS_LOGI("WS: Frame parsing incomplete, waiting for more data. State: %d, Buffer: %zu/%d, Payload remaining: %zu", 
+        WS_LOGI("WS: Frame parsing incomplete, waiting for more data. State: %d, Buffer: %u/%d, Payload remaining: %u", 
                 parser->state, parser->buffer_pos, WS_BUFFER_SIZE, parser->payload_remaining);
     }
     return total_processed;
@@ -574,7 +574,7 @@ const uint8_t* ws_get_payload(ws_parser_t* parser) {
     
     // 检查载荷数据是否在缓存范围内
     if (header_size + parser->header.payload_len > parser->buffer_pos) {
-        WS_LOGE("WS: Payload start position %zu exceeds buffer size %zu", header_size, parser->buffer_pos);
+        WS_LOGE("WS: Payload start position %u exceeds buffer size %u", header_size, parser->buffer_pos);
         return NULL;
     }
     
index bdfc33c..e81ad92 100644 (file)
@@ -461,11 +461,11 @@ static int zhihui_ws_send_frame(zhihui_websocket_client_t *client, ws_opcode_t o
     // 发送帧头
     ssize_t header_sent = lwip_send(client->socket_fd, header, header_len, 0);
     if (header_sent != (ssize_t)header_len) {
-        ZHIHUI_WS_LOGE("Failed to send frame header: sent=%d, expected=%zu, errno=%d", 
+        ZHIHUI_WS_LOGE("Failed to send frame header: sent=%d, expected=%u, errno=%d", 
                        (int)header_sent, header_len, errno);
         return WEBSOCKET_ERROR_READ_WRITE_SOCKET;
     }
-    ZHIHUI_WS_LOGD("Sent frame header: %zu bytes", header_len);
+    ZHIHUI_WS_LOGD("Sent frame header: %u bytes", header_len);
     
     // 发送负载数据(需要mask)
     if (len > 0 && data) {
@@ -482,11 +482,11 @@ static int zhihui_ws_send_frame(zhihui_websocket_client_t *client, ws_opcode_t o
         free(masked_data);
         
         if (sent != (ssize_t)len) {
-            ZHIHUI_WS_LOGE("Failed to send frame payload: sent=%d, expected=%zu, errno=%d", 
+            ZHIHUI_WS_LOGE("Failed to send frame payload: sent=%d, expected=%u, errno=%d", 
                            (int)sent, len, errno);
             return WEBSOCKET_ERROR_READ_WRITE_SOCKET;
         }
-        ZHIHUI_WS_LOGD("Sent frame payload: %zu bytes", len);
+        ZHIHUI_WS_LOGD("Sent frame payload: %u bytes", len);
     }
     
     return WEBSOCKET_OK;
@@ -748,7 +748,7 @@ int zhihui_WebSocketClientSendBinStart(int client, const char *data, size_t leng
         return WEBSOCKET_NO_CONNECTION;
     }
     
-    ZHIHUI_WS_LOGD("SendBinStart: Client %d sending %zu bytes", client, length);
+    ZHIHUI_WS_LOGD("SendBinStart: Client %d sending %u bytes", client, length);
     int result = zhihui_ws_send_frame(ws_client, WS_BINARY, (const uint8_t*)data, length, true); // 改为true,表示完整帧
     ZHIHUI_WS_LOGD("SendBinStart: Client %d send result=%d", client, result);
     return result;
index 64bf6d3..a9830bd 100644 (file)
@@ -189,7 +189,7 @@ int ws_multi_test_init(void)
             g_test_manager.config.use_dns = true;
             strcpy(g_test_manager.config.server_url, "127.0.0.1");
             g_test_manager.config.server_port = 8080;
-            WS_TEST_LOGI("Configuration restored: data_size=%zu", g_test_manager.config.data_size);
+            WS_TEST_LOGI("Configuration restored: data_size=%u", g_test_manager.config.data_size);
         }
         return 0;
     }
@@ -817,7 +817,7 @@ static int ws_test_send_data_for_client(ws_test_client_t *client)
     // 分配发送缓冲区
     uint8_t *send_buffer = malloc(g_test_manager.config.data_size);
     if (send_buffer == NULL) {
-        WS_TEST_LOGE("Failed to allocate send buffer for client %d (size: %zu bytes)", 
+        WS_TEST_LOGE("Failed to allocate send buffer for client %d (size: %u bytes)", 
                      client->client_id, g_test_manager.config.data_size);
         return -1;
     }
@@ -827,7 +827,7 @@ static int ws_test_send_data_for_client(ws_test_client_t *client)
     
     // 发送二进制数据
     if (client->stats.sent_packets % 10 == 0 || client->stats.sent_packets < 5) { // 前5个包和每10个包打印详细日志
-        WS_TEST_LOGI("Client %d sending packet #%d, data_value=0x%02X, size=%zu bytes", 
+        WS_TEST_LOGI("Client %d sending packet #%d, data_value=0x%02X, size=%u bytes", 
                      client->client_id, client->stats.sent_packets, client->current_data_value, 
                      g_test_manager.config.data_size);
     }
@@ -1086,14 +1086,14 @@ static void ws_test_on_message_callback(int client, const char *data, size_t len
     ws_test_client_t *test_client = ws_test_find_client_by_fd(client);
     if (test_client) {
         if (test_client->stats.recv_packets % 100 == 0) { // 每100个包打印一次详细日志
-            WS_TEST_LOGI("WebSocket onMessage callback: client_fd=%d, client_id=%d, length=%zu, isFinished=%d", 
+            WS_TEST_LOGI("WebSocket onMessage callback: client_fd=%d, client_id=%d, length=%u, isFinished=%d", 
                          client, test_client->client_id, length, isFinished);
         }
         
         if (isFinished) {
             // 检查消息大小
             if (length > WS_TEST_MSG_MAX_SIZE) {
-                WS_TEST_LOGW("Received message too large for client %d: %zu bytes (max: %d)", 
+                WS_TEST_LOGW("Received message too large for client %d: %u bytes (max: %d)", 
                              test_client->client_id, length, WS_TEST_MSG_MAX_SIZE);
                 return;
             }
@@ -1109,7 +1109,7 @@ static void ws_test_on_message_callback(int client, const char *data, size_t len
             if (length > 0 && data) {
                 msg.data = malloc(length);
                 if (msg.data == NULL) {
-                    WS_TEST_LOGE("Failed to allocate memory for message data, client %d, size=%zu", 
+                    WS_TEST_LOGE("Failed to allocate memory for message data, client %d, size=%u", 
                                  test_client->client_id, length);
                     return;
                 }
@@ -1119,7 +1119,7 @@ static void ws_test_on_message_callback(int client, const char *data, size_t len
             }
             
             if (test_client->stats.recv_packets % 100 == 0) { // 每100个包打印一次详细日志
-                WS_TEST_LOGI("Client %d received packet #%d, length=%zu, first_byte=0x%02X", 
+                WS_TEST_LOGI("Client %d received packet #%d, length=%u, first_byte=0x%02X", 
                              test_client->client_id, test_client->stats.recv_packets, length, 
                              length > 0 ? (uint8_t)data[0] : 0);
             }
@@ -1474,7 +1474,7 @@ static void ws_test_cleanup_all_resources(void)
     strcpy(g_test_manager.config.server_url, "127.0.0.1");
     g_test_manager.config.server_port = 8080;
     
-    WS_TEST_LOGD("Configuration reset to defaults: data_size=%zu, client_count=%d", 
+    WS_TEST_LOGD("Configuration reset to defaults: data_size=%u, client_count=%d", 
                  g_test_manager.config.data_size, g_test_manager.config.client_count);
     
     uint32_t cleanup_time = osKernelGetTickCount() - cleanup_start;