#if AI_VOICE_ENABLE
uapi_UI_start_asr(GetCurUsing4GAiModelType());
#endif
- }
+ }
else
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "init_4gai_task fail!!");
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)
{
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);
}
{
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;
}
#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
#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));
}
else
{
+ // if(AiVoiceModel::GetInstance().GetUserQuestionStr() !=)
+ // SendMsgToUIService(UI_UPDATE_USER_QUESTION_STR, 0, nullptr, 0);
+
SendMsgToUIService(UI_UPDATE_AI_ANSWER_STR, 0, nullptr, 0);
}
}
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;
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
}
--- /dev/null
+/* 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 <string.h>
+
+#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 <string.h>
+
+#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 */
*/
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
${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
#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;
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");
}
} 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;
}
/**
// 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);
MediaMutexUnLock(g_aiMediaMutex);
return;
}
+
+ // 保存线程句柄(如果需要的话)
+ g_recordContext->recordThread = (void*)task_cfg.task_handle;
g_recordStatus = 1; // Set recording status
MediaMutexUnLock(g_aiMediaMutex);
--- /dev/null
+/*
+ * 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 <climits>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string>
+#include <iostream>
+#include <unistd.h>
+#include <time.h>
+#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<AudioStreamIn> g_streamIn = nullptr;
+static std::shared_ptr<AudioStreamOut> 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
${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
#include <stdlib.h>
#include <string.h>
#include <math.h>
+#include "ai_core_types.h"
+#include "websocket_server_manager.h"
+
// #include "phoneaddressmodel.h" // Commented out due to missing header
#ifdef __cplusplus
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);
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;
}
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);
{ (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)
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);
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
/* 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 */
/* 常量定义 */
*/
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 准备开始标志
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定义时可用)
#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;
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);
/**
* @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;
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初始化
*/
/**
* @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);
}
cJSON_AddStringToObject(dataJSON, "type", "jiutian-lan");
- cJSON_AddStringToObject(dataJSON, "content", msg);
+ cJSON_AddStringToObject(dataJSON, "content", final_msg);
char *jsonString = cJSON_PrintUnformatted(dataJSON);
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");
}
}
}
+/**
+ * @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主线程
*/
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);
}
/* 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 */
/**
* @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初始化
* @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主线程
*/
void chat_server_deinit(void);
+// bool ai_chat_send_txt(uint8_t type, char *msg, WebSocketClient *chat_client, bool cache)
+
/* 全局变量声明 */
extern WebSocketClient *chat_client;
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;
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
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数据包回调函数
*/
}
/**
- * @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;
}
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服务
*/
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
/* 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 */
/* 常量定义 */
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服务
*/
void tts_server_deinit(void);
+/**
+ * @brief TTS结果分析
+ * @param buf 结果字符串
+ * @return 返回事件类型枚举值
+ */
+tts_event_type_t tts_analysis(char *buf);
+
/* 全局变量声明 */
extern WebSocketClient *tts_client;
--- /dev/null
+/**
+ * @file websocket_server_example.cpp
+ * @brief WebSocket服务器使用示例
+ * @version 1.0.0
+ * @date 2024-09-05
+ *
+ * 展示如何使用统一的WebSocket服务器管理接口
+ */
+
+#include "websocket_server_manager.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#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
--- /dev/null
+/**
+ * @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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#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
--- /dev/null
+/**
+ * @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 <stdint.h>
+#include <stdbool.h>
+
+#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 */
// 包含新的WebSocket解析器
#include "websocket_parser_wrapper.h"
-#define TEST_AI_WS 0
+#define TEST_AI_WS 1
#ifdef __cplusplus
extern "C" {
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;
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;
if (send_handshake(client,headers) < 0) { //发送头
printf("socket send_handshake client_ERROR");
- close(sockfd);
+ lwip_close(sockfd);
goto client_ERROR;
}
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);
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:
}
// 使用标准 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");
}
// 设置 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));
}
// 连接到服务器
+
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);
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);
// 先关闭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;
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;i<offset;i++)
+ // {
+ // printf("%02X",frame[i]);
+ // }
+ // printf("receive_websocket_message:payload_len=%d frame[0]&0x8E=%d",payload_len,frame[0]&0x8E);
+ if (mask) {
+ // 检查超时
+ if (check_timeout()) {
+ printf("receive_websocket_message: timeout while waiting for masking key\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 masking key\n");
+ return -1;
+ }
+
+ int ret = lwip_recv_with_timeout(client->sockfd, 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
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stddef.h>
+#include "bk_websocket.h"
+
+
////////////////////////// UI 使用接口/////////////////////////////////
bool init_ai_task(uint8_t type);
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);
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
${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
#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();