From 7553fd2ad36bc740a8cfebc754969bd1972bb3ca Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E8=83=A1=E4=BC=A0=E5=86=9B?= <1224667075@qq.com> Date: Sat, 13 Sep 2025 10:48:28 +0000 Subject: [PATCH] Merge #2238 into sdk650pathc0423 from refs/changes/2238/head MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Code review from 胡传军 * refs/changes/2238/head: (2 commits squashed) - ai对话提交,有bug待调试,与ui协调有问题 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 Signed-off-by: aliyun8003852482 <1224667075@qq.com> Merged-by: 罗正 <1732036298@qq.com> CR-link: https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230/change/2238 --- .../nativeui/aivoice/src/AiVoiceModel.cpp | 54 +- .../zhihui/4g/cat1Serviccpp/cat1_UI_hal.cpp | 135 +++ code/sdk/zhihui/ai/include/ai_media.h | 31 + code/sdk/zhihui/ai/media/CMakeLists.txt | 3 + code/sdk/zhihui/ai/media/ai_media_service.cpp | 49 +- .../zhihui/ai/media/ai_media_service_ext.cpp | 944 +++++++++++++++ code/sdk/zhihui/ai_old/CMakeLists.txt | 2 + code/sdk/zhihui/ai_old/ai_old_at_cmd.cpp | 81 +- .../zhihui/ai_old/asr_websocket_server.cpp | 116 +- code/sdk/zhihui/ai_old/asr_websocket_server.h | 33 + code/sdk/zhihui/ai_old/bk_websocket.h | 2 +- .../zhihui/ai_old/chat_websocket_server.cpp | 252 +++- .../sdk/zhihui/ai_old/chat_websocket_server.h | 43 +- .../zhihui/ai_old/tts_websocket_server.cpp | 378 +++++- code/sdk/zhihui/ai_old/tts_websocket_server.h | 70 +- .../ai_old/websocket_server_example.cpp | 1034 +++++++++++++++++ .../ai_old/websocket_server_manager.cpp | 789 +++++++++++++ .../zhihui/ai_old/websocket_server_manager.h | 255 ++++ code/sdk/zhihui/ai_old/zhihui_ws.cpp | 370 +++++- code/sdk/zhihui/ai_old/zhihui_ws.h | 30 +- .../ble_service/blemoder/CMakeLists.txt | 1 + .../blemoder/ble_instruct_init.cpp | 4 + 22 files changed, 4564 insertions(+), 112 deletions(-) create mode 100644 code/sdk/zhihui/4g/cat1Serviccpp/cat1_UI_hal.cpp create mode 100644 code/sdk/zhihui/ai/media/ai_media_service_ext.cpp create mode 100644 code/sdk/zhihui/ai_old/websocket_server_example.cpp create mode 100644 code/sdk/zhihui/ai_old/websocket_server_manager.cpp create mode 100644 code/sdk/zhihui/ai_old/websocket_server_manager.h diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/aivoice/src/AiVoiceModel.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/aivoice/src/AiVoiceModel.cpp index 19e457eff..7f8bde1b1 100644 --- a/code/sdk/application/wearable/fd230/nativeapp/nativeui/aivoice/src/AiVoiceModel.cpp +++ b/code/sdk/application/wearable/fd230/nativeapp/nativeui/aivoice/src/AiVoiceModel.cpp @@ -186,7 +186,7 @@ namespace OHOS #if AI_VOICE_ENABLE uapi_UI_start_asr(GetCurUsing4GAiModelType()); #endif - } + } else { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "init_4gai_task fail!!"); @@ -534,14 +534,6 @@ namespace OHOS return; } userQuestionStr += str; - if (!entryTextViewState) - { - SendMsgToUIService(UI_AI_JUMP_TO_RESULT_VIEW, 0, nullptr, 0); - entryTextViewState = true; // 进入返回结果界面的标志 避免重复发消息 - usleep(50000); // 延时等待创建完成,0.05秒 - } - /* -------------------------------------------------------------------------- */ - SendMsgToUIService(UI_UPDATE_USER_QUESTION_STR, 0, nullptr, 0); } else if (GetSelect4gorbt() == AI_SELECT_BT) { @@ -552,9 +544,9 @@ namespace OHOS usleep(50000); // 延时等待创建完成,0.05秒 } /* -------------------------------------------------------------------------- */ - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "传入的问题:%s", str.c_str()); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "传入的问题:%s", str.c_str()); userQuestionStr = str; - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "拼接的问题:%s", userQuestionStr.c_str()); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "拼接的问题:%s", userQuestionStr.c_str()); /* -------------------------------------------------------------------------- */ SendMsgToUIService(UI_UPDATE_USER_QUESTION_STR, 0, nullptr, 0); } @@ -1062,7 +1054,7 @@ namespace OHOS { if (AiVoiceModel::GetInstance().is_need_to_jump_result_view()) { - SendMsgToUIService(UI_AI_JUMP_TO_RESULT_VIEW, 0, nullptr, 0); // 跳转到结果界面 + SendMsgToUIService(UI_AI_JUMP_TO_RESULT_VIEW, 0, nullptr, 0); } return; } @@ -1258,7 +1250,7 @@ namespace OHOS #if AI_VOICE_ENABLE uapi_UI_stop_asr(); #endif - AiVoicePresenter::GetInstance()->ChangeViewAndLogicOperator(AIVOICE_LISTENING_VIEWID, AIVOICE_THINKING_VIEWID); // 切换到思考中界面 + AiVoicePresenter::GetInstance()->ChangeViewAndLogicOperator(AIVOICE_LISTENING_VIEWID, AIVOICE_THINKING_VIEWID); // 切换到思考中界面 } } else @@ -1361,7 +1353,7 @@ namespace OHOS #if AI_VOICE_ENABLE state = get_asr_is_ready_start(); #endif - if (state) + if (state) { bool state = true; SendMsgToUIService(UI_AI_JUMP_TO_LISTENING_VIEW, 0, &state, sizeof(bool)); @@ -1656,6 +1648,9 @@ namespace OHOS } else { + // if(AiVoiceModel::GetInstance().GetUserQuestionStr() !=) + // SendMsgToUIService(UI_UPDATE_USER_QUESTION_STR, 0, nullptr, 0); + SendMsgToUIService(UI_UPDATE_AI_ANSWER_STR, 0, nullptr, 0); } } @@ -1834,7 +1829,7 @@ namespace OHOS chat_state = get_ai_chat_service_init_flag(); tts_state = get_ai_tts_server_init_flag(); #endif - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "asr_state:%d,chat_state:%d,tts_state:%d", asr_state, chat_state, tts_state); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "asr_state:%d,chat_state:%d,tts_state:%d", asr_state, chat_state, tts_state); if ((asr_state == 1) && (chat_state == 1) && (tts_state == 1)) { init_4gai_task = true; @@ -2008,6 +2003,35 @@ extern "C" clear_queue_stream = state; } + + #ifdef BLE_INSTRUCT + + #include "bleEvent.h" + #include "bleInstruct.h" + + void ble_set_ai_Question_str_(char *buf) + { + OHOS::AiVoiceModel::GetInstance().SetUserQuestionStr(std::string(buf)); + } + + + void ble_set_ai_Answer_str_(char *buf,uint8_t type) + { + OHOS::AiVoiceModel::GetInstance().SetAiAnswerStrStr(std::string(buf),type); + } + + extern bleInstruct_t_ bleInstruct_t; + + void ble_AI_init(void) + { + bleInstruct_t.ble_set_ai_Question_str= ble_set_ai_Question_str_; + + bleInstruct_t.ble_set_ai_Answer_str=ble_set_ai_Answer_str_; + } + + #endif + + #ifdef __cplusplus #if __cplusplus } diff --git a/code/sdk/zhihui/4g/cat1Serviccpp/cat1_UI_hal.cpp b/code/sdk/zhihui/4g/cat1Serviccpp/cat1_UI_hal.cpp new file mode 100644 index 000000000..e2f2217cf --- /dev/null +++ b/code/sdk/zhihui/4g/cat1Serviccpp/cat1_UI_hal.cpp @@ -0,0 +1,135 @@ +/* BSD Socket API Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include + +#include "wearable_log.h" +#include "securec.h" +#include "broadcast_service.h" +#include "broadcast_feature.h" +#include "service_id_define.h" +#include "samgr_lite.h" +#include "thread_adapter.h" +#include "graphic_service.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +#include "errcode.h" +#include "errno.h" + +#include "cat1_server_hal.h" +#include "custom_utils.h" + + +static errcode_t calldial_event_notify(char* callno) +{ + + CustomEventPublish(TOPIC_EVENT_CAT1_CALLDIAL,(uint8_t*)callno,strlen(callno)+1); + return 0; +} + + +static errcode_t incomingcall_event_notify(char* callno) +{ + CustomEventPublish(TOPIC_EVENT_CAT1_INCOMMING,(uint8_t*)callno,strlen(callno)+1); + return 0; +} + +static errcode_t incomed_call_event_notify(char* callno) +{ + CustomEventPublish(TOPIC_EVENT_CAT1_CALLCONNTED,(uint8_t*)callno,strlen(callno)+1); + return 0; +} + +static errcode_t callend_event_notify(char* callno) +{ + CustomEventPublish(TOPIC_EVENT_CAT1_CALLENDED,(uint8_t*)callno,strlen(callno)+1); + return 0; +} + +static errcode_t cat1msg_event_notify(char* callno) +{ + CustomEventPublish(TOPIC_EVENT_CAT1_SIGNAL_UPDATE,(uint8_t*)callno,strlen(callno)+1); + return 0; +} +//传地址应设置固定内存,否则异进程内存安全没法保证 + +// 将 std::string 改为 char 数组 +static char call_number_end[64] = {0}; +static char incomedcall_number_end[64] = {0}; +static char incomingcall_number[64] = {0}; +static char call_number[64] = {0}; + +int callend_event_sendmsg_toUI(char* callno) +{ + /**通过UI事件发送Notify*/ + strncpy(call_number_end, callno, sizeof(call_number_end) - 1); + call_number_end[sizeof(call_number_end) - 1] = '\0'; + + GraphicService::GetInstance()->PostGraphicEvent(std::bind(callend_event_notify, call_number_end)); + return ERRCODE_SUCC; +} + +int incomedcall_event_sendmsg_toUI(char* callno) +{ + /**通过UI事件发送Notify*/ + strncpy(incomedcall_number_end, callno, sizeof(incomedcall_number_end) - 1); + incomedcall_number_end[sizeof(incomedcall_number_end) - 1] = '\0'; + + GraphicService::GetInstance()->PostGraphicEvent(std::bind(incomed_call_event_notify, incomedcall_number_end)); + return ERRCODE_SUCC; +} + +int incomingcall_event_sendmsg_toUI(char* callno) +{ + /**通过UI事件发送Notify*/ + strncpy(incomingcall_number, callno, sizeof(incomingcall_number) - 1); + incomingcall_number[sizeof(incomingcall_number) - 1] = '\0'; + + GraphicService::GetInstance()->PostGraphicEvent(std::bind(incomingcall_event_notify, incomingcall_number)); + return ERRCODE_SUCC; +} + +int calldial_event_sendmsg_toUI(char* callno) +{ + /**通过UI事件发送Notify*/ + strncpy(call_number, callno, sizeof(call_number) - 1); + call_number[sizeof(call_number) - 1] = '\0'; + + // 移除空格 + char* p = call_number; + char* q = call_number; + while (*p) { + if (*p != ' ') { + *q++ = *p; + } + p++; + } + *q = '\0'; + + GraphicService::GetInstance()->PostGraphicEvent(std::bind(calldial_event_notify, call_number)); + return ERRCODE_SUCC; +} + +int cat1_msg_event_sendmsg_toUI(char* callno) +{ + /**通过UI事件发送Notify*/ + GraphicService::GetInstance()->PostGraphicEvent(std::bind(cat1msg_event_notify, callno)); + return ERRCODE_SUCC; +} + + +#ifdef __cplusplus + +} + +#endif /* __cplusplus */ diff --git a/code/sdk/zhihui/ai/include/ai_media.h b/code/sdk/zhihui/ai/include/ai_media.h index 1a466fe55..34f8a74ae 100644 --- a/code/sdk/zhihui/ai/include/ai_media.h +++ b/code/sdk/zhihui/ai/include/ai_media.h @@ -89,6 +89,37 @@ uint8_t ai_media_get_play_status(void); */ void ai_media_play_buffer(uint8_t *data, uint32_t size); +typedef void (*AIVoiceDataCallback)(const uint8_t* data, uint32_t size, void* userData); + +int32_t old_Start_AIVoice(AIVoiceDataCallback dataCallback, void* userData); + +/* + * @brief 写入音频数据到输出流 + * @param data 音频数据指针 + * @param size 音频数据大小 + * @return 0 成功,-1 失败 + */ +int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size); + +/* + * @brief 检查AI语音服务是否正在运行 + * @return true 正在运行,false 未运行 + */ +bool AI_IsVoiceServiceRunning(void); + +/* + * @brief 获取AI语音服务状态信息 + * @param isRunning 输出参数:是否正在运行 + * @param hasOutput 输出参数:是否有输出流 + * @return 0 成功,-1 失败 + */ +// int32_t AI_GetVoiceServiceStatus(bool* isRunning, bool* hasOutput); + +int32_t StopAIVoice(void); + + +int32_t AI_SendAudioDataToQueue(const uint8_t* data, uint32_t size); + #ifdef __cplusplus }; #endif diff --git a/code/sdk/zhihui/ai/media/CMakeLists.txt b/code/sdk/zhihui/ai/media/CMakeLists.txt index cd631abee..f93f44504 100644 --- a/code/sdk/zhihui/ai/media/CMakeLists.txt +++ b/code/sdk/zhihui/ai/media/CMakeLists.txt @@ -16,10 +16,13 @@ ${ROOT_DIR}/middleware/services/media/foundation/audiomanager/interfaces/kits/au ${ROOT_DIR}/middleware/services/media/foundation/audiomanager/frameworks/audiostream/include ${ROOT_DIR}/middleware/services/media/foundation/sample/wrapper ${ROOT_DIR}/middleware/services/media/foundation/utils/include +${ROOT_DIR}/include ) set(AI_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ai_media_service.cpp +${CMAKE_CURRENT_SOURCE_DIR}/ai_media_service_ext.cpp + ) set(SOURCES diff --git a/code/sdk/zhihui/ai/media/ai_media_service.cpp b/code/sdk/zhihui/ai/media/ai_media_service.cpp index ac80cef97..f7e6ec046 100644 --- a/code/sdk/zhihui/ai/media/ai_media_service.cpp +++ b/code/sdk/zhihui/ai/media/ai_media_service.cpp @@ -28,10 +28,13 @@ #include "media_log.h" #include "media_thread_adapt.h" #include "audio_manager_c_wrapper.h" +#include "thread_init.h" +#include "errcode.h" #ifdef __cplusplus extern "C" { #endif +#include "errcode.h" using namespace::Audio; @@ -246,17 +249,21 @@ static void CleanupRecordingResources(AIRecordContext *context) context->isInitialized = false; } +// 全局变量用于传递录制上下文(因为uapi_create_osThread_custom不支持参数传递) +static AIRecordContext *g_recording_context = nullptr; + /** * @brief Recording thread function - * @param priv Recording context pointer - * @return nullptr + * @param data 线程参数(uapi_create_osThread_custom固定为NULL) + * @return void */ -static void* RecordingThreadFunc(void* priv) +static void RecordingThreadFunc(void *data) { - AIRecordContext *context = (AIRecordContext*)priv; + // 由于uapi_create_osThread_custom不支持参数传递,使用全局变量 + AIRecordContext *context = g_recording_context; if (context == nullptr) { MEDIA_ERR_LOG("Recording context is null in thread"); - return nullptr; + return; } MEDIA_INFO_LOG("Recording thread started"); @@ -280,12 +287,14 @@ static void* RecordingThreadFunc(void* priv) } } else { // Sleep briefly if no data available + printf("AudioStreamInObtainBuffer error\r\n"); usleep(10000); // 10ms } } MEDIA_INFO_LOG("Recording thread stopped"); - return nullptr; + // 清理全局变量 + g_recording_context = nullptr; } /** @@ -409,11 +418,28 @@ void ai_media_record_start(uint8_t* format) // Start recording thread g_recordContext->isRecordingStopped = false; - MediaThreadattr attr = { "AIRecordingThread", 0x2000, THREAD_SCHED_INVALID, 0, false }; - g_recordContext->recordThread = MediaThreadCreate(RecordingThreadFunc, g_recordContext, &attr); - if (g_recordContext->recordThread == nullptr) { - MEDIA_ERR_LOG("Failed to create recording thread"); + // 设置全局变量供线程函数使用 + g_recording_context = g_recordContext; + + // 创建线程配置 + app_task_definition_t task_cfg; + memset(&task_cfg, 0, sizeof(task_cfg)); + + task_cfg.create_queue_handle = NULL; + task_cfg.attr.name = "AIRecordingThread"; + task_cfg.attr.priority = (osPriority_t)16; // 使用合适的优先级 + task_cfg.attr.stack_size = 0x2000; // 8KB栈空间 + task_cfg.func = RecordingThreadFunc; + task_cfg.use_mem = USE_EXTEND_MEM; + + // 创建线程 + extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg); + errcode_t ret_errcode_t = uapi_create_osThread_custom(&task_cfg); + + if (ret_errcode_t != ERRCODE_SUCC) { + MEDIA_ERR_LOG("Failed to create recording thread: %d", ret); + g_recording_context = nullptr; // 清理全局变量 AudioStreamInStop(); AudioStreamInDeinit(); g_amInstance.DeactivateAudioInterrupt(g_recordContext->interrupt); @@ -423,6 +449,9 @@ void ai_media_record_start(uint8_t* format) MediaMutexUnLock(g_aiMediaMutex); return; } + + // 保存线程句柄(如果需要的话) + g_recordContext->recordThread = (void*)task_cfg.task_handle; g_recordStatus = 1; // Set recording status MediaMutexUnLock(g_aiMediaMutex); diff --git a/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp b/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp new file mode 100644 index 000000000..4150296b4 --- /dev/null +++ b/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp @@ -0,0 +1,944 @@ +/* + * Copyright (c) CompanyNameMagicTag. 2023-2023. All rights reserved. + * Description: AI voice conversation sample wrapper + * Author: Media Software Group + * Create: 2023-07-06 + */ + +#include "voice_call_volte_sample_wrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" + +#include "audio_manager.h" +#include "audio_stream_in.h" +#include "audio_stream_out.h" +#include "audio_base_type.h" + +#include "media_log.h" +#include "media_thread_adapt.h" +#include "audio_manager_c_wrapper.h" +#include "thread_init.h" +#include "errcode.h" +#include "ai_media.h" + +#ifdef __cplusplus +extern "C" { +#endif + +using namespace::Audio; + +#define MEDIA_CMD_LEN_MAX 128 +#define MEDIA_UNUSED(x) ((void)(x)) + +#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0])) + +const int32_t NUMBER_BASE = 10; +const int32_t CHANNEL_COUNT = 2; +const int32_t SAMPLERATE_16000 = 16000; +const uint32_t AI_VOICE_BUFFER_SIZE = 0x800; // 2KB buffer for AI voice +const uint32_t AI_VOICE_QUEUE_SIZE = 10; // 队列大小 + +// 定义音频数据回调函数类型 + +// 定义队列消息结构体 +typedef struct { + uint8_t *data; + uint32_t size; + uint32_t timestamp; +} AIVoiceQueueMessage; + + +typedef struct { + AudioSession sessionId; + bool isAIVoiceStopped; + MediaThreadCondHandle cond; // 保留条件变量用于线程同步 + MediaThreadIdHandle taskHandle; + char threadName[32]; // 线程名称,用于验证线程ID + uint8_t *dataBuffer; + uint32_t bufferSize; + AIVoiceDataCallback dataCallback; // 音频数据回调函数 + void* callbackUserData; // 回调函数用户数据 + osMessageQueueId_t inputQueue; // 输入播放数据队列 + uint32_t queueMsgCount; // 队列消息数量 +} AIVoiceContext; + +static MediaMutexHandle g_aiVoiceContextMutex = nullptr; +static AIVoiceContext *g_aiVoiceContext = nullptr; + +// 全局互斥锁初始化函数(只创建一次,不释放) +static bool InitGlobalMutex(void) +{ + if (g_aiVoiceContextMutex == nullptr) { + g_aiVoiceContextMutex = MediaMutexCreate(nullptr); + if (g_aiVoiceContextMutex == nullptr) { + MEDIA_ERR_LOG("Failed to create global AI voice context mutex"); + return false; + } + MEDIA_INFO_LOG("Global AI voice context mutex created successfully"); + } + return true; +} + +// 获取全局互斥锁(线程安全,只创建一次) +static MediaMutexHandle GetGlobalMutex(void) +{ + if (g_aiVoiceContextMutex == nullptr) { + // 使用简单的初始化,避免重复创建 + static bool init_once = false; + if (!init_once) { + init_once = true; + g_aiVoiceContextMutex = MediaMutexCreate(nullptr); + if (g_aiVoiceContextMutex != nullptr) { + MEDIA_INFO_LOG("Global AI voice context mutex created successfully"); + } else { + MEDIA_ERR_LOG("Failed to create global AI voice context mutex"); + } + } + } + return g_aiVoiceContextMutex; +} + +// 注意:全局互斥锁不释放,程序运行期间持续存在 + +static std::shared_ptr g_streamIn = nullptr; +static std::shared_ptr g_streamOut = nullptr; + +// 注意:g_aiVoiceContext 已在上方定义,这里不需要重复定义 + + + +typedef struct { + char cmd[MEDIA_CMD_LEN_MAX]; + int32_t (*func)(const char **param); +} FuncsMap; + + +static int32_t CreateAIVoiceInputThread(AIVoiceContext *context); + +static void AIVoiceInputThreadFunc(void *data); + +// 队列创建函数 +static void CreateAIVoiceQueue(void) +{ + if (g_aiVoiceContext != nullptr) { + // 创建消息队列 + g_aiVoiceContext->inputQueue = osMessageQueueNew(AI_VOICE_QUEUE_SIZE, sizeof(AIVoiceQueueMessage), NULL); + if (g_aiVoiceContext->inputQueue == NULL) { + MEDIA_ERR_LOG("Failed to create AI voice input queue"); + } else { + g_aiVoiceContext->queueMsgCount = 0; + MEDIA_INFO_LOG("AI voice input queue created successfully"); + } + } +} + +// 验证线程名和ID是否匹配,避免ID被复用 +static bool IsValidThread(osThreadId_t threadId, const char* expectedName) +{ + if (threadId == NULL || expectedName == NULL) { + return false; + } + + // 首先检查线程状态,如果线程已经不存在,直接返回false + osThreadState_t threadState = osThreadGetState(threadId); + if (threadState == osThreadError) { + return false; // 线程不存在或无效 + } + + // 获取线程名称 + const char* actualName = osThreadGetName(threadId); + if (actualName == NULL) { + return false; + } + + // 比较线程名称 + bool nameMatch = (strcmp(actualName, expectedName) == 0); + + // 调试信息(可选,在需要时启用) + // printf("Thread validation: ID=%p, Expected='%s', Actual='%s', State=%d, Match=%s\r\n", + // threadId, expectedName, actualName, threadState, nameMatch ? "YES" : "NO"); + + return nameMatch; +} + +static void DeinitAIVoiceResources(AIVoiceContext *context) +{ + (void)g_streamIn.reset(); + (void)g_streamOut.reset(); + + if (context != nullptr) { + if (context->dataBuffer != nullptr) { + free(context->dataBuffer); + context->dataBuffer = nullptr; + } + + // 清理队列 + if (context->inputQueue != NULL) { + // 清空队列中的所有消息 + AIVoiceQueueMessage msg; + while (osMessageQueueGet(context->inputQueue, &msg, NULL, 0) == osOK) { + if (msg.data != nullptr) { + free(msg.data); + } + } + + // 删除队列 + osMessageQueueDelete(context->inputQueue); + context->inputQueue = NULL; + MEDIA_INFO_LOG("AI voice input queue deleted"); + } + } +} + +static bool InitAIVoiceResources(AIVoiceContext *context) +{ + context->cond = MediaThreadCondCreate(); + if (context->cond == nullptr) { + MEDIA_ERR_LOG("create Voice Call Volte thread cond failed"); + return false; + } + + // Allocate data buffer for AI voice + context->dataBuffer = (uint8_t*)malloc(AI_VOICE_BUFFER_SIZE); + if (context->dataBuffer == nullptr) { + MEDIA_ERR_LOG("Failed to allocate AI voice buffer"); + MediaThreadCondDestroy(&context->cond); + return false; + } + + context->bufferSize = AI_VOICE_BUFFER_SIZE; + return true; +} + +static int32_t LocalAIVoiceStart(AIVoiceContext *context) +{ + int32_t ret = 0; + // AudioManagerSetDeviceConnectionState(IN_MODEM, AUDIO_DEVICE_CONNECTED); + // if (ret != 0) { + // MEDIA_ERR_LOG("set modem in device connect failed"); + // } + + // ret = AudioManagerSetDeviceConnectionState(OUT_MODEM, AUDIO_DEVICE_CONNECTED); + // if (ret != 0) { + // MEDIA_ERR_LOG("set modem out device connect failed"); + // } + + // create downlink AudioStreamIn + CapturerInputConfig captureConfig = {}; + captureConfig.audioFormat = MP3; + captureConfig.channelCount = 1; + captureConfig.linkDir = AUDIO_DOWN_LINK; + captureConfig.sampleRate = SAMPLERATE_16000; + captureConfig.sessionID = context->sessionId; + captureConfig.streamType = AUDIO_STREAM_VOICE_ASSISTANT; + + ret = AudioStreamInInit(captureConfig); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream in init failed"); + } + + // create uplink AudioStreamOut + AudioRendererConfig renderConfig = {}; + renderConfig.audioFormat = MP3; + renderConfig.channelCount = 1; + renderConfig.linkDir = AUDIO_UP_LINK; + renderConfig.sampleRate = SAMPLERATE_16000; + renderConfig.sessionID = context->sessionId; + renderConfig.streamType = AUDIO_STREAM_VOICE_ASSISTANT; + + ret = AudioStreamOutInit(renderConfig); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream out init failed"); + } + + ret = AudioStreamInStart(); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream in start failed"); + } + + ret = AudioStreamOutPlay(); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream out play failed"); + } + + // Create input thread for data processing + ret = CreateAIVoiceInputThread(context); + if (ret != 0) { + MEDIA_ERR_LOG("Failed to create AI voice input thread"); + return ret; + } + + return ret; +} + +static int32_t LocalAIVoiceStop(void) +{ + int32_t ret = AudioStreamInStop(); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream in stop failed"); + } + + ret = AudioStreamOutStop(); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream out stop failed"); + } + + ret = AudioStreamInDeinit(); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream in release failed"); + } + + ret = AudioStreamOutDeinit(); + if (ret != 0) { + MEDIA_ERR_LOG("audio stream out release failed"); + } + return ret; +} + +/** + * @brief Create AI voice input thread + * @param context AI voice context + * @return 0 on success, -1 on failure + */ +static int32_t CreateAIVoiceInputThread(AIVoiceContext *context) +{ + if (context == nullptr) { + MEDIA_ERR_LOG("AI voice context is null"); + return -1; + } + + // 设置全局变量供线程函数使用(使用统一的全局变量) + // g_aiVoiceContext 已经在上面设置过了 + + // 创建线程配置 + app_task_definition_t task_cfg; + memset(&task_cfg, 0, sizeof(task_cfg)); + + task_cfg.create_queue_handle = NULL; + task_cfg.attr.name = "AIVoiceInputThread"; + task_cfg.attr.priority = (osPriority_t)16; // 使用合适的优先级 + task_cfg.attr.stack_size = 0x2000; // 8KB栈空间 + task_cfg.func = AIVoiceInputThreadFunc; + task_cfg.use_mem = USE_EXTEND_MEM; + + // 创建线程 + extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg); + errcode_t ret_errcode_t = uapi_create_osThread_custom(&task_cfg); + + if (ret_errcode_t != ERRCODE_SUCC) { + MEDIA_ERR_LOG("Failed to create AI voice input thread: %d", ret_errcode_t); + // 清理全局变量 + return -1; + } + + // 保存线程句柄 + context->taskHandle = (void*)task_cfg.task_handle; + + MEDIA_INFO_LOG("AI voice input thread created successfully"); + return 0; +} + +/** + * @brief AI voice input stream reading thread function + * @param data 线程参数(uapi_create_osThread_custom固定为NULL) + * @return void + */ +static void AIVoiceInputThreadFunc(void *data) +{ + // 由于uapi_create_osThread_custom不支持参数传递,使用全局变量 + AIVoiceContext *context = g_aiVoiceContext; + if (context == nullptr) { + MEDIA_ERR_LOG("AI voice context is null in thread"); + return; + } + + MEDIA_INFO_LOG("AI voice input thread started"); + + while (true) { + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex != nullptr) { + MediaMutexLock(mutex); + if (context->isAIVoiceStopped) { + MediaMutexUnLock(mutex); + break; + } + MediaMutexUnLock(mutex); + } + + // Obtain audio data from input stream + uint32_t readSize = context->bufferSize; + int32_t ret = AudioStreamInObtainBuffer(context->dataBuffer, &readSize); + + if (ret == 0 && readSize > 0) { + // 调用音频数据回调函数 + if (context->dataCallback != nullptr) { + context->dataCallback(context->dataBuffer, readSize, context->callbackUserData); + } + + // Write audio data to output stream (echo back) + // int32_t writeRet = AudioStreamOutStreamWrite(context->dataBuffer, readSize); + // if (readSize != 0) { + // MEDIA_ERR_LOG("Failed to write audio data to output stream, ret=%d", readSize); + // } + + } + usleep(10000); // 10ms + } + + + MEDIA_INFO_LOG("AI voice input thread stopped"); + // 清理全局变量 + // g_aiVoiceContext 的清理由主线程负责 +} + +static int32_t RunAIVoice(AIVoiceContext *context) +{ + bool success = InitAIVoiceResources(context); + if (!success) { + MEDIA_ERR_LOG("init AI voice resources failed"); + return -1; + } + int32_t ret = LocalAIVoiceStart(context); + if (ret != 0) { + return ret; + } + +// Wait for stop signal and process queue data + MEDIA_INFO_LOG("RunAIVoice: waiting for queue data..."); + + while (!context->isAIVoiceStopped) { + if (context->inputQueue != NULL) { + AIVoiceQueueMessage msg; + osStatus_t status = osMessageQueueGet(context->inputQueue, &msg, NULL, 100); // 100ms超时 + + if (status == osOK) { + // 成功接收到队列数据 + MEDIA_INFO_LOG("RunAIVoice: received queue data: size=%u, timestamp=%u", msg.size, msg.timestamp); + + // 处理接收到的音频数据 + if (msg.data != nullptr && msg.size > 0) { + // 写入音频数据到输出流 + AI_WriteAudioData(msg.data,msg.size); + + // 释放消息数据内存 + free(msg.data); + } + + // 更新队列消息计数 + if (context->queueMsgCount > 0) { + context->queueMsgCount--; + } + } else if (status == osErrorTimeout) { + // 超时,继续循环检查停止标志 + continue; + } else { + // 其他错误 + MEDIA_ERR_LOG("RunAIVoice: failed to get message from queue: %d", status); + usleep(10000); // 10ms延时 + } + } else { + // 队列未创建,使用条件变量等待 + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex != nullptr) { + MediaMutexLock(mutex); + if (!context->isAIVoiceStopped) { + MediaThreadCondWait(context->cond, mutex); + } + MediaMutexUnLock(mutex); + } + } + } + + LocalAIVoiceStop(); + DeinitAIVoiceResources(context); + return 0; +} + +static bool RequsetAudioFocus(AIVoiceContext *context) +{ + bool success = AudioManagerInit(); + if (!success) { + MEDIA_ERR_LOG("audiomanager init failed"); + return false; + } + + AudioSession sessionId = AudioManagerMakeSessionId(); + if (sessionId == AUDIO_SESSION_ID_NONE) { + MEDIA_ERR_LOG("audio session id invalid"); + return false; + } + + if (AudioManagerActivateInterrupt(AUDIO_STREAM_VOICE_ASSISTANT, sessionId) == INTERRUPT_FAILED) { + MEDIA_ERR_LOG("activate audio interrupt failed"); + return false; + } + + context->sessionId = sessionId; + + return true; +} + +static bool ReleaseAudioFocus(void) +{ + if (AudioManagerDeactivateInterrupt() != 0) { + MEDIA_ERR_LOG("deactivate audio interrupt failed"); + return false; + } + return true; +} + + +static void AIVoiceThread(void *priv) +{ + // 由于uapi_create_osThread_custom不支持参数传递,使用全局变量 + AIVoiceContext *context = g_aiVoiceContext; + if (context == nullptr) { + MEDIA_ERR_LOG("AI voice context is null in thread"); + return; + } + + bool success = RequsetAudioFocus(context); + if (!success) { + MEDIA_ERR_LOG("requset audio focus failed"); + return; + } + + int32_t ret = RunAIVoice(context); + if (ret != 0) { + MEDIA_ERR_LOG("run AI voice failed"); + (void)ReleaseAudioFocus(); + return; + } + + success = ReleaseAudioFocus(); + if (!success) { + MEDIA_ERR_LOG("release audio focus failed"); + } + // 简单清理,避免复杂的互斥锁操作 + printf("AI voice thread cleanup completed\r\n"); +} +int32_t old_Start_AIVoice(AIVoiceDataCallback dataCallback, void* userData) +{ + MEDIA_ERR_LOG("StartAIVoice"); + app_task_definition_t task_cfg; + errcode_t ret_errcode_t; + + // 获取全局互斥锁(自动初始化) + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + MEDIA_ERR_LOG("Failed to get global mutex"); + return -1; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext != nullptr) { + MEDIA_ERR_LOG("invalid operation"); + goto EXIT; + } + g_aiVoiceContext = (AIVoiceContext *)malloc(sizeof(AIVoiceContext)); + if (g_aiVoiceContext == nullptr) { + MEDIA_ERR_LOG("malloc failed"); + goto EXIT; + } + if (memset_s(g_aiVoiceContext, sizeof(AIVoiceContext), 0, sizeof(AIVoiceContext)) != EOK) { + MEDIA_ERR_LOG("memset_s failed"); + goto EXIT; + } + + // 设置回调函数 + g_aiVoiceContext->dataCallback = dataCallback; + g_aiVoiceContext->callbackUserData = userData; + + MediaMutexUnLock(mutex); + MediaThreadJoin(&g_aiVoiceContext->taskHandle); + + MEDIA_ERR_LOG("g_aiVoiceContext->taskHandle"); + + // 设置全局变量供线程函数使用(g_aiVoiceContext 已经设置好了) + + // 创建线程配置 + memset(&task_cfg, 0, sizeof(task_cfg)); + + task_cfg.create_queue_handle = CreateAIVoiceQueue; // 设置队列创建函数 + task_cfg.attr.name = "AIVoiceThread"; + task_cfg.attr.priority = (osPriority_t)16; // 使用合适的优先级 + task_cfg.attr.stack_size = 0x2000; // 8KB栈空间 + task_cfg.func = AIVoiceThread; + task_cfg.use_mem = USE_EXTEND_MEM; + + // 创建线程 + extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg); + ret_errcode_t= uapi_create_osThread_custom(&task_cfg); + + if (ret_errcode_t != ERRCODE_SUCC) { + MEDIA_ERR_LOG("Failed to create AI voice thread: %d", ret_errcode_t); + free(g_aiVoiceContext); + g_aiVoiceContext = nullptr; + return -1; + } + + // 保存线程句柄和名称 + g_aiVoiceContext->taskHandle = (void*)task_cfg.task_handle; + strncpy(g_aiVoiceContext->threadName, task_cfg.attr.name, sizeof(g_aiVoiceContext->threadName) - 1); + g_aiVoiceContext->threadName[sizeof(g_aiVoiceContext->threadName) - 1] = '\0'; // 确保字符串结束 + + MEDIA_INFO_LOG("AI voice thread created successfully with name: %s", g_aiVoiceContext->threadName); + return 0; +EXIT: + // 清理已分配的资源 + if (g_aiVoiceContext != nullptr) { + DeinitAIVoiceResources(g_aiVoiceContext); + free(g_aiVoiceContext); + g_aiVoiceContext = nullptr; + } + MediaMutexUnLock(mutex); + return -1; +} + + +int32_t StopAIVoice(void) +{ + AIVoiceContext *context = nullptr; + + // 获取全局互斥锁(自动初始化) + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + MEDIA_ERR_LOG("Failed to get global mutex"); + return -1; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext == nullptr) { + MEDIA_ERR_LOG("AI voice context is null, execute start command first"); + MediaMutexUnLock(mutex); + return -1; + } + + if (g_aiVoiceContext->isAIVoiceStopped) { + MEDIA_ERR_LOG("AI voice already stopped"); + MediaMutexUnLock(mutex); + return 0; + } + + // 保存上下文指针 + context = g_aiVoiceContext; + + // 设置停止标志(在全局锁保护下) + context->isAIVoiceStopped = true; + + // 释放全局互斥锁 + MediaMutexUnLock(g_aiVoiceContextMutex); + + // 等待线程结束 - 使用 osThread 接口 + printf("Waiting for AI voice thread to stop...\r\n"); + + if (context != nullptr && context->taskHandle != nullptr) { + osThreadId_t threadId = (osThreadId_t)context->taskHandle; + + // 验证线程ID和名称是否匹配,避免ID被复用 + if (!IsValidThread(threadId, context->threadName)) { + printf("Thread ID may have been reused, thread name mismatch. Expected: %s\r\n", context->threadName); + printf("Assuming thread already stopped due to ID reuse\r\n"); + } else { + printf("Thread validation passed, waiting for thread: %s\r\n", context->threadName); + + // 首先尝试使用 osThreadJoin 等待线程结束 + osStatus_t joinStatus = osThreadJoin(threadId); + if (joinStatus == osOK) { + printf("AI voice thread stopped successfully (osThreadJoin)\r\n"); + } else { + // 如果 osThreadJoin 不可用或失败,使用状态检查 + printf("osThreadJoin failed (%d), using state check...\r\n", joinStatus); + + // 使用 osThreadGetState 检查线程状态 + for (int i = 0; i < 50; i++) { + usleep(100000); // 100ms + + // 再次验证线程,防止在等待过程中ID被复用 + if (!IsValidThread(threadId, context->threadName)) { + printf("Thread ID reused during wait, stopping check\r\n"); + break; + } + + osThreadState_t threadState = osThreadGetState(threadId); + + // 检查线程状态,如果线程不存在或已终止,则认为已停止 + bool thread_running = (threadState == osThreadReady || + threadState == osThreadRunning || + threadState == osThreadBlocked); + + printf("Waiting... %d/50 (state: %d, running: %s)\r\n", + i + 1, threadState, thread_running ? "yes" : "no"); + + if (!thread_running) { + printf("AI voice thread stopped successfully (state check)\r\n"); + break; + } + } + } + } + } else { + printf("No valid thread handle, assuming thread already stopped\r\n"); + } + + // 重新获取全局互斥锁进行最终清理 + MediaMutexLock(g_aiVoiceContextMutex); + + // 清理线程相关资源 + if (context != nullptr) { + // 首先清理AI语音资源(包括队列) + DeinitAIVoiceResources(context); + + // 清理线程同步资源 + if (context->cond != nullptr) { + (void)MediaThreadCondDestroy(&context->cond); + } + + // 释放上下文内存 + free(context); + g_aiVoiceContext = nullptr; + } + + MediaMutexUnLock(g_aiVoiceContextMutex); + + // 注意:全局互斥锁不释放,程序运行期间持续存在 + + printf("AI voice stopped and resources cleaned up\r\n"); + return 0; +} + +// 示例音频数据回调函数 +static void ExampleAIVoiceDataCallback(const uint8_t* data, uint32_t size, void* userData) +{ + MEDIA_INFO_LOG("Received audio data: size=%u bytes", size); + // 在这里处理音频数据,例如: + // - 发送到AI语音识别服务 + // - 保存到文件 + // - 进行音频处理等 + // 注意:data指针指向的缓冲区在回调函数返回后可能被释放,如需保存数据请复制 +} + +// 使用示例: +// int32_t result = old_Start_AIVoice(ExampleAIVoiceDataCallback, nullptr); +// +// 队列使用示例: +// uint8_t audioData[] = {0x01, 0x02, 0x03, 0x04}; +// int32_t sendResult = AI_SendAudioDataToQueue(audioData, sizeof(audioData)); +// +// 获取队列状态: +// uint32_t queueSize, queueCapacity; +// AI_GetQueueStatus(&queueSize, &queueCapacity); + +/** + * @brief 写入音频数据到输出流 + * @param data 音频数据指针 + * @param size 音频数据大小 + * @return 0 成功,-1 失败 + */ +int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size) +{ + if (data == nullptr || size == 0) { + MEDIA_ERR_LOG("Invalid audio data parameters"); + return -1; + } + AI_WRITE: + // 获取全局互斥锁(自动初始化) + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + MEDIA_ERR_LOG("Failed to get global mutex"); + return -1; + } + + // 检查AI语音上下文是否存在 + MediaMutexLock(mutex); + if (g_aiVoiceContext == nullptr) { + MEDIA_ERR_LOG("AI voice context is null, please start AI voice first"); + MediaMutexUnLock(mutex); + return -1; + } + MediaMutexUnLock(g_aiVoiceContextMutex); + + // 写入音频数据到输出流 + int32_t ret = AudioStreamOutStreamWrite(data, size); + if (ret != 0) { + // MEDIA_ERR_LOG("Failed to write audio data to output stream, ret=%d", ret); + usleep(100000); + // return -1; + goto AI_WRITE; + } + + MEDIA_INFO_LOG("Successfully wrote %u bytes of audio data to output stream", size); + return 0; +} + +/** + * @brief 检查AI语音服务是否正在运行 + * @return true 正在运行,false 未运行 + */ +bool AI_IsVoiceServiceRunning(void) +{ + bool isRunning = false; + + // 获取全局互斥锁(自动初始化) + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + return false; // 互斥锁创建失败,服务未运行 + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext != nullptr && !g_aiVoiceContext->isAIVoiceStopped) { + isRunning = true; + } + MediaMutexUnLock(mutex); + + return isRunning; +} + +/** + * @brief 发送音频数据到AI语音播放队列 + * @param data 音频数据指针 + * @param size 音频数据大小 + * @return 0 成功,-1 失败 + */ +int32_t AI_SendAudioDataToQueue(const uint8_t* data, uint32_t size) +{ + if (data == nullptr || size == 0) { + MEDIA_ERR_LOG("Invalid audio data parameters"); + return -1; + } + + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + MEDIA_ERR_LOG("Failed to get global mutex"); + return -1; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext == nullptr || g_aiVoiceContext->inputQueue == NULL) { + MEDIA_ERR_LOG("AI voice context or queue is null, please start AI voice first"); + MediaMutexUnLock(mutex); + return -1; + } + + // 分配内存并复制数据 + uint8_t *dataCopy = (uint8_t*)malloc(size); + if (dataCopy == nullptr) { + MEDIA_ERR_LOG("Failed to allocate memory for audio data"); + MediaMutexUnLock(mutex); + return -1; + } + + memcpy(dataCopy, data, size); + + // 创建队列消息 + AIVoiceQueueMessage msg; + msg.data = dataCopy; + msg.size = size; + msg.timestamp = (uint32_t)time(NULL); + + // 发送消息到队列 + osStatus_t status = osMessageQueuePut(g_aiVoiceContext->inputQueue, &msg, 0, 0); + if (status == osOK) { + g_aiVoiceContext->queueMsgCount++; + MEDIA_INFO_LOG("Successfully sent %u bytes to AI voice queue", size); + MediaMutexUnLock(mutex); + return 0; + } else { + MEDIA_ERR_LOG("Failed to send data to queue: %d", status); + free(dataCopy); // 释放分配的内存 + MediaMutexUnLock(mutex); + return -1; + } +} + +/** + * @brief 获取AI语音队列状态 + * @param queueSize 输出参数:队列当前大小 + * @param queueCapacity 输出参数:队列容量 + * @return 0 成功,-1 失败 + */ +int32_t AI_GetQueueStatus(uint32_t* queueSize, uint32_t* queueCapacity) +{ + if (queueSize == nullptr || queueCapacity == nullptr) { + MEDIA_ERR_LOG("Invalid output parameters"); + return -1; + } + + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + MEDIA_ERR_LOG("Failed to get global mutex"); + return -1; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext == nullptr || g_aiVoiceContext->inputQueue == NULL) { + *queueSize = 0; + *queueCapacity = 0; + MediaMutexUnLock(mutex); + return -1; + } + + *queueSize = g_aiVoiceContext->queueMsgCount; + *queueCapacity = AI_VOICE_QUEUE_SIZE; + MediaMutexUnLock(mutex); + + return 0; +} + +/** + * @brief 获取AI语音服务状态信息 + * @param isRunning 输出参数:是否正在运行 + * @param hasOutput 输出参数:是否有输出流 + * @return 0 成功,-1 失败 + */ +// int32_t AI_GetVoiceServiceStatus(bool* isRunning, bool* hasOutput) +// { +// if (isRunning == nullptr || hasOutput == nullptr) { +// MEDIA_ERR_LOG("Invalid output parameters"); +// return -1; +// } + +// MediaMutexLock(g_aiVoiceContextMutex); +// // if (g_aiVoiceContext != nullptr) { +// // *isRunning = !g_aiVoiceContext->isAIVoiceStopped; +// // // *hasOutput = (g_streamOut != nullptr); +// // } else { +// // *isRunning = false; +// // *hasOutput = false; +// // } +// MediaMutexUnLock(g_aiVoiceContextMutex); + +// return 0; +// } + +/** + * @brief 清理AI语音模块资源(程序退出时调用) + * @return 0 成功,-1 失败 + */ +int32_t AI_CleanupModule(void) +{ + // 如果AI语音服务还在运行,先停止它 + if (AI_IsVoiceServiceRunning()) { + MEDIA_INFO_LOG("AI voice service is still running, stopping it first..."); + StopAIVoice(); + } + + // 注意:全局互斥锁不释放,程序运行期间持续存在 + + MEDIA_INFO_LOG("AI voice module cleanup completed"); + return 0; +} + +#ifdef __cplusplus +}; +#endif diff --git a/code/sdk/zhihui/ai_old/CMakeLists.txt b/code/sdk/zhihui/ai_old/CMakeLists.txt index ae9d84098..226a3b6a4 100644 --- a/code/sdk/zhihui/ai_old/CMakeLists.txt +++ b/code/sdk/zhihui/ai_old/CMakeLists.txt @@ -22,6 +22,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/zhihui_ai_server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/asr_websocket_server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tts_websocket_server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/chat_websocket_server.cpp +${CMAKE_CURRENT_SOURCE_DIR}/websocket_server_example.cpp +${CMAKE_CURRENT_SOURCE_DIR}/websocket_server_manager.cpp ) set(COMPONENT_PUBLIC_CCFLAGS diff --git a/code/sdk/zhihui/ai_old/ai_old_at_cmd.cpp b/code/sdk/zhihui/ai_old/ai_old_at_cmd.cpp index fdc572b9a..17743ea46 100644 --- a/code/sdk/zhihui/ai_old/ai_old_at_cmd.cpp +++ b/code/sdk/zhihui/ai_old/ai_old_at_cmd.cpp @@ -10,6 +10,9 @@ #include #include #include +#include "ai_core_types.h" +#include "websocket_server_manager.h" + // #include "phoneaddressmodel.h" // Commented out due to missing header #ifdef __cplusplus @@ -67,7 +70,7 @@ static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen, extern void uapi_set4GCapability(bool isopen); extern bool init_ai_task(uint8_t type); extern errcode_t chat_server_init(void); -extern bool ai_chat_send_txt(uint8_t type,char *msg); +// extern bool ai_chat_send_txt(uint8_t type,char *msg); extern bool uapi_start_tts(void); extern void uapi_start_tts_single(); extern void uapi_UI_start_asr(uint8_t type); @@ -126,7 +129,7 @@ static uint32_t ai_msg(uint8_t *para, uint32_t paraLen, char *strCommonCmd) printf("CAT1TEST ATCMD Convert Command Failed\n"); return ERRCODE_FAIL; } - ai_chat_send_txt(0,(char *)argv[0]); + // ai_chat_send_txt(0, (char *)argv[0], NULL); return 0; } @@ -335,6 +338,73 @@ static uint32_t stop_websocket_test_cmd(uint8_t *para, uint32_t paraLen, char *s return ERRCODE_SUCC; } +static uint32_t startserver(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[PPG_MAX_ARGC][PPG_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + printf("ai_msg\n"); + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != PPG_SUCCESS) { + printf("CAT1TEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + init_all_websocket_servers(); + start_all_websocket_servers(); + return 0; +} + + +static uint32_t StartTranscription(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[PPG_MAX_ARGC][PPG_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + printf("ai_msg\n"); + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != PPG_SUCCESS) { + printf("CAT1TEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + uapi_start_asr_StartTranscription(); + return 0; +} + + +static uint32_t STOPTranscription(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[PPG_MAX_ARGC][PPG_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + printf("ai_msg\n"); + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != PPG_SUCCESS) { + printf("CAT1TEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + uapi_stop_asr_StopTranscription(); + return 0; +} +extern void msleep(uint32_t ms); + +static uint32_t STOPserver(uint8_t *para, uint32_t paraLen, char *strCommonCmd) +{ + UNUSED(strCommonCmd); + uint8_t argv[PPG_MAX_ARGC][PPG_MAX_ARGV_LEN] = { 0 }; + uint32_t argc = 0; + printf("ai_msg\n"); + int ret = ConvertCmdToParam(para, paraLen, argv, &argc); + if (ret != PPG_SUCCESS) { + printf("CAT1TEST ATCMD Convert Command Failed\n"); + return ERRCODE_FAIL; + } + stop_all_websocket_servers(); + msleep(1000); + destroy_all_websocket_servers(); + return 0; +} + + static uint32_t websocket_status(uint8_t *para, uint32_t paraLen, char *strCommonCmd) { UNUSED(strCommonCmd); @@ -369,6 +439,13 @@ static const at_cmd_table_t g_ai_old_cmd[] = { { (char *)"AT^TESTWEBSOCKET", test_websocket, (char *)"1" }, { (char *)"AT^STOPWEBSOCKET", stop_websocket_test_cmd, (char *)"1" }, { (char *)"AT^WEBSOCKETSTATUS", websocket_status, (char *)"1" }, + { (char *)"AT^WEBSOCKETSTART", startserver, (char *)"1" }, + { (char *)"AT^STARTTRAN", StartTranscription, (char *)"1" }, + { (char *)"AT^STOPTRAN", STOPTranscription, (char *)"1" }, + { (char *)"AT^WEBSOCKSTOP", STOPserver, (char *)"1" }, + + + }; at_cmd_table_t *get_ai_old_cmd_table(uint32_t *cmd_count) diff --git a/code/sdk/zhihui/ai_old/asr_websocket_server.cpp b/code/sdk/zhihui/ai_old/asr_websocket_server.cpp index 840675ba5..d15c2143d 100644 --- a/code/sdk/zhihui/ai_old/asr_websocket_server.cpp +++ b/code/sdk/zhihui/ai_old/asr_websocket_server.cpp @@ -143,8 +143,8 @@ void uapi_UI_stop_asr(void) if(strlen(result_str_asr)) { // 发送ASR结果到聊天服务 - extern bool ai_chat_send_txt(uint8_t type, char *msg); - ai_chat_send_txt(0, result_str_asr); + // extern bool ai_chat_send_txt(uint8_t type, char *msg); + // ai_chat_send_txt(0, result_str_asr, NULL); ble_set_ai_Question_str(result_str_asr); memset(result_str_asr, 0, AI_RESULT_STR_LEN); @@ -435,6 +435,118 @@ void asr_server_deinit(void) set_ai_asr_service_init_flag(0); } + + +uint8_t asr_is_ready=0; + + + +asr_event_type_t aer_result_analysis(char *buf, asr_result_output_t *output) +{ + // 初始化输出结构体 + if (output) { + memset(output, 0, sizeof(asr_result_output_t)); + output->event_type = ASR_EVENT_UNKNOWN; + } + + cJSON* root = cJSON_Parse((const char *)buf); + if (root == NULL) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"解析失败 %s\n",buf); + if (output) { + output->event_type = ASR_EVENT_ERROR; + } + return ASR_EVENT_ERROR; + } + + char *result_str = NULL; + int start_time_int = 0; + int end_time_int = 0; + bool is_end_bool = false; + char *event_str = NULL; + asr_event_type_t event_type = ASR_EVENT_UNKNOWN; + + // 解析event字段 + cJSON* event = cJSON_GetObjectItem(root, "event"); + if (event && cJSON_IsString(event)) { + event_str = event->valuestring; + } + + // 解析result字段 + cJSON* result = cJSON_GetObjectItem(root, "result"); + if (result) { + cJSON* result_ = cJSON_GetObjectItem(result, "result"); + if (result_ && cJSON_IsString(result_)) { + result_str = result_->valuestring; + } + + cJSON* start_time = cJSON_GetObjectItem(result, "start_time"); + if (start_time && cJSON_IsNumber(start_time)) { + start_time_int = start_time->valueint; + } + + cJSON* end_time = cJSON_GetObjectItem(result, "end_time"); + if (end_time && cJSON_IsNumber(end_time)) { + end_time_int = end_time->valueint; + } + + cJSON* is_end = cJSON_GetObjectItem(result, "is_end"); + if (is_end && cJSON_IsBool(is_end)) { + is_end_bool = cJSON_IsTrue(is_end); + } + } + + // 根据事件字符串确定事件类型 + if (event_str) { + if (strcmp(event_str, "TranscriptionStarted") == 0) { + event_type = ASR_EVENT_TRANSCRIPTION_STARTED; + asr_is_ready = 1; + } else if (strcmp(event_str, "TranscriptionCompleted") == 0) { + event_type = ASR_EVENT_TRANSCRIPTION_COMPLETED; + } else if (strcmp(event_str, "Failed") == 0) { + event_type = ASR_EVENT_FAILED; + } else { + event_type = ASR_EVENT_UNKNOWN; + } + } + + // 如果有结果文本,设置为结果事件 + if (result_str && strlen(result_str) > 0) { + if (is_end_bool) { + event_type = ASR_EVENT_RESULT; + } else { + event_type = ASR_EVENT_PARTIAL_RESULT; + } + } + + // 填充输出结构体 + if (output) { + output->event_type = event_type; + output->start_time = start_time_int; + output->end_time = end_time_int; + output->is_end = is_end_bool; + output->is_ready = (event_type == ASR_EVENT_TRANSCRIPTION_STARTED); + + if (result_str) { + strncpy(output->result_text, result_str, sizeof(output->result_text) - 1); + output->result_text[sizeof(output->result_text) - 1] = '\0'; + } + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"Event: %s, Result: %s, Start: %d, End: %d, IsEnd: %d, EventType: %d\n", + event_str ? event_str : "NULL", + result_str ? result_str : "NULL", + start_time_int, end_time_int, is_end_bool, event_type); + + + cJSON_Delete(root); + return event_type; +} + + + + + + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/code/sdk/zhihui/ai_old/asr_websocket_server.h b/code/sdk/zhihui/ai_old/asr_websocket_server.h index 5023af237..1448dec1d 100644 --- a/code/sdk/zhihui/ai_old/asr_websocket_server.h +++ b/code/sdk/zhihui/ai_old/asr_websocket_server.h @@ -20,6 +20,31 @@ extern "C" { /* ASR 服务相关类型定义 - errcode_t is defined in errcode.h */ +/** + * @brief ASR事件类型枚举 + */ +typedef enum { + ASR_EVENT_UNKNOWN = 0, ///< 未知事件 + ASR_EVENT_TRANSCRIPTION_STARTED, ///< 转录开始 + ASR_EVENT_TRANSCRIPTION_COMPLETED, ///< 转录完成 + ASR_EVENT_FAILED, ///< 转录失败 + ASR_EVENT_RESULT, ///< 转录结果 + ASR_EVENT_PARTIAL_RESULT, ///< 部分结果 + ASR_EVENT_ERROR ///< 错误事件 +} asr_event_type_t; + +/** + * @brief ASR结果输出结构体 + */ +typedef struct { + asr_event_type_t event_type; ///< 事件类型 + char result_text[512]; ///< 识别结果文本 + int start_time; ///< 开始时间 + int end_time; ///< 结束时间 + bool is_end; ///< 是否结束 + bool is_ready; ///< 是否准备就绪 +} asr_result_output_t; + /* 任务配置结构体 - 使用系统定义的 app_task_definition_t */ /* 常量定义 */ @@ -48,6 +73,14 @@ uint8_t get_ai_asr_service_init_flag(void); */ void set_ai_asr_service_init_flag(uint8_t flag); +/** + * @brief ASR结果分析函数 + * @param buf JSON格式的ASR结果数据 + * @param output 输出结构体指针,用于返回解析结果 + * @return asr_event_type_t 返回事件类型 + */ +asr_event_type_t aer_result_analysis(char *buf, asr_result_output_t *output); + /** * @brief 获取ASR准备开始标志 * @return 准备开始标志 diff --git a/code/sdk/zhihui/ai_old/bk_websocket.h b/code/sdk/zhihui/ai_old/bk_websocket.h index 0af58d5c9..e43b0fc74 100644 --- a/code/sdk/zhihui/ai_old/bk_websocket.h +++ b/code/sdk/zhihui/ai_old/bk_websocket.h @@ -76,7 +76,7 @@ int ws_process_received_data(WebSocketClient *client, const uint8_t *data, size_ void close_websocket_client(WebSocketClient *client) ; // 接收 WebSocket 消息 -int receive_websocket_message(WebSocketClient *client, unsigned char *buf,int len,WebSocketMessageType *code); +int receive_websocket_message(WebSocketClient *client, unsigned char *buf,int len,WebSocketMessageType *code, uint32_t timeout_ms); int send_websocket_message(WebSocketClient *client, uint8_t *message, size_t message_len, WebSocketMessageType type) ; // WebSocket测试客户端(当SUPPORT_WEBSOCKET_TEST定义时可用) diff --git a/code/sdk/zhihui/ai_old/chat_websocket_server.cpp b/code/sdk/zhihui/ai_old/chat_websocket_server.cpp index e9339d7f8..bf0dd93c3 100644 --- a/code/sdk/zhihui/ai_old/chat_websocket_server.cpp +++ b/code/sdk/zhihui/ai_old/chat_websocket_server.cpp @@ -25,9 +25,12 @@ #include "los_printf.h" #include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h" + #ifdef __cplusplus extern "C" { #endif +#include "chat_websocket_server.h" +#include "ai_core_types.h" /* Chat 相关全局变量 */ WebSocketClient *chat_client = NULL; @@ -37,10 +40,80 @@ static uint8_t is_bk_websocket_chat = 0; static uint8_t ai_chat_service_init_flag = 0; static uint8_t chat_client_ping = 0; +/* Chat 缓存相关变量 */ +#define CHAT_MAX_CACHED_LENGTH 10240 // 最大缓存文本总长度 +static char *chat_cached_text = NULL; // 缓存待发送的文本字符串(动态分配) +static int chat_cached_length = 0; // 当前缓存的文本长度 +static int chat_cached_capacity = 0; // 当前分配的内存容量 + /* 外部函数声明 */ extern bool read_dev_token(char *data_token); extern bool get_dev_id(char *device_id); // extern void uapi_cat1_keep_wakeup(uint8_t state); // Commented out due to missing implementation + +/** + * @brief 初始化Chat缓存内存 + * @return true 成功,false 失败 + */ +static bool chat_cache_init(void) +{ + if (chat_cached_text == NULL) { + chat_cached_text = (char*)malloc(CHAT_MAX_CACHED_LENGTH); + if (chat_cached_text == NULL) { + printf("Failed to allocate memory for Chat cache\n"); + return false; + } + chat_cached_capacity = CHAT_MAX_CACHED_LENGTH; + chat_cached_length = 0; + memset(chat_cached_text, 0, CHAT_MAX_CACHED_LENGTH); + printf("Chat cache initialized with %d bytes\n", CHAT_MAX_CACHED_LENGTH); + } + return true; +} + +/** + * @brief 释放Chat缓存内存 + */ +static void chat_cache_deinit(void) +{ + if (chat_cached_text != NULL) { + free(chat_cached_text); + chat_cached_text = NULL; + chat_cached_capacity = 0; + chat_cached_length = 0; + printf("Chat cache deinitialized\n"); + } +} + +/** + * @brief 确保Chat缓存有足够的空间 + * @param required_size 需要的空间大小 + * @return true 成功,false 失败 + */ +static bool chat_cache_ensure_capacity(int required_size) +{ + if (chat_cached_text == NULL) { + return chat_cache_init(); + } + + if (required_size > chat_cached_capacity) { + // 需要扩容 + int new_capacity = (required_size > CHAT_MAX_CACHED_LENGTH) ? + required_size + 1024 : CHAT_MAX_CACHED_LENGTH; + + char *new_buffer = (char*)realloc(chat_cached_text, new_capacity); + if (new_buffer == NULL) { + printf("Failed to reallocate Chat cache memory\n"); + return false; + } + + chat_cached_text = new_buffer; + chat_cached_capacity = new_capacity; + printf("Chat cache expanded to %d bytes\n", new_capacity); + } + + return true; +} extern void ble_set_ai_Answer_str(char *str, uint8_t type); extern void msleep(uint32_t ms); @@ -82,17 +155,19 @@ void set_ai_chat_service_init_flag(uint8_t flag) /** * @brief Chat结果分析 */ -bool chat_result_analysis(char *buf) +chat_event_type_t chat_result_analysis(char *buf, char *out_put, size_t out_put_size) { extern cJSON* cJSON_Parse(const char *value); extern cJSON* cJSON_GetObjectItem(const cJSON * const object, const char * const string); extern int cJSON_IsString(const cJSON * const item); extern void cJSON_Delete(cJSON *c); + chat_event_type_t event_type = CHAT_EVENT_UNKNOWN; + cJSON* root = cJSON_Parse((const char *)buf); if (root == NULL) { printf("Chat result parse failed\n"); - return false; + return CHAT_EVENT_ERROR; } char *type_str = NULL; @@ -108,32 +183,54 @@ bool chat_result_analysis(char *buf) content_str = content->valuestring; } - if(strcmp("middle", type_str) == 0) - { - if(strlen(content_str) > 0) - { - char *content_str_chat = (char *)malloc(strlen(content_str) + 1); - strcpy(content_str_chat, content_str); - ble_set_ai_Answer_str(content_str_chat, 2); - free(content_str_chat); + // 根据type字段确定事件类型 + if (type_str != NULL) { + if (strcmp("start", type_str) == 0) { + event_type = CHAT_EVENT_START; + } else if (strcmp("middle", type_str) == 0) { + event_type = CHAT_EVENT_MIDDLE; + } else if (strcmp("end", type_str) == 0) { + event_type = CHAT_EVENT_END; + } else if (strcmp("error", type_str) == 0) { + event_type = CHAT_EVENT_ERROR; + } else { + event_type = CHAT_EVENT_UNKNOWN; } } - if(strcmp("end", type_str) == 0) - { - char end_str_chat[128] = {0}; - if(strlen(content_str) > 0) - { + // 处理内容输出 + if (content_str != NULL && strlen(content_str) > 0) { + // 如果提供了输出缓冲区,复制内容到输出缓冲区 + if (out_put != NULL && out_put_size > 0) { + size_t copy_size = (strlen(content_str) < out_put_size - 1) ? + strlen(content_str) : out_put_size - 1; + strncpy(out_put, content_str, copy_size); + out_put[copy_size] = '\0'; + } + + // 原有的处理逻辑保持不变 + if (event_type == CHAT_EVENT_MIDDLE) { + char *content_str_chat = (char *)malloc(strlen(content_str) + 1); + if (content_str_chat != NULL) { + strcpy(content_str_chat, content_str); + ble_set_ai_Answer_str(content_str_chat, 2); + free(content_str_chat); + } + } else if (event_type == CHAT_EVENT_END) { + char end_str_chat[128] = {0}; memset(end_str_chat, 0, 128); - memcpy(end_str_chat, content_str, strlen(content_str)); + memcpy(end_str_chat, content_str, strlen(content_str) < 127 ? strlen(content_str) : 127); ble_set_ai_Answer_str(end_str_chat, 2); } } cJSON_Delete(root); - return true; + return event_type; } + + + /** * @brief Chat URL初始化 */ @@ -188,12 +285,69 @@ bool chat_ws_url_init(char * put_buf, uint8_t type) /** * @brief 发送聊天文本 + * @param type 消息类型 + * @param msg 要发送的消息 + * @param chat_client WebSocket客户端 + * @param cache 是否缓存,false: 缓存文本,true: 发送缓存+当前文本 + * @return true 成功,false 失败 */ -bool ai_chat_send_txt(uint8_t type, char *msg) +bool ai_chat_send_txt(uint8_t type, char *msg, WebSocketClient *chat_client, bool cache) { if(msg) - printf("ai_chat_send_txt message: %s\n", msg); + printf("ai_chat_send_txt message: %s, cache: %s\n", msg, cache ? "true" : "false"); + + // 如果没有传入chat_client,使用全局变量 + if (chat_client == NULL) { + extern WebSocketClient *chat_client; + // 使用全局chat_client + printf("chat_client ==null\r\n"); + } + // 如果cache为false,将文本追加到缓存 + if (!cache) { + if (msg && strlen(msg) > 0) { + int msg_len = strlen(msg); + int required_size = chat_cached_length + msg_len + 1; // 预留结束符空间 + + // 确保有足够的空间 + if (!chat_cache_ensure_capacity(required_size)) { + printf("Chat cache allocation failed\n"); + return false; + } + + // 追加文本到缓存字符串 + strcat(chat_cached_text, msg); + chat_cached_length += msg_len; + printf("Chat text appended to cache (length: %d/%d): %s\n", chat_cached_length, chat_cached_capacity, msg); + } + return true; + } + + // 如果cache为true,先发送缓存的文本,然后发送当前文本 + char *final_msg = msg; + + if (chat_cached_length > 0) { + printf("Chat sending cached text (length: %d): %s\n", chat_cached_length, chat_cached_text); + + // 将缓存文本和当前文本组合 + int msg_len = msg ? strlen(msg) : 0; + int total_len = chat_cached_length + msg_len + 1; + + if (!chat_cache_ensure_capacity(total_len)) { + printf("Chat cache allocation failed for combined text\n"); + return false; + } + + // 如果当前有消息,追加到缓存文本 + if (msg && strlen(msg) > 0) { + strcat(chat_cached_text, msg); + chat_cached_length += msg_len; + } + + final_msg = chat_cached_text; + printf("Chat combined text: %s\n", final_msg); + } + extern cJSON* cJSON_CreateObject(void); extern cJSON* cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); extern char* cJSON_PrintUnformatted(const cJSON *item); @@ -208,7 +362,7 @@ bool ai_chat_send_txt(uint8_t type, char *msg) } cJSON_AddStringToObject(dataJSON, "type", "jiutian-lan"); - cJSON_AddStringToObject(dataJSON, "content", msg); + cJSON_AddStringToObject(dataJSON, "content", final_msg); char *jsonString = cJSON_PrintUnformatted(dataJSON); @@ -227,16 +381,30 @@ bool ai_chat_send_txt(uint8_t type, char *msg) return false; } - if (send_websocket_message(chat_client, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0) + // 使用传入的chat_client或全局chat_client + WebSocketClient *client_to_use = chat_client; + if (client_to_use == NULL) { + extern WebSocketClient *chat_client; + client_to_use = chat_client; + } + + if (send_websocket_message(client_to_use, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0) { chat_reconnect = 0; printf("Chat message sent successfully\n"); cJSON_free(jsonString); + + // 清空缓存 + if (chat_cached_text != NULL) { + memset(chat_cached_text, 0, chat_cached_capacity); + } + chat_cached_length = 0; + return true; } else { - if(chat_client == NULL) + if(client_to_use == NULL) { printf("chat_client is NULL\n"); } @@ -246,6 +414,44 @@ bool ai_chat_send_txt(uint8_t type, char *msg) } } +/** + * @brief 初始化Chat缓存内存 + * @return true 成功,false 失败 + */ +bool ai_chat_cache_init(void) +{ + return chat_cache_init(); +} + +/** + * @brief 释放Chat缓存内存 + */ +void ai_chat_cache_deinit(void) +{ + chat_cache_deinit(); +} + +/** + * @brief 清空Chat缓存 + */ +void ai_chat_clear_cache(void) +{ + if (chat_cached_text != NULL) { + memset(chat_cached_text, 0, chat_cached_capacity); + } + chat_cached_length = 0; + printf("Chat cache cleared\n"); +} + +/** + * @brief 获取Chat缓存长度 + * @return 当前缓存的文本长度 + */ +int ai_chat_get_cached_length(void) +{ + return chat_cached_length; +} + /** * @brief Chat WebSocket主线程 */ @@ -395,7 +601,7 @@ CHAT_START: if (msg_type == WS_TEXT) { printf("Chat text data received: %s\n", recv_buf); - chat_result_analysis(recv_buf); + // chat_result_analysis(recv_buf); } else if (msg_type == WS_BINARY) { printf("Chat binary data received: %d bytes\n", len); } diff --git a/code/sdk/zhihui/ai_old/chat_websocket_server.h b/code/sdk/zhihui/ai_old/chat_websocket_server.h index 9d50a0e37..1b50825fe 100644 --- a/code/sdk/zhihui/ai_old/chat_websocket_server.h +++ b/code/sdk/zhihui/ai_old/chat_websocket_server.h @@ -22,6 +22,16 @@ extern "C" { /* Chat 服务相关类型定义 */ // errcode_t is defined in errcode.h +/* Chat事件类型枚举 */ +typedef enum { + CHAT_EVENT_UNKNOWN = 0, // 未知事件 + CHAT_EVENT_MIDDLE, // 中间结果事件 + CHAT_EVENT_END, // 结束事件 + CHAT_EVENT_ERROR, // 错误事件 + CHAT_EVENT_START, // 开始事件 + CHAT_EVENT_MAX +} chat_event_type_t; + /* AI类型枚举 - 使用zhihui_ws.h中的定义 */ /* 任务配置结构体 - 使用系统定义的 app_task_definition_t */ @@ -55,9 +65,11 @@ void set_ai_chat_service_init_flag(uint8_t flag); /** * @brief Chat结果分析 * @param buf 结果字符串 - * @return 成功返回true,失败返回false + * @param out_put 输出内容缓冲区(可选,为NULL时不输出内容) + * @param out_put_size 输出缓冲区大小 + * @return 返回事件类型枚举值 */ -bool chat_result_analysis(char *buf); +chat_event_type_t chat_result_analysis(char *buf, char *out_put, size_t out_put_size); /** * @brief Chat URL初始化 @@ -71,9 +83,32 @@ bool chat_ws_url_init(char * put_buf, uint8_t type); * @brief 发送聊天文本 * @param type 消息类型 * @param msg 消息内容 + * @param chat_client WebSocket客户端(可选,为NULL时使用全局chat_client) * @return 成功返回true,失败返回false */ -bool ai_chat_send_txt(uint8_t type, char *msg); +bool ai_chat_send_txt(uint8_t type, char *msg, WebSocketClient *chat_client, bool cache); + +/** + * @brief 初始化Chat缓存内存 + * @return true 成功,false 失败 + */ +bool ai_chat_cache_init(void); + +/** + * @brief 释放Chat缓存内存 + */ +void ai_chat_cache_deinit(void); + +/** + * @brief 清空Chat缓存 + */ +void ai_chat_clear_cache(void); + +/** + * @brief 获取Chat缓存长度 + * @return 当前缓存的文本长度 + */ +int ai_chat_get_cached_length(void); /** * @brief Chat WebSocket主线程 @@ -92,6 +127,8 @@ errcode_t chat_server_init(void); */ void chat_server_deinit(void); +// bool ai_chat_send_txt(uint8_t type, char *msg, WebSocketClient *chat_client, bool cache) + /* 全局变量声明 */ extern WebSocketClient *chat_client; diff --git a/code/sdk/zhihui/ai_old/tts_websocket_server.cpp b/code/sdk/zhihui/ai_old/tts_websocket_server.cpp index ee6afb3f1..b6fbaf756 100644 --- a/code/sdk/zhihui/ai_old/tts_websocket_server.cpp +++ b/code/sdk/zhihui/ai_old/tts_websocket_server.cpp @@ -36,7 +36,7 @@ static uint8_t tts_break = 0; static uint8_t tts_reconnect = 0; static uint8_t is_bk_websocket_tts = 0; static uint8_t ai_tts_server_init_flag = 0; -static uint8_t tts_is_ready = 0; +static tts_ready_status_t tts_is_ready = TTS_READY_NOT_SENT; static uint8_t tts_client_ping = 0; static uint32_t bk_websocket_tts_tick = 0; @@ -48,6 +48,8 @@ extern int send_msg_data_into_Ai_msg_queue(void *data, uint16_t len, uint16_t ty extern void msleep(uint32_t ms); extern int32_t VoiceCallVolteSample(int32_t argc, const char **argv); +static bool uapi_send_single_tts_text(WebSocketClient *tts_client, const char *text); + /* TTS 配置和常量 */ #define AI_MAX_RETRY_COUNT 3 #define AI_WS_HANDSHAKE_TIMEOUT_MS 10000 @@ -55,6 +57,77 @@ extern int32_t VoiceCallVolteSample(int32_t argc, const char **argv); static char tts_task_id_buf[128] = {0}; +/* TTS 缓存相关变量 */ +#define TTS_MAX_CACHED_LENGTH 1024 // 最大缓存文本总长度 +static char *tts_cached_text = NULL; // 缓存待发送的文本字符串(动态分配) +static int tts_cached_length = 0; // 当前缓存的文本长度 +static int tts_cached_capacity = 0; // 当前分配的内存容量 +// static bool tts_is_ready = false; // TTS是否准备就绪 + +/** + * @brief 初始化TTS缓存内存 + * @return true 成功,false 失败 + */ +static bool tts_cache_init(void) +{ + if (tts_cached_text == NULL) { + tts_cached_text = (char*)malloc(TTS_MAX_CACHED_LENGTH); + if (tts_cached_text == NULL) { + printf("Failed to allocate memory for TTS cache\n"); + return false; + } + tts_cached_capacity = TTS_MAX_CACHED_LENGTH; + tts_cached_length = 0; + memset(tts_cached_text, 0, TTS_MAX_CACHED_LENGTH); + printf("TTS cache initialized with %d bytes\n", TTS_MAX_CACHED_LENGTH); + } + return true; +} + +/** + * @brief 释放TTS缓存内存 + */ +static void tts_cache_deinit(void) +{ + if (tts_cached_text != NULL) { + free(tts_cached_text); + tts_cached_text = NULL; + tts_cached_capacity = 0; + tts_cached_length = 0; + printf("TTS cache deinitialized\n"); + } +} + +/** + * @brief 确保TTS缓存有足够的空间 + * @param required_size 需要的空间大小 + * @return true 成功,false 失败 + */ +static bool tts_cache_ensure_capacity(int required_size) +{ + if (tts_cached_text == NULL) { + return tts_cache_init(); + } + + if (required_size > tts_cached_capacity) { + // 需要扩容 + int new_capacity = (required_size > TTS_MAX_CACHED_LENGTH) ? + required_size + 1024 : TTS_MAX_CACHED_LENGTH; + + char *new_buffer = (char*)realloc(tts_cached_text, new_capacity); + if (new_buffer == NULL) { + printf("Failed to reallocate TTS cache memory\n"); + return false; + } + + tts_cached_text = new_buffer; + tts_cached_capacity = new_capacity; + printf("TTS cache expanded to %d bytes\n", new_capacity); + } + + return true; +} + /** * @brief TTS WebSocket数据包回调函数 */ @@ -197,12 +270,90 @@ bool uapi_start_tts(void) } /** - * @brief 启动单个TTS文本合成 + * @brief TTS文本处理函数(组合版本) + * @param tts_client TTS WebSocket客户端 + * @param buf 要处理的文本 + * @param ready true: 清理缓存并发送本次文本, false: 将文本添加到缓存 + * @return true 成功,false 失败 + */ +bool uapi_tts_process_text(WebSocketClient *tts_client, char *buf, bool ready) +{ + if (tts_client == NULL) { + printf("TTS client is NULL\n"); + return false; + } + + // 如果ready为false,将文本追加到缓存字符串 + if (!ready) { + if (buf && strlen(buf) > 0) { + int buf_len = strlen(buf); + int required_size = tts_cached_length + buf_len + 1; // 预留结束符空间 + + // 确保有足够的空间 + if (!tts_cache_ensure_capacity(required_size)) { + printf("TTS cache allocation failed\n"); + return false; + } + + // 追加文本到缓存字符串 + strcat(tts_cached_text, buf); + tts_cached_length += buf_len; + printf("TTS text appended to cache (length: %d/%d): %s\n", tts_cached_length, tts_cached_capacity, buf); + } + return true; + } + + // 如果ready为true,先发送缓存的文本,然后发送本次携带的内容 + if (tts_cached_length > 0) { + printf("TTS ready, sending cached text (length: %d): %s\n", tts_cached_length, tts_cached_text); + bool result = uapi_send_single_tts_text(tts_client, tts_cached_text); + if (!result) { + printf("Failed to send cached text: %s\n", tts_cached_text); + return false; + } else { + printf("Successfully sent cached text: %s\n", tts_cached_text); + } + + // 清空缓存 + if (tts_cached_text != NULL) { + memset(tts_cached_text, 0, tts_cached_capacity); + } + tts_cached_length = 0; + } + + // 发送本次携带的内容 + if (buf && strlen(buf) > 0) { + printf("TTS ready, sending current text: %s\n", buf); + bool result = uapi_send_single_tts_text(tts_client, buf); + if (!result) { + printf("Failed to send current text: %s\n", buf); + return false; + } else { + printf("Successfully sent current text: %s\n", buf); + } + } else { + printf("TTS ready but no current text to send\n"); + } + + return true; +} + // "{\"header\" : { \ + // \"event\": \"RunSynthesis\", \ + // \"trace_id\": \"%s\" \ + // }, \ + // \"payload\": { \ + // \"text\":\"%s\" \ + // }}" + +/** + * @brief 发送单个TTS文本(内部函数) + * @param tts_client TTS WebSocket客户端 + * @param text 要发送的文本 + * @return true 成功,false 失败 */ -bool uapi_start_tts_single_txt(char *buf) +static bool uapi_send_single_tts_text(WebSocketClient *tts_client, const char *text) { - if (!buf || strlen(buf) == 0) { - printf("TTS text buffer is empty\n"); + if (!text || strlen(text) == 0) { return false; } @@ -211,34 +362,150 @@ bool uapi_start_tts_single_txt(char *buf) return false; } - // 构建TTS请求JSON - char *json_template = - "{" - "\"type\":\"text\"," - "\"task_id\":\"%s\"," - "\"text\":\"%s\"" - "}"; + // 使用cJSON构建TTS请求 + cJSON *json = cJSON_CreateObject(); + if (json == NULL) { + printf("Failed to create JSON object\n"); + return false; + } + + // 创建header对象 + cJSON *header = cJSON_CreateObject(); + if (header == NULL) { + printf("Failed to create header object\n"); + cJSON_Delete(json); + return false; + } - char *jsonString = (char *)malloc(strlen(json_template) + strlen(buf) + strlen(tts_task_id_buf) + 100); - if (!jsonString) { - printf("Failed to allocate memory for TTS JSON\n"); + // 添加header字段 + if (cJSON_AddStringToObject(header, "event", "RunSynthesis") == NULL) { + printf("Failed to add event field\n"); + cJSON_Delete(header); + cJSON_Delete(json); return false; } - sprintf(jsonString, json_template, tts_task_id_buf, buf); + if (cJSON_AddStringToObject(header, "trace_id", "") == NULL) { + printf("Failed to add trace_id field\n"); + cJSON_Delete(header); + cJSON_Delete(json); + return false; + } + // 将header添加到根对象 + if (cJSON_AddItemToObject(json, "header", header) == 0) { + printf("Failed to add header to root object\n"); + cJSON_Delete(header); + cJSON_Delete(json); + return false; + } + + // 创建payload对象 + cJSON *payload = cJSON_CreateObject(); + if (payload == NULL) { + printf("Failed to create payload object\n"); + cJSON_Delete(json); + return false; + } + + // 添加payload字段 + if (cJSON_AddStringToObject(payload, "text", text) == NULL) { + printf("Failed to add text field\n"); + cJSON_Delete(payload); + cJSON_Delete(json); + return false; + } + + // 将payload添加到根对象 + if (cJSON_AddItemToObject(json, "payload", payload) == 0) { + printf("Failed to add payload to root object\n"); + cJSON_Delete(payload); + cJSON_Delete(json); + return false; + } + + // 生成JSON字符串 + char *jsonString = cJSON_Print(json); + if (jsonString == NULL) { + printf("Failed to print JSON to string\n"); + cJSON_Delete(json); + return false; + } + + // 发送WebSocket消息 bool result = false; if (send_websocket_message(tts_client, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0) { - printf("TTS text message sent successfully: %s\n", buf); result = true; + printf("TTS text %s\n", jsonString); } else { printf("TTS text message send failed\n"); } + // 清理资源 free(jsonString); + cJSON_Delete(json); + return result; } +/** + * @brief 获取TTS就绪状态 + * @return true 准备就绪,false 未准备就绪 + */ +tts_ready_status_t uapi_get_tts_ready(void) +{ + return tts_is_ready; +} + +/** + * @brief 获取缓存的文本数量 + * @return 缓存的文本数量 + */ +int uapi_get_cached_text_count(void) +{ + return tts_cached_length; +} + +/** + * @brief 清空TTS缓存 + */ +void uapi_clear_tts_cache(void) +{ + if (tts_cached_text != NULL) { + memset(tts_cached_text, 0, tts_cached_capacity); + } + tts_cached_length = 0; + printf("TTS cache cleared\n"); +} + +/** + * @brief 初始化TTS缓存内存 + * @return true 成功,false 失败 + */ +bool uapi_tts_cache_init(void) +{ + return tts_cache_init(); +} + +/** + * @brief 释放TTS缓存内存 + */ +void uapi_tts_cache_deinit(void) +{ + tts_cache_deinit(); +} + +/** + * @brief TTS文本处理函数(使用全局客户端,向后兼容) + * @param buf 要处理的文本 + * @param ready true: 清理缓存并发送本次文本, false: 将文本添加到缓存 + * @return true 成功,false 失败 + */ +bool uapi_tts_process_text_global(char *buf, bool ready) +{ + return uapi_tts_process_text(tts_client, buf, ready); +} + /** * @brief 停止TTS服务 */ @@ -510,6 +777,83 @@ void tts_server_deinit(void) set_ai_tts_server_init_flag(0); } + + +// {"header":{"event":"StartedSynthesis","msg":"success","status":20000000,"task_id":"8e8996f9148b66738309602ec3219412"}} +tts_event_type_t tts_analysis(char *buf) +{ + tts_event_type_t event_type = TTS_EVENT_UNKNOWN; + + cJSON* root = cJSON_Parse((const char *)buf); + if (root == NULL) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"解析失败\n"); + return TTS_EVENT_ERROR; + } + + char *event_str = NULL; + char *task_id_str = NULL; + char *msg_str = NULL; + + cJSON* header = cJSON_GetObjectItem(root, "header"); + if (header) { + cJSON* event = cJSON_GetObjectItem(header, "event"); + if (event && cJSON_IsString(event)) { + event_str = event->valuestring; + } + + cJSON* msg = cJSON_GetObjectItem(header, "msg"); + if (msg && cJSON_IsString(msg)) { + msg_str = msg->valuestring; + } + + cJSON* task_id = cJSON_GetObjectItem(header, "task_id"); + if (task_id && cJSON_IsString(task_id)) { + // memcpy_s(tts_task_id_buf, 128, task_id->valuestring, strlen(task_id->valuestring)); + } + + // 根据事件类型和消息状态确定事件类型 + if (event_str != NULL) { + if (strcmp("StartedSynthesis", event_str) == 0) { + if (msg_str != NULL && strcmp("success", msg_str) == 0) { + event_type = TTS_EVENT_STARTED_SYNTHESIS; + tts_is_ready = TTS_READY_SUCCESS_READY; + + // TTS准备就绪,发送缓存的文本(如果有的话) + if (tts_cached_length > 0) { + printf("TTS is ready, sending cached text (length: %d): %s\n", tts_cached_length, tts_cached_text); + if (tts_client != NULL) { + bool result = uapi_send_single_tts_text(tts_client, tts_cached_text); + if (result) { + printf("Successfully sent cached text: %s\n", tts_cached_text); + // 清空缓存 + memset(tts_cached_text, 0, tts_cached_capacity); + tts_cached_length = 0; + } else { + printf("Failed to send cached text: %s\n", tts_cached_text); + } + } + } + } else { + event_type = TTS_EVENT_SYNTHESIS_ERROR; + } + } else if (strcmp("SynthesisComplete", event_str) == 0) { + event_type = TTS_EVENT_SYNTHESIS_COMPLETE; + } else if (strcmp("AudioData", event_str) == 0) { + event_type = TTS_EVENT_AUDIO_DATA; + } else if (strcmp("TaskComplete", event_str) == 0) { + event_type = TTS_EVENT_TASK_COMPLETE; + } else if (strcmp("Error", event_str) == 0) { + event_type = TTS_EVENT_ERROR; + } else { + event_type = TTS_EVENT_UNKNOWN; + } + } + } + + cJSON_Delete(root); + return event_type; +} + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/code/sdk/zhihui/ai_old/tts_websocket_server.h b/code/sdk/zhihui/ai_old/tts_websocket_server.h index b08e766ad..e819219f3 100644 --- a/code/sdk/zhihui/ai_old/tts_websocket_server.h +++ b/code/sdk/zhihui/ai_old/tts_websocket_server.h @@ -20,6 +20,25 @@ extern "C" { /* TTS 服务相关类型定义 - errcode_t is defined in errcode.h */ +/* TTS事件类型枚举 */ +typedef enum { + TTS_EVENT_UNKNOWN = 0, // 未知事件 + TTS_EVENT_STARTED_SYNTHESIS, // 开始合成事件 + TTS_EVENT_SYNTHESIS_COMPLETE, // 合成完成事件 + TTS_EVENT_SYNTHESIS_ERROR, // 合成错误事件 + TTS_EVENT_AUDIO_DATA, // 音频数据事件 + TTS_EVENT_TASK_COMPLETE, // 任务完成事件 + TTS_EVENT_ERROR, // 错误事件 + TTS_EVENT_MAX +} tts_event_type_t; + +/* TTS就绪状态枚举 */ +typedef enum { + TTS_READY_NOT_SENT = 0, // 未发送 + TTS_READY_SENT_SUCCESS, // 发送成功 + TTS_READY_SUCCESS_READY // 成功就绪 +} tts_ready_status_t; + /* 任务配置结构体 - 使用系统定义的 app_task_definition_t */ /* 常量定义 */ @@ -92,11 +111,49 @@ int32_t uapi_tts_paly_stop(void); bool uapi_start_tts(void); /** - * @brief 启动单个TTS文本合成 - * @param buf 文本内容 + * @brief TTS文本处理函数(组合版本) + * @param tts_client TTS WebSocket客户端 + * @param buf 要处理的文本 + * @param ready true: 清理缓存并发送本次文本, false: 将文本添加到缓存 * @return 成功返回true,失败返回false */ -bool uapi_start_tts_single_txt(char *buf); +bool uapi_tts_process_text(WebSocketClient *tts_client, char *buf, bool ready); + +/** + * @brief 获取TTS就绪状态 + * @return true 准备就绪,false 未准备就绪 + */ +tts_ready_status_t uapi_get_tts_ready(void); + +/** + * @brief 获取缓存的文本数量 + * @return 缓存的文本数量 + */ +int uapi_get_cached_text_count(void); + +/** + * @brief 清空TTS缓存 + */ +void uapi_clear_tts_cache(void); + +/** + * @brief 初始化TTS缓存内存 + * @return true 成功,false 失败 + */ +bool uapi_tts_cache_init(void); + +/** + * @brief 释放TTS缓存内存 + */ +void uapi_tts_cache_deinit(void); + +/** + * @brief TTS文本处理函数(使用全局客户端,向后兼容) + * @param buf 要处理的文本 + * @param ready true: 清理缓存并发送本次文本, false: 将文本添加到缓存 + * @return 成功返回true,失败返回false + */ +bool uapi_tts_process_text_global(char *buf, bool ready); /** * @brief 停止TTS服务 @@ -120,6 +177,13 @@ errcode_t tts_server_init(void); */ void tts_server_deinit(void); +/** + * @brief TTS结果分析 + * @param buf 结果字符串 + * @return 返回事件类型枚举值 + */ +tts_event_type_t tts_analysis(char *buf); + /* 全局变量声明 */ extern WebSocketClient *tts_client; diff --git a/code/sdk/zhihui/ai_old/websocket_server_example.cpp b/code/sdk/zhihui/ai_old/websocket_server_example.cpp new file mode 100644 index 000000000..d89cd31fd --- /dev/null +++ b/code/sdk/zhihui/ai_old/websocket_server_example.cpp @@ -0,0 +1,1034 @@ +/** + * @file websocket_server_example.cpp + * @brief WebSocket服务器使用示例 + * @version 1.0.0 + * @date 2024-09-05 + * + * 展示如何使用统一的WebSocket服务器管理接口 + */ + +#include "websocket_server_manager.h" +#include +#include +#include +#include "ai_core_types.h" +#include "ai_connection_manager.h" +#include "ai_media.h" +#include "cJSON.h" +#include "asr_websocket_server.h" +#include "chat_websocket_server.h" +#include "tts_websocket_server.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* 全局服务器句柄 */ +static ws_server_handle_t *g_asr_server = NULL; +static ws_server_handle_t *g_tts_server = NULL; +static ws_server_handle_t *g_chat_server = NULL; + +static uint8_t asr_is_ready=0; + +tts_ready_status_t tts_is_ready = TTS_READY_NOT_SENT; + +/* 外部函数声明 */ +extern void ble_set_ai_Question_str(char *str); +extern void ble_set_ai_Answer_str(char *str, uint8_t type); +extern int send_msg_data_into_Ai_msg_queue(void *data, uint16_t len, uint16_t type); +extern void set_AIStreamend(uint8_t state); +extern bool uapi_start_tts(void); +// extern bool ai_chat_send_txt(uint8_t type, char *msg, WebSocketClient *chat_client); +extern bool read_dev_token(char *data_token); +extern bool get_dev_id(char *device_id); + +void handle_asr_data(const ws_event_data_t *event_data); + +void handle_tts_data(const ws_event_data_t *event_data); + +void handle_chat_data(const ws_event_data_t *event_data); + +char* extract_json_content( char *json_str, char *key); + +void parse_chat_result(char *json_str); + +void uapi_stop_tts_type_stop(void); + +bool uapi_start_tts_type_start(void); + +void ai_media_init(void); + +void uapi_stop_asr(void); + +/** + * @brief WebSocket事件回调函数 + */ +void websocket_event_callback(const ws_event_data_t *event_data) +{ + if (!event_data) { + return; + } + + // handle->callback(&event_data); + + switch (event_data->event_type) { + case WS_EVENT_CONNECTED: + printf("WebSocket connected: type=%d\n", event_data->server_type); + break; + + case WS_EVENT_DATA_RECEIVED: + printf("WebSocket data received: type=%d, len=%d\n", + event_data->server_type, event_data->data_len); + + // 根据服务器类型处理数据 + switch (event_data->server_type) { + case WS_SERVER_TYPE_ASR: + handle_asr_data(event_data); + break; + case WS_SERVER_TYPE_TTS: + handle_tts_data(event_data); + break; + case WS_SERVER_TYPE_CHAT: + handle_chat_data(event_data); + break; + default: + break; + } + break; + + case WS_EVENT_DISCONNECTED: + printf("WebSocket disconnected: type=%d\n", event_data->server_type); + // 可以在这里添加断连后的处理逻辑 + switch (event_data->server_type) { + case WS_SERVER_TYPE_ASR: + // handle_asr_data(event_data); + asr_is_ready=0; + break; + case WS_SERVER_TYPE_TTS: + tts_is_ready = TTS_READY_NOT_SENT; + // handle_tts_data(event_data); + break; + case WS_SERVER_TYPE_CHAT: + // handle_chat_data(event_data); + break; + default: + break; + } + break; + + case WS_EVENT_RECONNECTING: + printf("WebSocket reconnecting: type=%d\n", event_data->server_type); + // 可以在这里添加重连前的处理逻辑 + switch (event_data->server_type) { + case WS_SERVER_TYPE_ASR: + // handle_asr_data(event_data); + asr_is_ready=0; + break; + case WS_SERVER_TYPE_TTS: + tts_is_ready = TTS_READY_NOT_SENT; + // handle_tts_data(event_data); + break; + case WS_SERVER_TYPE_CHAT: + // handle_chat_data(event_data); + break; + default: + break; + } + break; + + case WS_EVENT_ERROR: + printf("WebSocket error: type=%d, error_code=%d\n", + event_data->server_type, event_data->error_code); + break; + + default: + break; + } +} + + +extern asr_event_type_t aer_result_analysis(char *buf, asr_result_output_t *output); +/** + * @brief 处理ASR数据 + */ +void handle_asr_data(const ws_event_data_t *event_data) +{ + if (event_data->msg_type == WS_TEXT && event_data->data) { + printf("ASR text data: %s\n", (char *)event_data->data); + + // 解析ASR结果并发送到聊天服务 + if (strlen((char *)event_data->data) > 0) { + asr_result_output_t data; + int asr_event_type_t= aer_result_analysis((char *)event_data->data,&data); + switch (asr_event_type_t) + { + case ASR_EVENT_TRANSCRIPTION_STARTED : + // /* code */ + asr_is_ready=1; + break; + case ASR_EVENT_FAILED : + // /* code */ + asr_is_ready=0; + break; + case ASR_EVENT_RESULT : + // /* code */ + // asr_is_ready=0; + if(data.is_end) + { + ai_chat_send_txt(0, data.result_text, g_chat_server->client,false); + ble_set_ai_Question_str(data.result_text); + + // uapi_stop_asr_StopTranscription(); + } + + break; + + case ASR_EVENT_TRANSCRIPTION_COMPLETED: + ai_chat_send_txt(0, "", g_chat_server->client,true); + asr_is_ready=0; + break; + default: + break; + } + } + } else if (event_data->msg_type == WS_BINARY) { + printf("ASR binary data: %d bytes\n", event_data->data_len); + send_msg_data_into_Ai_msg_queue(event_data->data, event_data->data_len, 100); + } +} + + + +/** + * @brief 处理TTS数据 + */ +void handle_tts_data(const ws_event_data_t *event_data) +{ + if (event_data->msg_type == WS_BINARY) { + printf("TTS binary data: %d bytes\n", event_data->data_len); + set_AIStreamend(0); + // AI_WriteAudioData(event_data->data, event_data->data_len); + AI_SendAudioDataToQueue(event_data->data,event_data->data_len); + // send_msg_data_into_Ai_msg_queue(event_data->data, event_data->data_len, 200); + } else if (event_data->msg_type == WS_TEXT) { + printf("TTS text data: %s\n", (char *)event_data->data); + tts_event_type_t event = tts_analysis((char *)event_data->data); + + switch (event) { + case TTS_EVENT_STARTED_SYNTHESIS: + printf("TTS synthesis started successfully\n"); + tts_is_ready = TTS_READY_SUCCESS_READY; + uapi_tts_process_text(g_tts_server->client,"",(tts_is_ready == TTS_READY_SUCCESS_READY)); + break; + case TTS_EVENT_SYNTHESIS_COMPLETE: + printf("TTS synthesis completed\n"); + break; + case TTS_EVENT_AUDIO_DATA: + printf("TTS audio data received\n"); + break; + case TTS_EVENT_TASK_COMPLETE: + printf("TTS task completed\n"); + tts_is_ready = TTS_READY_NOT_SENT; + break; + case TTS_EVENT_SYNTHESIS_ERROR: + printf("TTS synthesis error\n"); + break; + case TTS_EVENT_ERROR: + printf("TTS general error\n"); + break; + case TTS_EVENT_UNKNOWN: + printf("TTS unknown event\n"); + break; + } + } +} + +/** + * @brief 处理Chat数据 + */ +void handle_chat_data(const ws_event_data_t *event_data) +{ + if ((event_data->msg_type == WS_TEXT || event_data->msg_type == WS_BINARY) && event_data->data) { + printf("Chat text data: %s\n", (char *)event_data->data); + + // 解析Chat结果 + char buf[512]={0}; + chat_event_type_t ret=chat_result_analysis((char*)event_data->data, buf, 512); + + // 根据事件类型进行处理 + switch (ret) { + case CHAT_EVENT_START: + printf("Chat event: START\n"); + if(tts_is_ready == TTS_READY_NOT_SENT) { + + tts_is_ready = TTS_READY_SENT_SUCCESS; + uapi_start_tts_type_start(); + + } + break; + + case CHAT_EVENT_MIDDLE: + printf("Chat event: MIDDLE - Content: %s\n", buf); + ble_set_ai_Answer_str(buf, 2); + uapi_tts_process_text(g_tts_server->client,buf,(tts_is_ready == TTS_READY_SUCCESS_READY)); + break; + + case CHAT_EVENT_END: + printf("Chat event: END - Final content: %s\n", buf); + uapi_stop_tts_type_stop(); + break; + + case CHAT_EVENT_ERROR: + printf("Chat event: ERROR\n"); + break; + + case CHAT_EVENT_UNKNOWN: + printf("Chat event: UNKNOWN\n"); + break; + + default: + printf("Chat event: Invalid type (%d)\n", ret); + break; + } + + } +} + +/** + * @brief 解析Chat结果 + */ +void parse_chat_result(char *json_str) +{ + // 这里可以添加JSON解析逻辑 + // 简化示例,直接显示结果 + if (strstr(json_str, "\"type\":\"middle\"")) { + // 中间结果 + char *content = extract_json_content(json_str, "content"); + if (content) { + ble_set_ai_Answer_str(content, 2); + free(content); + } + } else if (strstr(json_str, "\"type\":\"end\"")) { + // 最终结果 + char *content = extract_json_content(json_str, "content"); + if (content) { + ble_set_ai_Answer_str(content, 2); + free(content); + } + } +} + +/** + * @brief 提取JSON内容(简化实现) + */ +char* extract_json_content( char *json_str, char *key) +{ + // 这是一个简化的实现,实际应该使用cJSON库 + char *start = strstr(json_str, key); + if (!start) return NULL; + + start = strchr(start, '"'); + if (!start) return NULL; + start++; + + char *end = strchr(start, '"'); + if (!end) return NULL; + + size_t len = end - start; + char *result = (char *)malloc(len + 1); + if (result) { + strncpy(result, start, len); + result[len] = '\0'; + } + + return result; +} + +/** + * @brief ASR URL生成回调函数 + */ +bool asr_url_callback(ws_server_type_t type, char *url_buf, uint32_t url_size, void *user_data) +{ + char token[64] = {0}; + if (read_dev_token(token)) { + snprintf(url_buf, url_size, "ws://amp.raisound.com/asr/ws?dt=%s&platform=aliyun&noCache=1", token); + } else { + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/asr/ws"); + } + return true; +} + +/** + * @brief TTS URL生成回调函数 + */ +bool tts_url_callback(ws_server_type_t type, char *url_buf, uint32_t url_size, void *user_data) +{ + char token[64] = {0}; + if (read_dev_token(token)) { + snprintf(url_buf, url_size, "ws://amp.raisound.com/tts/stream?dt=%s&platform=aliyun&noCache=1", token); + } else { + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/tts/stream?audio_buffer_size=512"); + } + return true; +} + +/** + * @brief Chat URL生成回调函数 + */ +bool chat_url_callback(ws_server_type_t type, char *url_buf, uint32_t url_size, void *user_data) +{ + char token[64] = {0}; + if (read_dev_token(token)) { + char device_id[128] = {0}; + get_dev_id(device_id); + char *product_id = "1927646275585744898"; + snprintf(url_buf, url_size, "ws://amp.raisound.com/chat/ws?device_id=%s&product_id=%s&dt=%s&platform=aliyun&noCache=1", + device_id, product_id, token); + } else { + char device_id[128] = {0}; + get_dev_id(device_id); + char *product_id = "1927646275585744898"; + + // 根据AI类型选择不同的URL + uint8_t *ai_type = (uint8_t *)user_data; + if (ai_type && *ai_type == 1) { // AI_TYPE_JIU + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws?device_id=%s&product_id=%s&platform=chinamobiledevice&bot_type=jiutian-lan", + device_id, product_id); + } else if (ai_type && *ai_type == 2) { // AI_TYPE_DOU + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws?platform=huoshan"); + } else if (ai_type && *ai_type == 3) { // AI_TYPE_WEN + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws?platform=baidu"); + } else if (ai_type && *ai_type == 4) { // AI_TYPE_KIMI + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws?platform=moonshot"); + } else if (ai_type && *ai_type == 5) { // AI_TYPE_DE + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws?platform=huoshan&model=ep-20250205103849-582m7"); + } else { + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws?platform=aliyun"); + } + } + return true; +} + +/** + * @brief 初始化所有WebSocket服务器 + */ +errcode_t init_all_websocket_servers(void) +{ + // 初始化管理器 + errcode_t ret = ws_server_manager_init(); + if (ret != ERRCODE_SUCC) { + printf("Failed to initialize WebSocket manager\n"); + return ret; + } + + + // 注册URL回调函数 + ws_server_register_url_callback(WS_SERVER_TYPE_ASR, asr_url_callback, NULL); + ws_server_register_url_callback(WS_SERVER_TYPE_TTS, tts_url_callback, NULL); + + // Chat服务器需要AI类型参数 + static uint8_t chat_ai_type = 0; // 默认类型 + ws_server_register_url_callback(WS_SERVER_TYPE_CHAT, chat_url_callback, &chat_ai_type); + + // 创建ASR服务器 + ws_server_config_t asr_config = ws_server_get_default_config(WS_SERVER_TYPE_ASR); + asr_config.disconnect_strategy = WS_DISCONNECT_RECONNECT; // 设置自动重连 + asr_config.reconnect_interval_ms = 3000; // 3秒重连间隔 + g_asr_server = ws_server_create(&asr_config, websocket_event_callback, NULL); + if (!g_asr_server) { + printf("Failed to create ASR server\n"); + return ERRCODE_FAIL; + } + + // 创建TTS服务器 + ws_server_config_t tts_config = ws_server_get_default_config(WS_SERVER_TYPE_TTS); + tts_config.disconnect_strategy = WS_DISCONNECT_RECONNECT; // 设置自动重连 + tts_config.reconnect_interval_ms = 5000; // 5秒重连间隔 + g_tts_server = ws_server_create(&tts_config, websocket_event_callback, NULL); + if (!g_tts_server) { + printf("Failed to create TTS server\n"); + ws_server_destroy(g_asr_server); + return ERRCODE_FAIL; + } + + // 创建Chat服务器 + ws_server_config_t chat_config = ws_server_get_default_config(WS_SERVER_TYPE_CHAT); + chat_config.disconnect_strategy = WS_DISCONNECT_RECONNECT; // 设置自动重连 + chat_config.reconnect_interval_ms = 2000; // 2秒重连间隔 + g_chat_server = ws_server_create(&chat_config, websocket_event_callback, NULL); + if (!g_chat_server) { + printf("Failed to create Chat server\n"); + ws_server_destroy(g_asr_server); + ws_server_destroy(g_tts_server); + return ERRCODE_FAIL; + } + + printf("All WebSocket servers created successfully\n"); + return ERRCODE_SUCC; +} + +static void AIVoiceDataCallback_(const uint8_t* data, uint32_t size, void* userData) +{ + // MEDIA_INFO_LOG("Received audio data: size=%u bytes", size); + // 在这里处理音频数据,例如: + // - 发送到AI语音识别服务 + // - 保存到文件 + // - 进行音频处理等 + // 注意:data指针指向的缓冲区在回调函数返回后可能被释放,如需保存数据请复制 + if (g_asr_server == NULL || !g_asr_server->is_initialized) { + printf("Services context not initialized"); + return; + } + // printf("Audio record callback, size: %d", size); + + /* 🆕 根据ASR服务启动标识决定是否发送音频数据 */ + if (asr_is_ready) { + // printf("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); + // g_asr_server + ws_server_send_message(g_asr_server,( uint8_t *)data,size,WS_BINARY); + // if (ret != AI_OK) { + // printf("Failed to send audio data to ASR service: %d", ret); + // } + } + +} + + +/** + * @brief 启动所有WebSocket服务器 + */ +errcode_t start_all_websocket_servers(void) +{ + errcode_t ret; + + // 启动ASR服务器 + ret = ws_server_start(g_asr_server); + if (ret != ERRCODE_SUCC) { + printf("Failed to start ASR server\n"); + return ret; + } + + // 启动TTS服务器 + ret = ws_server_start(g_tts_server); + if (ret != ERRCODE_SUCC) { + printf("Failed to start TTS server\n"); + ws_server_stop(g_asr_server); + return ret; + } + + // 启动Chat服务器 + ret = ws_server_start(g_chat_server); + if (ret != ERRCODE_SUCC) { + printf("Failed to start Chat server\n"); + ws_server_stop(g_asr_server); + ws_server_stop(g_tts_server); + return ret; + } + + // ai_media_init(); + old_Start_AIVoice(AIVoiceDataCallback_,nullptr); + + printf("All WebSocket servers started successfully\n"); + return ERRCODE_SUCC; +} + +/** + * @brief 停止所有WebSocket服务器 + */ +void stop_all_websocket_servers(void) +{ + if (g_asr_server) { + ws_server_stop(g_asr_server); + } + + if (g_tts_server) { + ws_server_stop(g_tts_server); + } + + if (g_chat_server) { + ws_server_stop(g_chat_server); + } + + StopAIVoice(); + + printf("All WebSocket servers stopped\n"); +} + +/** + * @brief 销毁所有WebSocket服务器 + */ +void destroy_all_websocket_servers(void) +{ + if (g_asr_server) { + ws_server_destroy(g_asr_server); + g_asr_server = NULL; + } + + if (g_tts_server) { + ws_server_destroy(g_tts_server); + g_tts_server = NULL; + } + + if (g_chat_server) { + ws_server_destroy(g_chat_server); + g_chat_server = NULL; + } + + ws_server_manager_deinit(); + printf("All WebSocket servers destroyed\n"); +} + +/** + * @brief 发送TTS文本 + */ +bool send_tts_text(const char *text) +{ + if (!g_tts_server || !text) { + return false; + } + + // 构建TTS请求JSON + char json_template[] = + "{" + "\"type\":\"text\"," + "\"task_id\":\"\"," + "\"text\":\"%s\"" + "}"; + + char *json_str = (char *)malloc(strlen(json_template) + strlen(text) + 100); + if (!json_str) { + return false; + } + + sprintf(json_str, json_template, text); + + bool result = ws_server_send_message(g_tts_server, (uint8_t *)json_str, + strlen(json_str), WS_TEXT); + + free(json_str); + return result; +} + +/** + * @brief 发送Chat消息 + */ +bool send_chat_message(const char *message) +{ + if (!g_chat_server || !message) { + return false; + } + + // 构建Chat请求JSON + char json_template[] = + "{" + "\"type\":\"jiutian-lan\"," + "\"content\":\"%s\"" + "}"; + + char *json_str = (char *)malloc(strlen(json_template) + strlen(message) + 100); + if (!json_str) { + return false; + } + + sprintf(json_str, json_template, message); + + bool result = ws_server_send_message(g_chat_server, (uint8_t *)json_str, + strlen(json_str), WS_TEXT); + + free(json_str); + return result; +} + +/** + * @brief 启动TTS服务 + */ +bool start_tts_service(void) +{ + if (!g_tts_server) { + return false; + } + + char *start_json = + "{" + "\"type\":\"start\"," + "\"task_id\":\"\"," + "\"audio_config\":{" + "\"sample_rate\":16000," + "\"format\":\"mp3\"," + "\"channel\":1" + "}," + "\"request_config\":{" + "\"protocol\":1," + "\"audio_buffer_size\":1024" + "}" + "}"; + + return ws_server_send_message(g_tts_server, (uint8_t *)start_json, + strlen(start_json), WS_TEXT); +} + +/** + * @brief 停止TTS服务 + */ +void stop_tts_service(void) +{ + if (!g_tts_server) { + return; + } + + char *stop_json = "{\"type\":\"stop\",\"task_id\":\"\"}"; + ws_server_send_message(g_tts_server, (uint8_t *)stop_json, + strlen(stop_json), WS_TEXT); +} + + +void uapi_start_asr_StartTranscription(void) +{ + if (!g_asr_server) { + return; + } + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_start_asr_StartTranscription \n"); + + + char *jsonString = "{\"header\":{\"event\":\"StartTranscription\",\"trace_id\":\ + \"\"},\"payload\":{\"format\":\"MP3\",\"sample_rate\":16000}}"; + + ws_server_send_message(g_asr_server,( uint8_t *)jsonString,strlen(jsonString),WS_TEXT); + +} + +/** + * @brief 设置服务器断连处理策略 + */ +void set_server_disconnect_strategy(ws_server_type_t type, ws_disconnect_strategy_t strategy) +{ + ws_server_handle_t *server = NULL; + + switch (type) { + case WS_SERVER_TYPE_ASR: + server = g_asr_server; + break; + case WS_SERVER_TYPE_TTS: + server = g_tts_server; + break; + case WS_SERVER_TYPE_CHAT: + server = g_chat_server; + break; + default: + return; + } + + if (server) { + ws_server_set_disconnect_strategy(server, strategy); + } +} + +/** + * @brief 手动触发服务器重连 + */ +bool trigger_server_reconnect(ws_server_type_t type) +{ + ws_server_handle_t *server = NULL; + + switch (type) { + case WS_SERVER_TYPE_ASR: + server = g_asr_server; + break; + case WS_SERVER_TYPE_TTS: + server = g_tts_server; + break; + case WS_SERVER_TYPE_CHAT: + server = g_chat_server; + break; + default: + return false; + } + + if (server) { + return ws_server_trigger_reconnect(server); + } + + return false; +} + +/** + * @brief 获取服务器状态信息 + */ +void print_server_status(void) +{ + printf("=== WebSocket Server Status ===\n"); + + if (g_asr_server) { + printf("ASR Server: %s, Running: %s\n", + ws_server_get_state(g_asr_server) == WS_SERVER_STATE_CONNECTED ? "Connected" : "Disconnected", + ws_server_is_running(g_asr_server) ? "Yes" : "No"); + } + + if (g_tts_server) { + printf("TTS Server: %s, Running: %s, Ready: %s\n", + ws_server_get_state(g_tts_server) == WS_SERVER_STATE_CONNECTED ? "Connected" : "Disconnected", + ws_server_is_running(g_tts_server) ? "Yes" : "No", + (uapi_get_tts_ready() == TTS_READY_SUCCESS_READY) ? "Yes" : "No"); + } + + if (g_chat_server) { + printf("Chat Server: %s, Running: %s\n", + ws_server_get_state(g_chat_server) == WS_SERVER_STATE_CONNECTED ? "Connected" : "Disconnected", + ws_server_is_running(g_chat_server) ? "Yes" : "No"); + } + + printf("===============================\n"); +} + +/** + * @brief 检查所有服务是否都就绪 + * @return true 所有服务都就绪,false 有服务未就绪 + */ +bool are_all_services_ready(void) +{ + bool asr_ready = false; + bool tts_ready = false; + bool chat_ready = false; + + // 检查ASR服务状态 + if (g_asr_server) { + asr_ready = (ws_server_get_state(g_asr_server) == WS_SERVER_STATE_CONNECTED) && + ws_server_is_running(g_asr_server); + } + + // 检查TTS服务状态 + if (g_tts_server) { + tts_ready = (ws_server_get_state(g_tts_server) == WS_SERVER_STATE_CONNECTED) && + ws_server_is_running(g_tts_server); + } + + // 检查Chat服务状态 + if (g_chat_server) { + chat_ready = (ws_server_get_state(g_chat_server) == WS_SERVER_STATE_CONNECTED) && + ws_server_is_running(g_chat_server); + } + + // 打印详细状态信息 + printf("Service Status Check:\n"); + printf(" ASR: %s\n", asr_ready ? "Ready" : "Not Ready"); + printf(" TTS: %s (State: %d)\n", tts_ready ? "Ready" : "Not Ready", uapi_get_tts_ready()); + printf(" Chat: %s\n", chat_ready ? "Ready" : "Not Ready"); + + return asr_ready && tts_ready && chat_ready; +} + + +/// @brief // +/// @param data +/// @param size // +static void audio_record_callback(uint8_t *data, uint32_t size) +{ + if (g_asr_server == NULL || !g_asr_server->is_initialized) { + printf("Services context not initialized (%s)",g_asr_server); + return; + } + printf("Audio record callback, size: %d", size); + + /* 🆕 根据ASR服务启动标识决定是否发送音频数据 */ + if (asr_is_ready) { + printf("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); + // g_asr_server + ws_server_send_message(g_asr_server,( uint8_t *)data,size,WS_TEXT); + // if (ret != AI_OK) { + // printf("Failed to send audio data to ASR service: %d", ret); + // } + } +} + + +void ai_media_init(void) +{ + printf("Initializing audio media service..."); + int32_t media_ret = ai_media_service_init(); + if (media_ret != 0) { + printf("Failed to initialize media service: %d", media_ret); + + return ; + } + + /* 🆕 设置录音回调 */ + ai_media_set_record_callback(audio_record_callback); + + /* 🆕 启动录音服务 */ + printf("Starting audio recording..."); + ai_media_record_start((uint8_t *)"MP3"); // 使用PCM格式 + + /* 🆕 启动播放服务 */ + // printf("Starting audio playback..."); + // ai_media_play_start((uint8_t *)"MP3"); // 使用PCM格式 + +} + + +/** + * @brief 启动TTS服务 + */ +bool uapi_start_tts_type_start(void) +{ + cJSON *json = NULL; + cJSON *header = NULL; + cJSON *payload = NULL; + char *jsonString = NULL; + bool result = false; + + if (g_tts_server->client == NULL) { + printf("TTS client is NULL\n"); + return false; + } + + // 创建根JSON对象 + json = cJSON_CreateObject(); + if (json == NULL) { + printf("Failed to create JSON object\n"); + return false; + } + + // 创建header对象 + header = cJSON_CreateObject(); + if (header == NULL) { + printf("Failed to create header object\n"); + goto cleanup; + } + + // 添加header字段 + if (cJSON_AddStringToObject(header, "event", "StartSynthesis") == NULL) { + printf("Failed to add event field\n"); + goto cleanup; + } + + if (cJSON_AddStringToObject(header, "trace_id", "") == NULL) { + printf("Failed to add trace_id field\n"); + goto cleanup; + } + + // 将header添加到根对象 + if (cJSON_AddItemToObject(json, "header", header) == 0) { + printf("Failed to add header to root object\n"); + goto cleanup; + } + header = NULL; // 防止重复释放 + + // 创建payload对象 + payload = cJSON_CreateObject(); + if (payload == NULL) { + printf("Failed to create payload object\n"); + goto cleanup; + } + + // 添加payload字段 + if (cJSON_AddStringToObject(payload, "voice", "xiaoyun") == NULL) { + printf("Failed to add voice field\n"); + goto cleanup; + } + + if (cJSON_AddStringToObject(payload, "format", "mp3") == NULL) { + printf("Failed to add format field\n"); + goto cleanup; + } + + if (cJSON_AddNumberToObject(payload, "sample_rate", 16000) == NULL) { + printf("Failed to add sample_rate field\n"); + goto cleanup; + } + + if (cJSON_AddNumberToObject(payload, "volume", 80) == NULL) { + printf("Failed to add volume field\n"); + goto cleanup; + } + + if (cJSON_AddNumberToObject(payload, "speech_rate", 0) == NULL) { + printf("Failed to add speech_rate field\n"); + goto cleanup; + } + + // 将payload添加到根对象 + if (cJSON_AddItemToObject(json, "payload", payload) == 0) { + printf("Failed to add payload to root object\n"); + goto cleanup; + } + payload = NULL; // 防止重复释放 + + // 生成JSON字符串 + jsonString = cJSON_Print(json); + printf("uapi_start_tts_type_start: %s\r\n",jsonString); + if (jsonString == NULL) { + printf("Failed to print JSON to string\n"); + goto cleanup; + } + + // 发送WebSocket消息 + if (send_websocket_message(g_tts_server->client, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0) { + printf("TTS start message sent successfully\n"); + // printf("JSON: %s\n", jsonString); + result = true; + } else { + printf("TTS start message send failed\n"); + } + +cleanup: + // 清理资源 + if (jsonString != NULL) { + free(jsonString); + } + if (header != NULL) { + cJSON_Delete(header); + } + if (payload != NULL) { + cJSON_Delete(payload); + } + if (json != NULL) { + cJSON_Delete(json); + } + + return result; +} + +void uapi_stop_tts_type_stop(void) +{ + if (g_tts_server->client) { + char *stop_json = "{\"header\":{\"event\":\"StopSynthesis\",\"trace_id\":\"\"}}"; + send_websocket_message(g_tts_server->client, (uint8_t *)stop_json, strlen(stop_json), WS_TEXT); + tts_is_ready = TTS_READY_NOT_SENT; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_stop_tts_type_stop \n"); + } +} + + +void uapi_stop_asr_StopTranscription(void) +{ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_stop_asr_StopTranscription \n"); + + char *jsonString ="{\"header\":{\"event\":\"StopTranscription\",\"trace_id\":\"3fa6b9e6a30b407ea38ad7ae69e6c291\"}}"; + + if (send_websocket_message(g_asr_server->client,( uint8_t *)jsonString,strlen(jsonString),WS_TEXT)==0) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"send_websocket_message succ 0\n"); + } + else + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"send_websocket_message erro\n"); + } +} + +uint8_t get_asr_is_ready_start_old(void) +{ + return asr_is_ready; +} + + + +#ifdef __cplusplus +} +#endif diff --git a/code/sdk/zhihui/ai_old/websocket_server_manager.cpp b/code/sdk/zhihui/ai_old/websocket_server_manager.cpp new file mode 100644 index 000000000..0ddd3f61d --- /dev/null +++ b/code/sdk/zhihui/ai_old/websocket_server_manager.cpp @@ -0,0 +1,789 @@ +/** + * @file websocket_server_manager.cpp + * @brief 统一WebSocket服务器管理实现 + * @version 1.0.0 + * @date 2024-09-05 + * + * 提供统一的WebSocket服务器管理实现,支持事件回调 + */ + +#include "websocket_server_manager.h" +#include "thread_init.h" +#include "cmsis_os2.h" +#include "soc_osal.h" +#include "wearable_log.h" +#include "custom_runtime.h" +#include "los_printf.h" +#include +#include +#include +#include +#include "chat_websocket_server.h" +#include "tts_websocket_server.h" +#include "asr_websocket_server.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* 外部函数声明 */ +extern bool read_dev_token(char *data_token); +extern bool get_dev_id(char *device_id); +extern void msleep(uint32_t ms); +extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg); +extern int receive_websocket_message_from_queue(ws_message_queue_t* queue, uint8_t* buffer, + uint16_t buffer_size, WebSocketMessageType* type, uint32_t timeout_ms); + +/* 全局变量 */ +static uint8_t g_ws_manager_initialized = 0; +static ws_server_handle_t *g_ws_servers[WS_SERVER_TYPE_MAX] = {0}; +static ws_url_callback_t g_url_callbacks[WS_SERVER_TYPE_MAX] = {0}; +static void *g_url_user_data[WS_SERVER_TYPE_MAX] = {0}; +static ws_server_handle_t *g_thread_handles[WS_SERVER_TYPE_MAX] = {0}; // 每个服务类型的线程handle + +/* 内部函数声明 */ +static void ws_server_thread_func(void *data); +static void ws_server_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type, ws_server_handle_t *handle); +static void ws_server_trigger_event(ws_server_handle_t *handle, ws_event_type_t event_type, + const uint8_t *data, uint32_t data_len, WebSocketMessageType msg_type, uint32_t error_code); +static bool ws_server_connect(ws_server_handle_t *handle); +static void ws_server_disconnect(ws_server_handle_t *handle); +static void ws_server_cleanup(ws_server_handle_t *handle); +static bool ws_server_should_reconnect(ws_server_handle_t *handle); +static bool ws_server_attempt_reconnect(ws_server_handle_t *handle); + +/** + * @brief 初始化WebSocket服务器管理器 + */ +errcode_t ws_server_manager_init(void) +{ + if (g_ws_manager_initialized) { + return ERRCODE_SUCC; + } + + memset(g_ws_servers, 0, sizeof(g_ws_servers)); + g_ws_manager_initialized = 1; + + printf("WebSocket Server Manager initialized\n"); + return ERRCODE_SUCC; +} + +/** + * @brief 反初始化WebSocket服务器管理器 + */ +void ws_server_manager_deinit(void) +{ + if (!g_ws_manager_initialized) { + return; + } + + // 停止所有服务器 + for (int i = 0; i < WS_SERVER_TYPE_MAX; i++) { + if (g_ws_servers[i]) { + ws_server_destroy(g_ws_servers[i]); + g_ws_servers[i] = NULL; + } + } + + g_ws_manager_initialized = 0; + printf("WebSocket Server Manager deinitialized\n"); +} + +/** + * @brief 创建WebSocket服务器 + */ +ws_server_handle_t* ws_server_create(const ws_server_config_t *config, + ws_event_callback_t callback, + void *user_data) +{ + if (!config || !callback) { + printf("Invalid parameters for ws_server_create\n"); + return NULL; + } + + if (config->type >= WS_SERVER_TYPE_MAX) { + printf("Invalid server type: %d\n", config->type); + return NULL; + } + + // 检查是否已存在 + if (g_ws_servers[config->type]) { + printf("Server type %d already exists\n", config->type); + return NULL; + } + + // 分配内存 + ws_server_handle_t *handle = (ws_server_handle_t *)malloc(sizeof(ws_server_handle_t)); + if (!handle) { + printf("Failed to allocate memory for server handle\n"); + return NULL; + } + + memset(handle, 0, sizeof(ws_server_handle_t)); + + // 初始化配置 + memcpy(&handle->config, config, sizeof(ws_server_config_t)); + handle->callback = callback; + handle->user_data = user_data; + handle->type = config->type; + handle->state = WS_SERVER_STATE_INIT; + + // 设置URL回调函数 + handle->url_callback = config->url_callback; + handle->url_user_data = config->url_user_data; + + // 初始化重连相关变量 + handle->reconnect_count = 0; + handle->last_reconnect_time = 0; + + // 初始化消息队列 + ws_queue_init(&handle->msg_queue, 0); + + // 分配接收缓冲区 + handle->recv_buffer = (char *)malloc(config->buffer_size); + if (!handle->recv_buffer) { + printf("Failed to allocate receive buffer\n"); + free(handle); + return NULL; + } + + g_ws_servers[config->type] = handle; + + printf("WebSocket server created: type=%d, name=%s\n", config->type, config->name); + return handle; +} + +/** + * @brief 销毁WebSocket服务器 + */ +void ws_server_destroy(ws_server_handle_t *handle) +{ + if (!handle) { + return; + } + + // 停止服务器 + ws_server_stop(handle); + + // 清理资源 + ws_server_cleanup(handle); + + // 从全局数组中移除 + if (g_ws_servers[handle->type] == handle) { + g_ws_servers[handle->type] = NULL; + } + + free(handle); + printf("WebSocket server destroyed: type=%d\n", handle->type); +} + +/** + * @brief 启动WebSocket服务器 + */ +errcode_t ws_server_start(ws_server_handle_t *handle) +{ + if (!handle) { + return ERRCODE_FAIL; + } + + if (handle->is_running) { + printf("Server is already running\n"); + return ERRCODE_FAIL; + } + + // 设置对应服务类型的全局变量,供线程函数使用 + g_thread_handles[handle->type] = handle; + + // 创建线程配置 + app_task_definition_t task_cfg; + memset(&task_cfg, 0, sizeof(task_cfg)); + + task_cfg.create_queue_handle = NULL; + task_cfg.attr.name = handle->config.name; + task_cfg.attr.priority = (osPriority_t)16; + task_cfg.attr.stack_size =0x2000;// handle->config.stack_size; + task_cfg.func = ws_server_thread_func; + task_cfg.use_mem = USE_EXTEND_MEM; + + // 创建线程 + errcode_t ret = uapi_create_osThread_custom(&task_cfg); + if (ret != ERRCODE_SUCC) { + printf("Failed to create server thread: %d\n", ret); + g_thread_handles[handle->type] = NULL; // 清理对应服务类型的全局变量 + return ret; + } + + handle->need_stop = 0; + + printf("WebSocket server started: type=%d %s\n", handle->type,handle->config.name); + return ERRCODE_SUCC; +} + +/** + * @brief 停止WebSocket服务器 + */ +void ws_server_stop(ws_server_handle_t *handle) +{ + if (!handle || !handle->is_running) { + return; + } + + handle->need_stop = 1; + handle->is_running = 0; + + // 发送关闭消息 + if (handle->client) { + send_websocket_message(handle->client, (uint8_t *)"WS_CLOSE", strlen("WS_CLOSE"), WS_CLOSE); + } + + printf("WebSocket server stopped: type=%d\n", handle->type); +} + +/** + * @brief 发送WebSocket消息 + */ +bool ws_server_send_message(ws_server_handle_t *handle, + uint8_t *data, + uint32_t len, + WebSocketMessageType msg_type) +{ + if (!handle || !handle->client || !data || len == 0) { + return false; + } + + if (send_websocket_message(handle->client, data, len, msg_type) == 0) { + return true; + } + + return false; +} + +/** + * @brief 获取服务器状态 + */ +ws_server_state_t ws_server_get_state(const ws_server_handle_t *handle) +{ + if (!handle) { + return WS_SERVER_STATE_ERROR; + } + + return handle->state; +} + +/** + * @brief 检查服务器是否运行 + */ +bool ws_server_is_running(const ws_server_handle_t *handle) +{ + if (!handle) { + return false; + } + + return handle->is_running; +} + +/** + * @brief 设置服务器停止标志 + */ +void ws_server_set_stop_flag(ws_server_handle_t *handle, bool stop) +{ + if (!handle) { + return; + } + + handle->need_stop = stop; +} + +/** + * @brief 设置断连处理策略 + */ +void ws_server_set_disconnect_strategy(ws_server_handle_t *handle, ws_disconnect_strategy_t strategy) +{ + if (!handle) { + return; + } + + handle->config.disconnect_strategy = strategy; + printf("Disconnect strategy set to %d for server type %d\n", strategy, handle->type); +} + +/** + * @brief 手动触发重连 + */ +bool ws_server_trigger_reconnect(ws_server_handle_t *handle) +{ + if (!handle || !handle->is_running) { + return false; + } + + printf("Manual reconnect triggered for server type %d\n", handle->type); + return ws_server_attempt_reconnect(handle); +} + +/** + * @brief 获取默认配置 + */ +ws_server_config_t ws_server_get_default_config(ws_server_type_t type) +{ + ws_server_config_t config = {}; + + config.type = type; + config.handshake_timeout_ms = 10000; + config.receive_timeout_ms = 30000; + config.max_retry_count = 3; + config.buffer_size = 4096; + config.url_callback = NULL; + config.url_user_data = NULL; + config.disconnect_strategy = WS_DISCONNECT_RECONNECT; + config.reconnect_interval_ms = 5000; + + switch (type) { + case WS_SERVER_TYPE_ASR: + strcpy(config.name, "asr_server"); + config.stack_size = 0x1000; + break; + case WS_SERVER_TYPE_TTS: + strcpy(config.name, "tts_server"); + config.stack_size = 0x2000; + break; + case WS_SERVER_TYPE_CHAT: + strcpy(config.name, "chat_server"); + config.stack_size = 0x1000; + break; + default: + break; + } + + return config; +} + +/** + * @brief 注册URL生成回调函数 + */ +bool ws_server_register_url_callback(ws_server_type_t type, + ws_url_callback_t url_callback, + void *user_data) +{ + if (type >= WS_SERVER_TYPE_MAX) { + printf("Invalid server type: %d\n", type); + return false; + } + + g_url_callbacks[type] = url_callback; + g_url_user_data[type] = user_data; + + printf("URL callback registered for server type: %d\n", type); + return true; +} + +/** + * @brief 创建URL(使用注册的回调函数) + */ +bool ws_server_create_url(ws_server_handle_t *handle, + char *url_buf, + uint32_t url_size) +{ + if (!handle || !url_buf || url_size == 0) { + return false; + } + + // 优先使用句柄中的URL回调函数 + if (handle->url_callback) { + return handle->url_callback(handle->type, url_buf, url_size, handle->url_user_data); + } + + // 使用全局注册的URL回调函数 + if (g_url_callbacks[handle->type]) { + return g_url_callbacks[handle->type](handle->type, url_buf, url_size, g_url_user_data[handle->type]); + } + + // 如果没有注册回调函数,使用默认URL + switch (handle->type) { + case WS_SERVER_TYPE_ASR: + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/asr/ws"); + break; + case WS_SERVER_TYPE_TTS: + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/tts/stream?audio_buffer_size=512"); + break; + case WS_SERVER_TYPE_CHAT: + snprintf(url_buf, url_size, "ws://118.145.158.207:22080/chat/ws"); + break; + default: + return false; + } + + return true; +} + +/** + * @brief WebSocket服务器线程函数 + */ +static void ws_server_thread_func(void *data) +{ + // 由于uapi_create_osThread_custom不支持参数传递,需要找到当前线程对应的handle + // 遍历所有服务类型,找到正在运行且需要启动的服务器 + ws_server_handle_t *handle = NULL; + for (int i = 0; i < WS_SERVER_TYPE_MAX; i++) { + if (g_thread_handles[i] && !g_thread_handles[i]->is_running && !g_thread_handles[i]->need_stop) { + handle = g_thread_handles[i]; + break; + } + } + + if (!handle) { + printf("WebSocket server thread: no running server found\n"); + return; + } + handle->is_running = 1; + + uint32_t last_activity; + + printf("WebSocket server thread started: type=%d\n", handle->type); + + // 连接服务器 + if (!ws_server_connect(handle)) { + printf("Failed to connect server: type=%d\n", handle->type); + ws_server_trigger_event(handle, WS_EVENT_ERROR, NULL, 0, WS_TEXT, 1); + goto cleanup; + } + + // 触发连接完成事件 + ws_server_trigger_event(handle, WS_EVENT_CONNECTED, NULL, 0, WS_TEXT, 0); + + // 主循环 + last_activity = (uint32_t)osKernelGetTickCount(); + + handle->is_initialized = 1; + + while (!handle->need_stop) { + // 检查超时 + if ((uint32_t)osKernelGetTickCount() - last_activity > handle->config.receive_timeout_ms) { + printf("Receive timeout, checking reconnect strategy...\n"); + ws_server_trigger_event(handle, WS_EVENT_RECONNECTING, NULL, 0, WS_TEXT, 0); + + if (ws_server_should_reconnect(handle)) { + if (ws_server_attempt_reconnect(handle)) { + last_activity = (uint32_t)osKernelGetTickCount(); + continue; + } + } + break; + } + + // 接收消息 + memset(handle->recv_buffer, 0, handle->config.buffer_size); + WebSocketMessageType msg_type; + // int len = receive_websocket_message_from_queue(&handle->msg_queue, + // (unsigned char *)handle->recv_buffer, + // handle->config.buffer_size, + // &msg_type, + // handle->config.receive_timeout_ms); + int len = receive_websocket_message(handle->client,(unsigned char *)handle->recv_buffer,handle->config.buffer_size, &msg_type, handle->config.receive_timeout_ms); + + if (len == -1 || msg_type == WS_CLOSE) { + printf("WebSocket connection closed, checking reconnect strategy...\n"); + ws_server_trigger_event(handle, WS_EVENT_DISCONNECTED, NULL, 0, WS_TEXT, 0); + + if (ws_server_should_reconnect(handle)) { + if (ws_server_attempt_reconnect(handle)) { + last_activity = (uint32_t)osKernelGetTickCount(); + continue; + } + } + break; + } + + if(len==0) + { + printf("handle->config.receive_timeout_ms \n"); + } + + if (len > 0) { + last_activity = (uint32_t)osKernelGetTickCount(); + + // 确保字符串以null结尾 + if (len < handle->config.buffer_size) { + handle->recv_buffer[len] = '\0'; + } + + // 触发数据接收事件 + ws_server_trigger_event(handle, WS_EVENT_DATA_RECEIVED, + (uint8_t *)handle->recv_buffer, len, msg_type, 0); + } + + msleep(10); + } + +cleanup: + msleep(100); + ws_server_disconnect(handle); + ws_server_cleanup(handle); + + // 清理对应服务类型的全局变量 + if (handle) { + g_thread_handles[handle->type] = NULL; + printf("WebSocket server thread ended: type=%d\n", handle->type); + } +} + +/** + * @brief WebSocket数据包回调函数 + */ +static void ws_server_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type, ws_server_handle_t *handle) +{ + if (handle && handle->client && handle->client->msg_queue) { + ws_queue_put(handle->client->msg_queue, data, datalen, type); + } +} + +/** + * @brief 触发事件 + */ +static void ws_server_trigger_event(ws_server_handle_t *handle, ws_event_type_t event_type, + const uint8_t *data, uint32_t data_len, WebSocketMessageType msg_type, uint32_t error_code) +{ + if (!handle || !handle->callback) { + return; + } + + ws_event_data_t event_data = {}; + event_data.event_type = event_type; + event_data.server_type = handle->type; + event_data.data = (uint8_t *)data; + event_data.data_len = data_len; + event_data.msg_type = msg_type; + event_data.error_code = error_code; + + handle->callback(&event_data); +} + +/** + * @brief 连接服务器 + */ +static bool ws_server_connect(ws_server_handle_t *handle) +{ + if (!handle) { + return false; + } + + handle->state = WS_SERVER_STATE_CONNECTING; + + // 创建URL + char url_buf[256] = {0}; + if (!ws_server_create_url(handle, url_buf, sizeof(url_buf))) { + printf("Failed to create URL for server type %d\n", handle->type); + return false; + } + + // 根据测试服务器索引选择URL + extern uint8_t g_test_server_index; + if (g_test_server_index == 1) { + switch (handle->type) { + case WS_SERVER_TYPE_ASR: + strcpy(url_buf, "ws://118.145.158.207:22080/asr/ws"); + break; + case WS_SERVER_TYPE_TTS: + strcpy(url_buf, "ws://118.145.158.207:22080/tts/stream"); + break; + case WS_SERVER_TYPE_CHAT: + strcpy(url_buf, "ws://118.145.158.207:22080/chat/ws"); + break; + default: + break; + } + } else if (g_test_server_index == 2) { + switch (handle->type) { + case WS_SERVER_TYPE_ASR: + strcpy(url_buf, "ws://110.42.224.129:8910/asr/ws"); + break; + case WS_SERVER_TYPE_TTS: + strcpy(url_buf, "ws://110.42.224.129:8910/tts/stream"); + break; + case WS_SERVER_TYPE_CHAT: + strcpy(url_buf, "ws://110.42.224.129:8910/chat/ws"); + break; + default: + break; + } + } + + printf("Connecting to: %s\n", url_buf); + + // 创建WebSocket客户端 + uint8_t client_count = handle->config.max_retry_count; + while (client_count > 0) { + handle->client = init_websocket_client(url_buf, NULL, 0); + if (handle->client) { + handle->client->msg_queue = &handle->msg_queue; + ws_set_packet_callback(handle->client, (void (*)(uint8_t*, size_t, WebSocketMessageType))ws_server_packet_callback); + break; + } + + printf("WebSocket client creation failed, retrying... (%d/%d)\n", + handle->config.max_retry_count - client_count + 1, handle->config.max_retry_count); + client_count--; + msleep(2000); + } + + if (!handle->client) { + printf("Failed to create WebSocket client after %d retries\n", handle->config.max_retry_count); + handle->state = WS_SERVER_STATE_ERROR; + return false; + } + + printf("WebSocket client created successfully\n"); + + // 等待握手完成 + uint32_t handshake_start_time = (uint32_t)osKernelGetTickCount(); + while (handle->client->handshake == 0) { + if ((uint32_t)osKernelGetTickCount() - handshake_start_time > handle->config.handshake_timeout_ms) { + printf("WebSocket handshake timeout (%d ms)\n", handle->config.handshake_timeout_ms); + close_websocket_client(handle->client); + handle->client = NULL; + handle->state = WS_SERVER_STATE_ERROR; + return false; + } + + msleep(1000); + printf("Waiting for WebSocket handshake... (%d ms)\n", + (uint32_t)osKernelGetTickCount() - handshake_start_time); + } + + printf("WebSocket handshake completed in %d ms\n", + (uint32_t)osKernelGetTickCount() - handshake_start_time); + + handle->state = WS_SERVER_STATE_CONNECTED; + return true; +} + +/** + * @brief 断开服务器连接 + */ +static void ws_server_disconnect(ws_server_handle_t *handle) +{ + if (!handle) { + return; + } + + if (handle->client) { + close_websocket_client(handle->client); + handle->client = NULL; + } + + handle->state = WS_SERVER_STATE_DISCONNECTED; +} + +/** + * @brief 清理服务器资源 + */ +static void ws_server_cleanup(ws_server_handle_t *handle) +{ + if (!handle) { + return; + } + + ws_server_disconnect(handle); + + if (handle->recv_buffer) { + free(handle->recv_buffer); + handle->recv_buffer = NULL; + } + + ws_queue_deinit(&handle->msg_queue); + handle->is_running = 0; + handle->is_initialized = 0; +} + +/** + * @brief 判断是否应该重连 + */ +static bool ws_server_should_reconnect(ws_server_handle_t *handle) +{ + if (!handle) { + return false; + } + + // 如果设置了停止标志,不重连 + if (handle->need_stop) { + printf("Server stop flag set, no reconnect\n"); + return false; + } + uint32_t current_time; + // 根据断连处理策略判断 + switch (handle->config.disconnect_strategy) { + case WS_DISCONNECT_STOP: + printf("Disconnect strategy is STOP, no reconnect\n"); + return false; + + case WS_DISCONNECT_RECONNECT: + // 检查重连次数限制 + if (handle->reconnect_count >= handle->config.max_retry_count) { + printf("Max reconnect count (%d) reached, no more reconnects\n", + handle->config.max_retry_count); + return false; + } + + // 检查重连间隔时间 + current_time = (uint32_t)osKernelGetTickCount(); + if (handle->last_reconnect_time > 0 && + current_time - handle->last_reconnect_time < handle->config.reconnect_interval_ms) { + printf("Reconnect interval not reached, waiting...\n"); + return false; + } + + printf("Should reconnect: count=%d/%d\n", + handle->reconnect_count, handle->config.max_retry_count); + return true; + + case WS_DISCONNECT_CUSTOM: + // 自定义处理,由事件回调函数决定 + printf("Custom disconnect strategy, let callback decide\n"); + return true; + + default: + return false; + } +} + +/** + * @brief 尝试重连 + */ +static bool ws_server_attempt_reconnect(ws_server_handle_t *handle) +{ + if (!handle) { + return false; + } + + printf("Attempting to reconnect server type %d (attempt %d/%d)\n", + handle->type, handle->reconnect_count + 1, handle->config.max_retry_count); + + // 断开当前连接 + ws_server_disconnect(handle); + + // 等待重连间隔 + if (handle->config.reconnect_interval_ms > 0) { + msleep(handle->config.reconnect_interval_ms); + } + + // 尝试重新连接 + bool success = ws_server_connect(handle); + + if (success) { + handle->reconnect_count++; + handle->last_reconnect_time = (uint32_t)osKernelGetTickCount(); + printf("Reconnect successful for server type %d\n", handle->type); + + // 触发重连成功事件 + ws_server_trigger_event(handle, WS_EVENT_CONNECTED, NULL, 0, WS_TEXT, 0); + } else { + printf("Reconnect failed for server type %d\n", handle->type); + + // 触发重连失败事件 + ws_server_trigger_event(handle, WS_EVENT_ERROR, NULL, 0, WS_TEXT, 1); + } + + return success; +} + +#ifdef __cplusplus +} +#endif diff --git a/code/sdk/zhihui/ai_old/websocket_server_manager.h b/code/sdk/zhihui/ai_old/websocket_server_manager.h new file mode 100644 index 000000000..6814096e9 --- /dev/null +++ b/code/sdk/zhihui/ai_old/websocket_server_manager.h @@ -0,0 +1,255 @@ +/** + * @file websocket_server_manager.h + * @brief 统一WebSocket服务器管理接口 + * @version 1.0.0 + * @date 2024-09-05 + * + * 提供统一的WebSocket服务器管理接口,支持事件回调 + */ + +#ifndef WEBSOCKET_SERVER_MANAGER_H +#define WEBSOCKET_SERVER_MANAGER_H + +#include "zhihui_ws.h" +#include "bk_websocket.h" +#include "ws_queue_common.h" +#include "common_def.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + + +/* WebSocket服务器类型定义 */ +typedef enum { + WS_SERVER_TYPE_ASR = 0, // ASR服务器 + WS_SERVER_TYPE_TTS = 1, // TTS服务器 + WS_SERVER_TYPE_CHAT = 2, // Chat服务器 + WS_SERVER_TYPE_MAX +} ws_server_type_t; + + +/* URL生成回调函数类型 */ +typedef bool (*ws_url_callback_t)(ws_server_type_t type, char *url_buf, uint32_t url_size, void *user_data); + +/* WebSocket服务器状态 */ +typedef enum { + WS_SERVER_STATE_INIT = 0, // 初始化 + WS_SERVER_STATE_CONNECTING, // 连接中 + WS_SERVER_STATE_CONNECTED, // 已连接 + WS_SERVER_STATE_DISCONNECTED, // 已断开 + WS_SERVER_STATE_ERROR, // 错误状态 + WS_SERVER_STATE_MAX +} ws_server_state_t; + +/* WebSocket事件类型 */ +typedef enum { + WS_EVENT_CONNECTED = 0, // 连接完成 + WS_EVENT_DATA_RECEIVED, // 数据接收 + WS_EVENT_DISCONNECTED, // 连接断开 + WS_EVENT_RECONNECTING, // 重连中 + WS_EVENT_ERROR, // 错误事件 + WS_EVENT_MAX +} ws_event_type_t; + +/* 断连处理策略 */ +typedef enum { + WS_DISCONNECT_STOP = 0, // 停止服务 + WS_DISCONNECT_RECONNECT, // 重连服务 + WS_DISCONNECT_CUSTOM // 自定义处理 +} ws_disconnect_strategy_t; + +/* WebSocket服务器配置 */ +typedef struct { + ws_server_type_t type; // 服务器类型 + char name[32]; // 服务器名称 + ws_url_callback_t url_callback; // URL生成回调函数 + void *url_user_data; // URL回调用户数据 + uint32_t stack_size; // 线程栈大小 + uint32_t handshake_timeout_ms; // 握手超时时间(ms) + uint32_t receive_timeout_ms; // 接收超时时间(ms) + uint32_t max_retry_count; // 最大重试次数 + uint32_t buffer_size; // 接收缓冲区大小 + ws_disconnect_strategy_t disconnect_strategy; // 断连处理策略 + uint32_t reconnect_interval_ms; // 重连间隔时间(ms) +} ws_server_config_t; + +/* WebSocket事件数据 */ +typedef struct { + ws_event_type_t event_type; // 事件类型 + ws_server_type_t server_type; // 服务器类型 + uint8_t *data; // 数据指针 + uint32_t data_len; // 数据长度 + WebSocketMessageType msg_type; // 消息类型 + uint32_t error_code; // 错误码 +} ws_event_data_t; + +/* 事件回调函数类型 */ +typedef void (*ws_event_callback_t)(const ws_event_data_t *event_data); + + +/* WebSocket服务器句柄 */ +typedef struct { + ws_server_type_t type; // 服务器类型 + ws_server_state_t state; // 当前状态 + WebSocketClient *client; // WebSocket客户端 + ws_message_queue_t msg_queue; // 消息队列 + ws_server_config_t config; // 服务器配置 + ws_event_callback_t callback; // 事件回调函数 + void *user_data; // 用户数据 + ws_url_callback_t url_callback; // URL生成回调函数 + void *url_user_data; // URL回调用户数据 + uint8_t is_running; // 运行标志 + uint8_t need_stop; // 停止标志 + uint8_t is_initialized; // 初始化标志 + char *recv_buffer; // 接收缓冲区 + uint32_t reconnect_count; // 重连次数 + uint32_t last_reconnect_time; // 上次重连时间 +} ws_server_handle_t; + +/* 函数声明 */ + +/** + * @brief 初始化WebSocket服务器管理器 + * @return errcode_t 错误码 + */ +errcode_t ws_server_manager_init(void); + +/** + * @brief 反初始化WebSocket服务器管理器 + */ +void ws_server_manager_deinit(void); + +/** + * @brief 创建WebSocket服务器 + * @param config 服务器配置 + * @param callback 事件回调函数 + * @param user_data 用户数据 + * @return ws_server_handle_t* 服务器句柄,失败返回NULL + */ +ws_server_handle_t* ws_server_create(const ws_server_config_t *config, + ws_event_callback_t callback, + void *user_data); + +/** + * @brief 销毁WebSocket服务器 + * @param handle 服务器句柄 + */ +void ws_server_destroy(ws_server_handle_t *handle); + +/** + * @brief 启动WebSocket服务器 + * @param handle 服务器句柄 + * @return errcode_t 错误码 + */ +errcode_t ws_server_start(ws_server_handle_t *handle); + +/** + * @brief 停止WebSocket服务器 + * @param handle 服务器句柄 + */ +void ws_server_stop(ws_server_handle_t *handle); + +/** + * @brief 发送WebSocket消息 + * @param handle 服务器句柄 + * @param data 数据指针 + * @param len 数据长度 + * @param msg_type 消息类型 + * @return bool 成功返回true,失败返回false + */ +bool ws_server_send_message(ws_server_handle_t *handle, + uint8_t *data, + uint32_t len, + WebSocketMessageType msg_type); + +/** + * @brief 获取服务器状态 + * @param handle 服务器句柄 + * @return ws_server_state_t 服务器状态 + */ +ws_server_state_t ws_server_get_state(const ws_server_handle_t *handle); + +/** + * @brief 检查服务器是否运行 + * @param handle 服务器句柄 + * @return bool 运行返回true,否则返回false + */ +bool ws_server_is_running(const ws_server_handle_t *handle); + +/** + * @brief 设置服务器停止标志 + * @param handle 服务器句柄 + * @param stop 停止标志 + */ +void ws_server_set_stop_flag(ws_server_handle_t *handle, bool stop); + +/** + * @brief 设置断连处理策略 + * @param handle 服务器句柄 + * @param strategy 断连处理策略 + */ +void ws_server_set_disconnect_strategy(ws_server_handle_t *handle, ws_disconnect_strategy_t strategy); + +/** + * @brief 手动触发重连 + * @param handle 服务器句柄 + * @return bool 成功返回true,失败返回false + */ +bool ws_server_trigger_reconnect(ws_server_handle_t *handle); + +/** + * @brief 获取默认配置 + * @param type 服务器类型 + * @return ws_server_config_t 默认配置 + */ +ws_server_config_t ws_server_get_default_config(ws_server_type_t type); + +/** + * @brief 注册URL生成回调函数 + * @param type 服务器类型 + * @param url_callback URL生成回调函数 + * @param user_data 用户数据 + * @return bool 成功返回true,失败返回false + */ +bool ws_server_register_url_callback(ws_server_type_t type, + ws_url_callback_t url_callback, + void *user_data); + +/** + * @brief 创建URL(使用注册的回调函数) + * @param handle 服务器句柄 + * @param url_buf URL缓冲区 + * @param url_size URL缓冲区大小 + * @return bool 成功返回true,失败返回false + */ +bool ws_server_create_url(ws_server_handle_t *handle, + char *url_buf, + uint32_t url_size); + +errcode_t start_all_websocket_servers(void); + +errcode_t init_all_websocket_servers(void); + +void uapi_start_asr_StartTranscription(void); + + +void uapi_stop_asr_StopTranscription(void); + +void stop_all_websocket_servers(void); + +bool are_all_services_ready(void); + +uint8_t get_asr_is_ready_start_old(void); + +void destroy_all_websocket_servers(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WEBSOCKET_SERVER_MANAGER_H */ diff --git a/code/sdk/zhihui/ai_old/zhihui_ws.cpp b/code/sdk/zhihui/ai_old/zhihui_ws.cpp index a8adc5595..0f6901029 100644 --- a/code/sdk/zhihui/ai_old/zhihui_ws.cpp +++ b/code/sdk/zhihui/ai_old/zhihui_ws.cpp @@ -65,7 +65,7 @@ // 包含新的WebSocket解析器 #include "websocket_parser_wrapper.h" -#define TEST_AI_WS 0 +#define TEST_AI_WS 1 #ifdef __cplusplus extern "C" { @@ -674,7 +674,7 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint ssize_t bytes_received; struct in_addr in_addr_t={0}; ip_addr_t ip; - struct cat1_ws_init_t ip_sem={0}; + // struct cat1_ws_init_t ip_sem={0}; int opt = 1; int sockfd = -1; @@ -748,7 +748,7 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint if (lwip_connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { printf("init_websocket_client connection failed\r\n"); msleep(100); - close(sockfd); + lwip_close(sockfd); goto client_ERROR; @@ -765,7 +765,7 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint if (send_handshake(client,headers) < 0) { //发送头 printf("socket send_handshake client_ERROR"); - close(sockfd); + lwip_close(sockfd); goto client_ERROR; } @@ -776,7 +776,7 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint printf("aic_zalloc(BUFFER_SIZE);null "); goto client_ERROR; } - printf("recv: ??? "); + printf("recv:%s\r\n",parsed_url.path); bytes_received = lwip_recv(sockfd, buffer, BUFFER_SIZE - 1, 0); @@ -799,21 +799,13 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint printf("init_websocket_client end 2"); - client->tcp_handle = tcp_add_existing_connection(sockfd, parsed_url.host, parsed_url.port, - _tcp_on_connect_cb, _tcp_on_disconnect_cb, _tcp_on_data_received_cb); - - client->parser = ws_parser_init(); + free(buffer); free_url(parsed_url); client->handshake=1; - if (!client->parser) { - WS_LOG_ERROR("init_websocket_client Error: Failed to initialize WebSocket parser"); - close(sockfd); - goto client_ERROR; - } return client; client_ERROR: @@ -876,6 +868,7 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint } // 使用标准 socket 替代 TCP 管理器 + printf("lwip_socket init_websocket_client\r\n"); client->sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0); if (client->sockfd < 0) { WS_LOG_ERROR("init_websocket_client Error: Socket creation failed"); @@ -883,11 +876,11 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint } // 设置 socket 选项 - lwip_setsockopt(client->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val)); + // lwip_setsockopt(client->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val)); - // 设置非阻塞模式 - flags = fcntl(client->sockfd, F_GETFL, 0); - fcntl(client->sockfd, F_SETFL, flags | O_NONBLOCK); + // // 设置非阻塞模式 + // flags = fcntl(client->sockfd, F_GETFL, 0); + // fcntl(client->sockfd, F_SETFL, flags | O_NONBLOCK); // 解析主机地址 memset(&server_addr, 0, sizeof(server_addr)); @@ -905,12 +898,13 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint } // 连接到服务器 + connect_result = lwip_connect(client->sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (connect_result < 0 && errno != EINPROGRESS) { WS_LOG_ERROR("init_websocket_client Error: Socket connection failed for %s:%d", parsed_url.host, parsed_url.port); goto client_ERROR; } - + printf("lwip_connect init_websocket_client\r\n"); // 等待连接完成 if (connect_result == 0 || errno == EINPROGRESS) { FD_ZERO(&write_fds); @@ -940,11 +934,16 @@ WebSocketClient *init_websocket_client(const char *url, const char *headers,uint WS_LOG_ERROR("Socket connection failed with error: %d", error); goto client_ERROR; } - } else { + } + else { WS_LOG_ERROR("Socket connection timeout or failed"); goto client_ERROR; } } + else + { + printf("connect_result fail init_websocket_client\r\n"); + } // 设置回阻塞模式用于后续通信 flags = fcntl(client->sockfd, F_GETFL, 0); @@ -1006,15 +1005,15 @@ void close_websocket_client(WebSocketClient *client) { // 先关闭socket连接,避免数据继续流入 if (client->sockfd >= 0) { - close(client->sockfd); + lwip_close(client->sockfd); client->sockfd = -1; } // 释放WebSocket解析器 - if (client->parser) { - ws_parser_destroy(client->parser); - client->parser = NULL; - } + // if (client->parser) { + // ws_parser_destroy(client->parser); + // client->parser = NULL; + // } // 清理客户端状态 client->handshake = 0; @@ -1336,6 +1335,327 @@ static int start_websocket_receive_thread(WebSocketClient* client) { return 0; } + +// 带超时的lwip_recv包装函数 +static int lwip_recv_with_timeout(int sockfd, void *buf, size_t len, int flags, uint32_t timeout_ms) { + uint32_t start_time = (uint32_t)osKernelGetTickCount(); + int bytes_received = 0; + + while (bytes_received < (int)len) { + // 检查超时 + uint32_t current_time = (uint32_t)osKernelGetTickCount(); + if ((current_time - start_time) >= timeout_ms) { + printf("lwip_recv_with_timeout: timeout after %u ms\n", timeout_ms); + return -1; + } + + // 尝试接收数据 + int ret = lwip_recv(sockfd, (char*)buf + bytes_received, len - bytes_received, flags); + + if (ret > 0) { + bytes_received += ret; + } else if (ret == 0) { + printf("lwip_recv_with_timeout: connection closed by peer\n"); + return 0; + } else { + // ret < 0, 检查是否是EAGAIN或EWOULDBLOCK + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // 没有数据可读,短暂等待后重试 + printf("errno == EAGAIN || errno == EWOULDBLOCK"); + msleep(10); // 等待10ms + continue; + } else { + printf("lwip_recv_with_timeout: error %d\n", errno); + return -1; + } + } + } + + return bytes_received; +} + +int receive_websocket_message(WebSocketClient *client, unsigned char *buf,int len,WebSocketMessageType *code, uint32_t timeout_ms) { + uint32_t start_time = (uint32_t)osKernelGetTickCount(); + uint32_t timer_ms = start_time; + //printf("receive_websocket_message 1 timer_ms=%d\n",timer_ms); + unsigned char frame[20]={0}; + unsigned char masking_key[4]; + int bytes_received = 0; + int recv_len = 0 ; + static uint8_t last_byte = 0; + + // 超时检查函数 + auto check_timeout = [&]() -> bool { + uint32_t current_time = (uint32_t)osKernelGetTickCount(); + return (current_time - start_time) >= timeout_ms; + }; + // while(1) + // { + // recv_len = lwip_send(client->sockfd, frame+bytes_received, 2-bytes_received, 0); + // bytes_received +=recv_len; + // if(bytes_received>=2) + // break; + // } + int max_count=len; + + while(max_count) + { + // 检查超时 + if (check_timeout()) { + printf("receive_websocket_message: timeout while waiting for frame header\n"); + return -1; + } + + // 使用带超时的lwip_recv + uint32_t remaining_time = timeout_ms - ((uint32_t)osKernelGetTickCount() - start_time); + if (remaining_time <= 0) { + printf("receive_websocket_message: timeout while waiting for frame header\n"); + return -1; + } + + bytes_received = lwip_recv_with_timeout(client->sockfd, frame, 1, 0, remaining_time); + + if (bytes_received <= 0) + { + if (bytes_received == 0) { + printf("Connection lwip_closed by server.\n"); + } else { + printf("receive message failed"); + } + return -1; + } + max_count--; + + if(frame[0]!= 0x80 && frame[0]!= 0x81 && frame[0]!= 0x82 && frame[0]!= 0x88 && frame[0]!= 0x89 && frame[0]!= 0x8A) + { + printf("receive_websocket_message:hend =0x%X,0x%X ,last_byte =0x%x ERROR!!!!!!!!!",frame[0],frame[1],last_byte); + + if(last_byte!= 0x80 && last_byte!= 0x81 && last_byte!= 0x82 && last_byte!= 0x88 && last_byte!= 0x89 && last_byte!= 0x8A) + { + printf("receive_websocket_message:hend =0x%X,0x%X ,last_byte =0x%x ERROR222222222!!!!!!!!!",frame[0],frame[1],last_byte); + continue; + } + if(frame[0]==0 && frame[1]==0 && last_byte==0) + { + break; + } + + } + else + { + break; + } + } + if(max_count==0) + { + return -1; + } + // 检查超时 + if (check_timeout()) { + printf("receive_websocket_message: timeout while waiting for frame length\n"); + return -1; + } + + timer_ms = (uint32_t)osKernelGetTickCount(); + //printf("receive_websocket_message 2 timer_ms=%d\n",timer_ms); + + // 使用带超时的lwip_recv + uint32_t remaining_time = timeout_ms - ((uint32_t)osKernelGetTickCount() - start_time); + if (remaining_time <= 0) { + printf("receive_websocket_message: timeout while waiting for frame length\n"); + return -1; + } + + bytes_received = lwip_recv_with_timeout(client->sockfd, frame+1, 1, 0, remaining_time); + if (bytes_received <= 0) { + if (bytes_received == 0) { + printf("Connection lwip_closed by server.\n"); + } else { + printf("receive message failed"); + } + return -1; + } + //unsigned char fin = (frame[0] & 0x80) >> 7; + *code = (WebSocketMessageType) (frame[0] & 0x0F); + unsigned char mask = (frame[1] & 0x80) >> 7; + unsigned int payload_len = frame[1] & 0x7F; + + + size_t offset = 2; + if (payload_len == 126) { + // 检查超时 + if (check_timeout()) { + printf("receive_websocket_message: timeout while waiting for extended length (126)\n"); + return -1; + } + + // 使用带超时的lwip_recv + uint32_t remaining_time = timeout_ms - ((uint32_t)osKernelGetTickCount() - start_time); + if (remaining_time <= 0) { + printf("receive_websocket_message: timeout while waiting for extended length (126)\n"); + return -1; + } + + unsigned short extended_payload_len; + int ret = lwip_recv_with_timeout(client->sockfd, frame+offset, 2, 0, remaining_time); + if (ret <= 0) { + printf("receive_websocket_message: failed to receive extended length (126)\n"); + return -1; + } + bytes_received += ret; + memcpy(&extended_payload_len, frame + 2, 2); + payload_len = ntohs(extended_payload_len); + //printf("receive_websocket_message:payload_len=0x%X",extended_payload_len); + offset += 2; + } else if (payload_len == 127) { + // 检查超时 + if (check_timeout()) { + printf("receive_websocket_message: timeout while waiting for extended length (127)\n"); + return -1; + } + + // 使用带超时的lwip_recv + uint32_t remaining_time = timeout_ms - ((uint32_t)osKernelGetTickCount() - start_time); + if (remaining_time <= 0) { + printf("receive_websocket_message: timeout while waiting for extended length (127)\n"); + return -1; + } + + uint64_t extended_payload_len; + int ret = lwip_recv_with_timeout(client->sockfd, frame+offset, 8, 0, remaining_time); + if (ret <= 0) { + printf("receive_websocket_message: failed to receive extended length (127)\n"); + return -1; + } + bytes_received += ret; + memcpy(&extended_payload_len, frame + 2, 8); + payload_len = ntohll(extended_payload_len); + offset += 8; + } + if(payload_len>len) + { + printf("receive_websocket_message:len =%d ERROR",payload_len); + + // if(*code == WS_BINARY) + // { + + // } + // fopen + // 使用带超时的lwip_recv丢弃多余数据 + uint32_t remaining_time = timeout_ms - ((uint32_t)osKernelGetTickCount() - start_time); + if (remaining_time > 0) { + lwip_recv_with_timeout(client->sockfd, buf, len, 0, remaining_time); + } + return -1; + } + timer_ms = (uint32_t)osKernelGetTickCount(); + //printf("receive_websocket_message 3 timer_ms=%d\n",timer_ms); + // printf("receive_websocket_message :"); + // for(int i = 0;isockfd, frame+offset, 4, 0, remaining_time); + if (ret <= 0) { + printf("receive_websocket_message: failed to receive masking key\n"); + return -1; + } + bytes_received = ret; + memcpy(masking_key, frame + offset, 4); + offset += 4; + } + bytes_received = 0; + + while(1)//读主体内容 + { + // 检查超时 + if (check_timeout()) { + printf("receive_websocket_message: timeout while receiving payload data\n"); + return -1; + } + + // 使用带超时的lwip_recv + uint32_t remaining_time = timeout_ms - ((uint32_t)osKernelGetTickCount() - start_time); + if (remaining_time <= 0) { + printf("receive_websocket_message: timeout while receiving payload data\n"); + return -1; + } + + //bk_sleep(recv_len/1000+1); + recv_len = lwip_recv_with_timeout(client->sockfd, buf+bytes_received, payload_len-bytes_received, 0, remaining_time); + if(recv_len>0) + bytes_received += recv_len; + else if(recv_len<0) + { + printf("receive_websocket_message: recv_len =%d lwip_recv ERROR!!!!!!",recv_len); + return -1; + } + //printf("receive_websocket_message:bytes_received=%d recv_len =%d",bytes_received,recv_len); + if(bytes_received >= payload_len) + { + //printf("receive_websocket_message:payload_len=%d,bytes_received=%d",payload_len,bytes_received); + break; + } + + } + if(mask) + { + for (size_t i = 0; i < payload_len; ++i) { + buf[i] = buf[i] ^ masking_key[i % 4]; + } + } + + // if (*code == WS_TEXT) { + // printf("Received text message: %.*s\n", (int)payload_len, buf); + // } else if (*code == WS_BINARY) { + // printf("Received binary message of length %zu\n", payload_len); + // } + + if (*code == WS_TEXT) + { + + }else if(*code == WS_BINARY) + { + + }else if(*code == WS_CLOSE) + { + payload_len = 0; + }else if(*code == WS_PONG) + { + payload_len = 0; + }else if(*code == WS_PING) + { + printf("send_websocket_message WS_PONG \n"); + send_websocket_message(client,buf,payload_len,WS_PONG); + payload_len = 0; + }else if(*code == WS_CONT) + { + payload_len = 0; + } + last_byte = buf[payload_len-1]; + timer_ms = (uint32_t)osKernelGetTickCount(); + //printf("receive_websocket_message 4 timer_ms=%d len = %d\n",timer_ms,payload_len); + return payload_len;//parse_frame(buf, bytes_received, buf,code); +} + + + + #ifdef __cplusplus } #endif /* __cplusplus */ \ No newline at end of file diff --git a/code/sdk/zhihui/ai_old/zhihui_ws.h b/code/sdk/zhihui/ai_old/zhihui_ws.h index 7b5908be6..5e583b7ab 100644 --- a/code/sdk/zhihui/ai_old/zhihui_ws.h +++ b/code/sdk/zhihui/ai_old/zhihui_ws.h @@ -3,6 +3,14 @@ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +#include + +#include +#include +#include "bk_websocket.h" + + ////////////////////////// UI 使用接口///////////////////////////////// bool init_ai_task(uint8_t type); @@ -21,15 +29,15 @@ bool uapi_start_tts_single_txt(char *buf); void uapi_UI_stop_start_asr_timeout(void); void ai_cleanup_resources(void); -typedef enum -{ - AI_TYPE_0=0, - AI_TYPE_JIU, //九天 - AI_TYPE_DE, //deepseek - AI_TYPE_DOU,//豆包 - AI_TYPE_WEN,//文心一言 - AI_TYPE_KIMI,//KIMI -}G_AI_TYPE; +// typedef enum +// { +// AI_TYPE_0=0, +// AI_TYPE_JIU, //九天 +// AI_TYPE_DE, //deepseek +// AI_TYPE_DOU,//豆包 +// AI_TYPE_WEN,//文心一言 +// AI_TYPE_KIMI,//KIMI +// }G_AI_TYPE; bool pre_AI_init(void); @@ -62,13 +70,13 @@ void uapi_start_asr_task(void); void asr_record_test(void); -bool ai_chat_send_txt(uint8_t type,char *msg); +// bool ai_chat_send_txt(uint8_t type, char *msg, WebSocketClient *chat_client); void uapi_start_asr(void); void uapi_stop_tts(void); - +int receive_websocket_message(WebSocketClient *client, unsigned char *buf,int len,WebSocketMessageType *code, uint32_t timeout_ms); #ifdef __cplusplus diff --git a/code/sdk/zhihui/services/ble_service/blemoder/CMakeLists.txt b/code/sdk/zhihui/services/ble_service/blemoder/CMakeLists.txt index aaca2b201..05b9f00c0 100644 --- a/code/sdk/zhihui/services/ble_service/blemoder/CMakeLists.txt +++ b/code/sdk/zhihui/services/ble_service/blemoder/CMakeLists.txt @@ -67,6 +67,7 @@ set(PRIVATE_HEADER ${ROOT_DIR}/application/wearable/${DEVICE_PATH}/nativeapp/nativelauncher/include ${ROOT_DIR}/middleware/services/gui/uikit/ui/frameworks ${ROOT_DIR}/zhihui/AI/AImain + ${ROOT_DIR}/application/wearable/${DEVICE_PATH}/nativeapp/nativeui/aivoice/include # ${ROOT_DIR}/application/wearable/${DEVICE_PATH}/nativeapp/nativeui/schedule/include/ScheduleModel.h # ${ROOT_DIR}/middleware/services/gui/uikit/surface/interfaces/kits # /home/hcj/FD230/code/sdk/middleware/services/gui/uikit/surface/interfaces/kits/ibuffer_consumer_listener.h diff --git a/code/sdk/zhihui/services/ble_service/blemoder/ble_instruct_init.cpp b/code/sdk/zhihui/services/ble_service/blemoder/ble_instruct_init.cpp index a52b27eae..fcc943b5e 100644 --- a/code/sdk/zhihui/services/ble_service/blemoder/ble_instruct_init.cpp +++ b/code/sdk/zhihui/services/ble_service/blemoder/ble_instruct_init.cpp @@ -13,9 +13,13 @@ #include "app_store.h" #include "rebootandshutdown/include/RebootAndShutdownModelC.h" #include "ScheduleModel.h" +#include "AiVoiceModelC.h" + void ble_instruct_init(void) { + ble_AI_init(); + ble_Schedule_init(); ble_dial_init(); -- 2.17.1