#ifdef SUPPORT_ZHIHUI_AI_SERVICE\r
#include "ai_at_cmd.h"\r
#endif\r
+#ifdef SUPPORT_ZHIHUI_AI_OLD\r
+#include "ai_old_at_cmd.h"\r
+#endif\r
\r
static void register_at_service(void)\r
{\r
at_cmd_add_moudle(cat1_table, cat1_cmd_count);\r
#endif\r
\r
+#ifdef SUPPORT_ZHIHUI_AI_OLD\r
+ uint32_t ai_old_cmd_count;\r
+ at_cmd_table_t *ai_old_table = get_ai_old_cmd_table(&ai_old_cmd_count);\r
+ at_cmd_add_moudle(ai_old_table, ai_old_cmd_count);\r
+#endif\r
+\r
#ifdef SUPPORT_AT_LIS2DOC\r
uint32_t lis2doc_cmd_count;\r
at_cmd_table_t *lis2doc_table = get_lis2doc_cmd_table(&lis2doc_cmd_count);\r
#ai 相关配置
"aiMediaService",
"aiService",
+ #ai_old相关配置
+ "zhihui_ai_old",
#音频相关配置
"audio_manager",
"CustomUIcomponentTool",
#define __PDP_ENDIAN 3412
+#ifndef BIG_ENDIAN
#define BIG_ENDIAN __BIG_ENDIAN
+#endif
+#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#ifndef PDP_ENDIAN
#define PDP_ENDIAN __PDP_ENDIAN
+#endif
+#ifndef BYTE_ORDER
#define BYTE_ORDER __BYTE_ORDER
+#endif
static __inline uint16_t __bswap16(uint16_t __x)
{
endif()
add_subdirectory_if_exist(4gTest)
add_subdirectory_if_exist(ai)
+add_subdirectory_if_exist(ai_old)
add_subdirectory_if_exist(ai/media)
add_subdirectory_if_exist(websocket)
${ROOT_DIR}/application/wearable/ohos_deps
${ROOT_DIR}/open_source/openharmony/third_party/cJSON
${ROOT_DIR}/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include
+${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include
+${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include
)
# 新的AI服务架构源文件 (LiteOS-M优化版v2)
${CMAKE_CURRENT_SOURCE_DIR}/src/ai_tts_service.c
${CMAKE_CURRENT_SOURCE_DIR}/src/ai_event_manager.c
${CMAKE_CURRENT_SOURCE_DIR}/src/dev_authentication.c
+${CMAKE_CURRENT_SOURCE_DIR}/src/http_getfile.c
${CMAKE_CURRENT_SOURCE_DIR}/example/ai_at_cmd.c
# ${CMAKE_CURRENT_SOURCE_DIR}/example/ai_demo.c
)
* @date 2024-09-02
*
* 本文件提供AI服务的AT命令功能,集成到LiteOS AT指令系统中
+ *
+ * 支持的AT命令列表:
+ *
+ * === 基础服务命令 ===
+ * AT^AIINIT - 初始化AI服务
+ * AT^AICONNECT - 连接AI服务
+ * AT^AISTATUS - 查询AI服务状态
+ * AT^AICLEANUP - 清理AI服务
+ *
+ * === AI功能测试命令 ===
+ * AT^AIASR=duration_ms - 测试ASR服务(语音识别)
+ * AT^AICHAT=message - 测试Chat服务(对话)
+ * AT^AITTS=text - 测试TTS服务(语音合成)
+ * AT^AIALLTEST - 运行所有服务的综合测试
+ *
+ * === 媒体录音播放命令 ===
+ * AT^AIRECORD=filename,duration - 录音到文件
+ * 参数:filename - 录音文件名(如:record.mp3)
+ * duration - 录音时长(秒,1-3600)
+ * 示例:AT^AIRECORD=test.mp3,30
+ *
+ * AT^AIPLAY=filename - 播放音频文件
+ * 参数:filename - 音频文件名
+ * 示例:AT^AIPLAY=test.mp3
+ *
+ * AT^AIRECORDSTOP - 停止录音
+ * AT^AIPLAYSTOP - 停止播放
+ *
+ * AT^AIMEDIATEST=duration - 媒体服务测试(边录边放)
+ * AT^AIMEDIASTOP - 停止媒体测试
+ *
+ * === 系统管理命令 ===
+ * AT^AIMEMORY=pool_type - 测试内存池
+ * AT^AIPERF - 性能测试
+ * AT^AISTATS - 显示测试统计信息
+ * AT^AIRECONNECT=service_type - 重连指定AI服务
+ *
+ * === 使用示例 ===
+ * 1. 初始化并连接:
+ * AT^AIINIT
+ * AT^AICONNECT
+ *
+ * 2. 录音30秒到文件:
+ * AT^AIRECORD=my_voice.mp3,30
+ *
+ * 3. 播放录音文件:
+ * AT^AIPLAY=my_voice.mp3
+ *
+ * 4. 停止录音或播放:
+ * AT^AIRECORDSTOP
+ * AT^AIPLAYSTOP
*/
#ifdef __TEST_MODE__
static ai_media_test_context_t g_media_test_ctx = {0};
+/* 文件录音/播放状态 */
+typedef struct {
+ bool is_recording;
+ bool is_playing;
+ char record_filename[256];
+ char play_filename[256];
+ uint32_t record_start_time;
+ uint32_t play_start_time;
+ uint32_t record_duration; // 录音持续时间(秒)
+ uint32_t recorded_size; // 已录音数据大小
+ uint32_t played_size; // 已播放数据大小
+ FILE* record_file;
+ FILE* play_file;
+} ai_file_media_context_t;
+
+static ai_file_media_context_t g_file_media_ctx = {0};
+
/**
* @brief 媒体测试录音回调函数 - 边录边放
*/
}
}
+/**
+ * @brief 文件录音回调函数
+ */
+static void file_record_callback(uint8_t *data, uint32_t size)
+{
+ if (!g_file_media_ctx.is_recording || g_file_media_ctx.record_file == NULL) {
+ return;
+ }
+
+ // 写入录音数据到文件
+ size_t written = fwrite(data, 1, size, g_file_media_ctx.record_file);
+ if (written == size) {
+ g_file_media_ctx.recorded_size += size;
+
+ // 每1000个数据包打印一次状态
+ static uint32_t record_packet_count = 0;
+ record_packet_count++;
+ if (record_packet_count % 1000 == 0) {
+ printf("[AI_FILE_RECORD] 已录音: %u字节, 数据包: %u\n",
+ g_file_media_ctx.recorded_size, record_packet_count);
+ }
+ } else {
+ printf("[AI_FILE_RECORD] 文件写入错误: 期望%u字节, 实际写入%zu字节\n", size, written);
+ }
+
+ // 检查录音时间是否到达
+ uint32_t current_time = get_timestamp_ms();
+ if (current_time - g_file_media_ctx.record_start_time >= g_file_media_ctx.record_duration * 1000) {
+ printf("[AI_FILE_RECORD] 录音时间到达,准备停止录音\n");
+ g_file_media_ctx.is_recording = false;
+ }
+}
+
/* ================================ 内部函数 ================================ */
/**
}
}
+/**
+ * @brief AT^AIRECORD - 录音到文件
+ * @param para 参数:filename,duration_seconds
+ * @param paraLen 参数长度
+ * @param strCommonCmd 通用命令字符串
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ *
+ * 格式: AT^AIRECORD=filename,duration
+ * 示例: AT^AIRECORD=test_record.mp3,30
+ */
+static uint32_t at_ai_record_to_file(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(strCommonCmd);
+
+ printf("[AI_AT] 执行 AT^AIRECORD - 录音到文件\n");
+
+ // 检查是否已经在录音
+ if (g_file_media_ctx.is_recording) {
+ printf("[AI_AT] 录音已在进行中,请先停止当前录音\n");
+ return ERRCODE_FAIL;
+ }
+
+ // 解析参数:文件名和录音时长
+ char filename[256] = "ai_record.mp3"; // 默认文件名
+ uint32_t duration = 30; // 默认30秒
+
+ if (para != NULL && paraLen > 0) {
+ uint8_t argv[AI_AT_MAX_ARGC][AI_AT_MAX_ARGV_LEN] = { 0 };
+ uint32_t argc = 0;
+
+ int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+ if (ret == AI_AT_SUCCESS && argc > 0) {
+ // 第一个参数:文件名
+ if (strlen((char*)argv[0]) > 0) {
+ strncpy(filename, (char*)argv[0], sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
+
+ // 第二个参数:录音时长
+ if (argc > 1) {
+ int dur = atoi((char*)argv[1]);
+ if (dur > 0 && dur <= 3600) { // 最大1小时
+ duration = (uint32_t)dur;
+ } else {
+ printf("[AI_AT] 无效的录音时长: %d (有效范围: 1-3600秒)\n", dur);
+ return ERRCODE_FAIL;
+ }
+ }
+ }
+ }
+
+ printf("[AI_AT] 开始录音到文件: %s, 时长: %u秒\n", filename, duration);
+
+ // 初始化文件录音上下文
+ memset(&g_file_media_ctx, 0, sizeof(g_file_media_ctx));
+ strncpy(g_file_media_ctx.record_filename, filename, sizeof(g_file_media_ctx.record_filename) - 1);
+ g_file_media_ctx.record_duration = duration;
+ g_file_media_ctx.is_recording = true;
+ g_file_media_ctx.record_start_time = get_timestamp_ms();
+
+ // 打开录音文件
+ g_file_media_ctx.record_file = fopen(filename, "wb");
+ if (g_file_media_ctx.record_file == NULL) {
+ printf("[AI_AT] 无法创建录音文件: %s\n", filename);
+ g_file_media_ctx.is_recording = false;
+ return ERRCODE_FAIL;
+ }
+
+ // 初始化媒体服务
+ int32_t init_ret = ai_media_service_init();
+ if (init_ret != 0) {
+ printf("[AI_AT] 媒体服务初始化失败: %d\n", init_ret);
+ fclose(g_file_media_ctx.record_file);
+ g_file_media_ctx.record_file = NULL;
+ g_file_media_ctx.is_recording = false;
+ return ERRCODE_FAIL;
+ }
+
+ // 设置录音回调
+ ai_media_set_record_callback(file_record_callback);
+
+ // 启动录音服务
+ printf("[AI_AT] 启动录音服务...\n");
+ uint8_t record_format[] = "mp3";
+ ai_media_record_start(record_format);
+
+ // 检查录音状态
+ if (!ai_media_get_record_status()) {
+ printf("[AI_AT] 录音服务启动失败\n");
+ fclose(g_file_media_ctx.record_file);
+ g_file_media_ctx.record_file = NULL;
+ ai_media_service_deinit();
+ g_file_media_ctx.is_recording = false;
+ return ERRCODE_FAIL;
+ }
+
+ printf("[AI_AT] 录音已启动!录音将保存到文件: %s\n", filename);
+ printf("[AI_AT] 录音将在%u秒后自动停止\n", duration);
+
+ // 等待录音完成
+ uint32_t check_interval = 1000; // 每秒检查一次
+ uint32_t elapsed_time = 0;
+
+ while (g_file_media_ctx.is_recording && elapsed_time < duration * 1000) {
+#ifndef __TEST_MODE__
+ osDelay(check_interval);
+#else
+ usleep(check_interval * 1000);
+#endif
+ elapsed_time += check_interval;
+
+ // 每5秒打印一次状态
+ if (elapsed_time % 5000 == 0) {
+ printf("[AI_AT] 录音进行中... 已录制: %u字节, 剩余时间: %u秒\n",
+ g_file_media_ctx.recorded_size, (duration * 1000 - elapsed_time) / 1000);
+ }
+ }
+
+ // 停止录音
+ printf("[AI_AT] 停止录音...\n");
+ g_file_media_ctx.is_recording = false;
+
+ // 停止录音服务
+ ai_media_record_stop();
+
+ // 关闭文件
+ if (g_file_media_ctx.record_file != NULL) {
+ fclose(g_file_media_ctx.record_file);
+ g_file_media_ctx.record_file = NULL;
+ }
+
+ // 反初始化媒体服务
+ ai_media_service_deinit();
+
+ // 打印录音结果
+ printf("[AI_AT] 录音完成!\n");
+ printf("[AI_AT] 文件: %s\n", filename);
+ printf("[AI_AT] 录音时长: %u秒\n", duration);
+ printf("[AI_AT] 文件大小: %u字节\n", g_file_media_ctx.recorded_size);
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief AT^AIPLAY - 播放音频文件
+ * @param para 参数:filename
+ * @param paraLen 参数长度
+ * @param strCommonCmd 通用命令字符串
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ *
+ * 格式: AT^AIPLAY=filename
+ * 示例: AT^AIPLAY=test_record.mp3
+ */
+static uint32_t at_ai_play_from_file(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(strCommonCmd);
+
+ printf("[AI_AT] 执行 AT^AIPLAY - 播放音频文件\n");
+
+ // 检查是否已经在播放
+ if (g_file_media_ctx.is_playing) {
+ printf("[AI_AT] 播放已在进行中,请先停止当前播放\n");
+ return ERRCODE_FAIL;
+ }
+
+ // 解析参数:文件名
+ char filename[256] = "ai_record.mp3"; // 默认文件名
+
+ if (para != NULL && paraLen > 0) {
+ uint8_t argv[AI_AT_MAX_ARGC][AI_AT_MAX_ARGV_LEN] = { 0 };
+ uint32_t argc = 0;
+
+ int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+ if (ret == AI_AT_SUCCESS && argc > 0) {
+ if (strlen((char*)argv[0]) > 0) {
+ strncpy(filename, (char*)argv[0], sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
+ }
+ }
+
+ printf("[AI_AT] 开始播放文件: %s\n", filename);
+
+ // 检查文件是否存在
+ FILE* test_file = fopen(filename, "rb");
+ if (test_file == NULL) {
+ printf("[AI_AT] 无法打开音频文件: %s\n", filename);
+ return ERRCODE_FAIL;
+ }
+
+ // 获取文件大小
+ fseek(test_file, 0, SEEK_END);
+ long file_size = ftell(test_file);
+ fseek(test_file, 0, SEEK_SET);
+ fclose(test_file);
+
+ if (file_size <= 0) {
+ printf("[AI_AT] 音频文件为空或无效: %s\n", filename);
+ return ERRCODE_FAIL;
+ }
+
+ printf("[AI_AT] 文件大小: %ld字节\n", file_size);
+
+ // 初始化文件播放上下文
+ strncpy(g_file_media_ctx.play_filename, filename, sizeof(g_file_media_ctx.play_filename) - 1);
+ g_file_media_ctx.is_playing = true;
+ g_file_media_ctx.play_start_time = get_timestamp_ms();
+ g_file_media_ctx.played_size = 0;
+
+ // 打开播放文件
+ g_file_media_ctx.play_file = fopen(filename, "rb");
+ if (g_file_media_ctx.play_file == NULL) {
+ printf("[AI_AT] 无法打开播放文件: %s\n", filename);
+ g_file_media_ctx.is_playing = false;
+ return ERRCODE_FAIL;
+ }
+
+ // 初始化媒体服务
+ int32_t init_ret = ai_media_service_init();
+ if (init_ret != 0) {
+ printf("[AI_AT] 媒体服务初始化失败: %d\n", init_ret);
+ fclose(g_file_media_ctx.play_file);
+ g_file_media_ctx.play_file = NULL;
+ g_file_media_ctx.is_playing = false;
+ return ERRCODE_FAIL;
+ }
+
+ // 启动播放服务
+ printf("[AI_AT] 启动播放服务...\n");
+ uint8_t play_format[] = "mp3";
+ ai_media_play_start(play_format);
+
+ // 检查播放状态
+ if (!ai_media_get_play_status()) {
+ printf("[AI_AT] 播放服务启动失败\n");
+ fclose(g_file_media_ctx.play_file);
+ g_file_media_ctx.play_file = NULL;
+ ai_media_service_deinit();
+ g_file_media_ctx.is_playing = false;
+ return ERRCODE_FAIL;
+ }
+
+ printf("[AI_AT] 开始播放文件: %s\n", filename);
+
+ // 读取并播放文件数据
+ uint8_t buffer[1024]; // 1KB缓冲区
+ size_t bytes_read;
+ uint32_t total_played = 0;
+
+ while (g_file_media_ctx.is_playing && (bytes_read = fread(buffer, 1, sizeof(buffer), g_file_media_ctx.play_file)) > 0) {
+ // 播放数据
+ ai_media_play_buffer(buffer, bytes_read);
+ total_played += bytes_read;
+ g_file_media_ctx.played_size = total_played;
+
+ // 每播放100KB打印一次状态
+ if (total_played % (100 * 1024) == 0) {
+ printf("[AI_AT] 播放进度: %u/%ld字节 (%.1f%%)\n",
+ total_played, file_size, (float)total_played * 100.0f / file_size);
+ }
+
+ // 控制播放速度,避免播放过快
+#ifndef __TEST_MODE__
+ osDelay(50); // 延时50ms
+#else
+ usleep(50000); // 测试模式延时50ms
+#endif
+ }
+
+ // 停止播放
+ printf("[AI_AT] 停止播放...\n");
+ g_file_media_ctx.is_playing = false;
+
+ // 停止播放服务
+ ai_media_play_stop();
+
+ // 关闭文件
+ if (g_file_media_ctx.play_file != NULL) {
+ fclose(g_file_media_ctx.play_file);
+ g_file_media_ctx.play_file = NULL;
+ }
+
+ // 反初始化媒体服务
+ ai_media_service_deinit();
+
+ // 打印播放结果
+ uint32_t play_duration = get_timestamp_ms() - g_file_media_ctx.play_start_time;
+ printf("[AI_AT] 播放完成!\n");
+ printf("[AI_AT] 文件: %s\n", filename);
+ printf("[AI_AT] 播放时长: %u毫秒\n", play_duration);
+ printf("[AI_AT] 播放数据: %u字节\n", total_played);
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief AT^AIRECORDSTOP - 停止录音
+ * @param para 参数(未使用)
+ * @param paraLen 参数长度
+ * @param strCommonCmd 通用命令字符串
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+static uint32_t at_ai_record_stop(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(para);
+ UNUSED(paraLen);
+ UNUSED(strCommonCmd);
+
+ printf("[AI_AT] 执行 AT^AIRECORDSTOP - 停止录音\n");
+
+ if (!g_file_media_ctx.is_recording) {
+ printf("[AI_AT] 当前没有进行中的录音\n");
+ return ERRCODE_SUCC;
+ }
+
+ // 停止录音
+ printf("[AI_AT] 强制停止录音...\n");
+ g_file_media_ctx.is_recording = false;
+
+ // 停止录音服务
+ ai_media_record_stop();
+
+ // 关闭文件
+ if (g_file_media_ctx.record_file != NULL) {
+ fclose(g_file_media_ctx.record_file);
+ g_file_media_ctx.record_file = NULL;
+ }
+
+ // 反初始化媒体服务
+ ai_media_service_deinit();
+
+ printf("[AI_AT] 录音已停止\n");
+ printf("[AI_AT] 文件: %s\n", g_file_media_ctx.record_filename);
+ printf("[AI_AT] 录制数据: %u字节\n", g_file_media_ctx.recorded_size);
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief AT^AIPLAYSTOP - 停止播放
+ * @param para 参数(未使用)
+ * @param paraLen 参数长度
+ * @param strCommonCmd 通用命令字符串
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+static uint32_t at_ai_play_stop(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(para);
+ UNUSED(paraLen);
+ UNUSED(strCommonCmd);
+
+ printf("[AI_AT] 执行 AT^AIPLAYSTOP - 停止播放\n");
+
+ if (!g_file_media_ctx.is_playing) {
+ printf("[AI_AT] 当前没有进行中的播放\n");
+ return ERRCODE_SUCC;
+ }
+
+ // 停止播放
+ printf("[AI_AT] 强制停止播放...\n");
+ g_file_media_ctx.is_playing = false;
+
+ // 停止播放服务
+ ai_media_play_stop();
+
+ // 关闭文件
+ if (g_file_media_ctx.play_file != NULL) {
+ fclose(g_file_media_ctx.play_file);
+ g_file_media_ctx.play_file = NULL;
+ }
+
+ // 反初始化媒体服务
+ ai_media_service_deinit();
+
+ printf("[AI_AT] 播放已停止\n");
+ printf("[AI_AT] 文件: %s\n", g_file_media_ctx.play_filename);
+ printf("[AI_AT] 播放数据: %u字节\n", g_file_media_ctx.played_size);
+
+ return ERRCODE_SUCC;
+}
+
/**
* @brief AT^AIMEDIATEST - 媒体服务测试(边录边放)
* @param para 参数:测试持续时间(秒),默认10秒
{ (char *)"AT^AISTATS", at_ai_stats, (char *)"Show test statistics" },
{ (char *)"AT^AICLEANUP", at_ai_cleanup, (char *)"Cleanup AI services" },
{ (char *)"AT^AIRECONNECT", at_ai_reconnect, (char *)"service_type (0=ASR,1=Chat,2=TTS)" },
+ { (char *)"AT^AIRECORD", at_ai_record_to_file, (char *)"filename,duration_seconds" },
+ { (char *)"AT^AIPLAY", at_ai_play_from_file, (char *)"filename" },
+ { (char *)"AT^AIRECORDSTOP", at_ai_record_stop, (char *)"Stop recording" },
+ { (char *)"AT^AIPLAYSTOP", at_ai_play_stop, (char *)"Stop playback" },
{ (char *)"AT^AIMEDIATEST", at_ai_media_test, (char *)"duration_seconds (default=10, max=300)" },
{ (char *)"AT^AIMEDIASTOP", at_ai_media_stop, (char *)"Stop media test" },
};
result = at_ai_performance_test(NULL, 0, NULL);
printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
- printf("\n9. 测试 AT^AIMEDIATEST=5 - 媒体服务测试(边录边放5秒)\n");
+ printf("\n9. 测试 AT^AIRECORD=test_record.mp3,30 - 录音到文件\n");
+ uint8_t record_para[] = "test_record.mp3,30";
+ result = at_ai_record_to_file(record_para, sizeof(record_para) - 1, NULL);
+ printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
+
+ printf("\n10. 测试 AT^AIPLAY=test_record.mp3 - 播放音频文件\n");
+ uint8_t play_para[] = "test_record.mp3";
+ result = at_ai_play_from_file(play_para, sizeof(play_para) - 1, NULL);
+ printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
+
+ printf("\n11. 测试 AT^AIRECORDSTOP - 停止录音\n");
+ result = at_ai_record_stop(NULL, 0, NULL);
+ printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
+
+ printf("\n12. 测试 AT^AIPLAYSTOP - 停止播放\n");
+ result = at_ai_play_stop(NULL, 0, NULL);
+ printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
+
+ printf("\n13. 测试 AT^AIMEDIATEST=5 - 媒体服务测试(边录边放5秒)\n");
uint8_t media_para[] = "5";
result = at_ai_media_test(media_para, sizeof(media_para) - 1, NULL);
printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
- printf("\n10. 最终统计和清理\n");
+ printf("\n14. 最终统计和清理\n");
result = at_ai_stats(NULL, 0, NULL);
printf(" 统计结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
- printf("\n11. 测试 AT^AICLEANUP - 清理AI服务\n");
+ printf("\n15. 测试 AT^AICLEANUP - 清理AI服务\n");
result = at_ai_cleanup(NULL, 0, NULL);
printf(" 结果: %s\n", result == ERRCODE_SUCC ? "✅ 成功" : "❌ 失败");
printf("✓ 所有命令表注册成功\n");
printf("✓ 命令处理函数正常工作\n");
printf("✓ 可以集成到LiteOS AT系统中\n");
+ printf("✓ 新增文件录音和播放功能\n");
+ printf("✓ 支持以下新AT命令:\n");
+ printf(" - AT^AIRECORD=filename,duration - 录音到文件\n");
+ printf(" - AT^AIPLAY=filename - 播放音频文件\n");
+ printf(" - AT^AIRECORDSTOP - 停止录音\n");
+ printf(" - AT^AIPLAYSTOP - 停止播放\n");
return 0;
}
/* WebSocket相关头文件 */
#ifdef SUPPORT_ZHIHUI_AI_SERVICE
// 宏定义:USE_ZHIHUI_WEBSOCKET = 1 使用新的zhihui接口,= 0 使用海思原有接口
-#define USE_ZHIHUI_WEBSOCKET 0
+#define USE_ZHIHUI_WEBSOCKET 1
#ifndef USE_ZHIHUI_WEBSOCKET
#define USE_ZHIHUI_WEBSOCKET 0 // 默认使用原有接口
#endif
ai_error_t result = AI_OK;
if (conn->state != AI_CONN_STATE_CONNECTED || conn->websocket < 0) {
- AI_LOGW("Service %s not connected, cannot send raw data", g_service_names[service_type]);
+ AI_LOGW("Service %s not ready for sending: state=%d, websocket=%d",
+ g_service_names[service_type], conn->state, conn->websocket);
result = AI_ERROR_SERVICE_UNAVAILABLE;
} else {
// 检查缓冲区空间
#ifndef __TEST_MODE__
// 发送二进制数据
+ AI_LOGI("Sending %lu bytes to service %s (websocket=%d)",
+ (unsigned long)length, g_service_names[service_type], conn->websocket);
int ret = WebSocketClientSendBinStart(conn->websocket, (const char*)conn->send_buffer->buffer, length);
if (ret < 0) {
- AI_LOGE("Failed to send raw data to service %s", g_service_names[service_type]);
+ AI_LOGE("Failed to send raw data to service %s: WebSocket error=%d",
+ g_service_names[service_type], ret);
result = AI_ERROR_WEBSOCKET;
} else {
// 更新统计
ai_error_t result = AI_OK;
if (conn->state != AI_CONN_STATE_CONNECTED || conn->websocket < 0) {
- AI_LOGW("Service %s not connected, cannot send data", g_service_names[service_type]);
+ AI_LOGW("Service %s not ready for sending: state=%d, websocket=%d",
+ g_service_names[service_type], conn->state, conn->websocket);
result = AI_ERROR_SERVICE_UNAVAILABLE;
} else {
/* 检查缓冲区空间 */
#ifndef __TEST_MODE__
/* 发送数据 */
+ AI_LOGI("Sending %lu bytes (text) to service %s (websocket=%d)",
+ (unsigned long)length, g_service_names[service_type], conn->websocket);
int ret = WebSocketClientSendText(conn->websocket, (const char*)conn->send_buffer->buffer, length);
if (ret < 0) {
- AI_LOGE("Failed to send data to service %s", g_service_names[service_type]);
+ AI_LOGE("Failed to send text data to service %s: WebSocket error=%d",
+ g_service_names[service_type], ret);
result = AI_ERROR_WEBSOCKET;
} else {
/* 更新统计 */
/* 生成统一会话ID */
ai_services_generate_session_id(context->current_session_id, sizeof(context->current_session_id));
+ // 检查并打印录音和播放服务器状态
+ uint8_t record_status = ai_media_get_record_status();
+ AI_LOGI("ai_media_get_record_status: %d", record_status);
+
+ uint8_t play_status = ai_media_get_play_status();
+ AI_LOGI("ai_media_get_play_status: %d", play_status);
+
context->last_activity_time = get_current_timestamp();
context->is_initialized = true;
--- /dev/null
+#include "http_client.h"
+
+
+#include <sys/time.h>
+
+#include "custom_utils.h"
+#include "custom_runtime.h"
+#include "bleANCS.h"
+#include "custom_utils.h"
+#include "cmsis_os2.h"
+#include "wearable_log.h"
+#include "dev_authentication.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define HTTP_JSON_SERVER_NAME "app-api.raisound.com"//"test.app.api.thisiot.com"//获取升级服务器
+#define PORT (80)// (80)http端口号 (443)//https端口号
+#define PUT_URL "/dev/auth/active"//激活接口
+#define POST_URL "/dev/auth/sign_in"//登录接口
+
+
+osSemaphoreId_t put_done_sem;
+
+static uint32_t put_srv_res=0;
+
+
+osSemaphoreId_t post_done_sem;
+
+static uint32_t post_srv_res=0;
+
+static httpc_connection_t settings = {0};
+
+
+ static err_t httpc_download_headers_done_ui(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "httpc_download_headers_done_ui hdrlen %d, file len %d\r\n",hdr_len,content_len);
+ // file_total_len = content_len;
+ return 0;
+ }
+ static err_t httpc_download_file_recv_ui(httpc_state_t *connection, u32_t rx_len,u32_t rx_total_len)
+ {
+ // WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "httpc_download_file_recv_ui recv %d bytes total received %d filelen %d\r\n",rx_len,rx_total_len,file_total_len);
+ return 0;
+ }
+
+ static void httpc_download_result_data_put(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "httpc_download_result_ui");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"httpc_download_result , httpc_result_t %d, rx_content_len %d , srv_res %d, err %d\n",
+ httpc_result,rx_content_len,srv_res,err);
+ // if(srv_res==200)
+ {
+ put_srv_res=srv_res;
+
+ (void)osSemaphoreRelease(put_done_sem);
+ //激活成功
+ }
+ return ;
+ }
+ // {"success":true,"msg":"登录成功","code":20001,"data":{"deviceId":"28110601009b","token":"95fc6dcdf5a3e9a48df535453efd097c19"}}
+
+ static void httpc_download_result_data_post(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "httpc_download_result_ui");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"httpc_download_result , httpc_result_t %d, rx_content_len %d , srv_res %d, err %d\n",
+ httpc_result,rx_content_len,srv_res,err);
+ // if(srv_res==200)
+ {
+ post_srv_res=srv_res;
+
+ (void)osSemaphoreRelease(post_done_sem);
+
+ //登录成功
+ }
+ return ;
+ }
+
+
+
+uint32_t put_data_by_get_file(char *data,uint16_t data_len)
+{
+
+ memset_s(&settings,sizeof(settings),0,sizeof(settings));
+ settings.use_proxy=0;
+
+ settings.use_post=1;
+ settings.args=(u8_t*)data;
+ settings.arglen=data_len;
+ uint8_t *otadatapath =(uint8_t *)AUTH_ACTIVE;
+
+ settings.result_fn=httpc_download_result_data_put;
+ settings.file_rx_fn = httpc_download_file_recv_ui;
+ settings.headers_done_fn = httpc_download_headers_done_ui;
+
+ httpc_state_t* connection=NULL;
+ err_t rett= httpc_get_file_dns_to_disk(
+ HTTP_JSON_SERVER_NAME,
+ PORT,
+ PUT_URL,
+ &settings,
+ NULL,
+ (const char*) otadatapath,
+ &connection);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"custom_start_ota_download %d \r\n",rett);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "use_put");
+
+ if(rett==0)
+ {
+ put_done_sem = osSemaphoreNew(1, 0, NULL);
+
+ osSemaphoreAcquire(put_done_sem, 1000*30);
+
+ osSemaphoreDelete(put_done_sem);
+ return put_srv_res;
+ }
+ else
+ {
+ return rett;
+ }
+}
+
+
+uint32_t post_data_by_get_file(char *data,uint16_t data_len)
+{
+ memset_s(&settings,sizeof(settings),0,sizeof(settings));
+ settings.use_proxy=0;
+
+ settings.use_post=1;
+ settings.args=(u8_t*)data;
+ settings.arglen=data_len;
+ uint8_t *otadatapath =(uint8_t *)AUTH_SIGN_IN;
+
+ settings.result_fn=httpc_download_result_data_post;
+ settings.file_rx_fn = httpc_download_file_recv_ui;
+ settings.headers_done_fn = httpc_download_headers_done_ui;
+
+ httpc_state_t* connection=NULL;
+ err_t rett= httpc_get_file_dns_to_disk(
+ HTTP_JSON_SERVER_NAME,
+ PORT,
+ POST_URL,
+ &settings,
+ NULL,
+ (const char*) otadatapath,
+ &connection);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"custom_start_ota_download %d \r\n",rett);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "use_post");
+
+ if(rett==0)
+ {
+ post_done_sem = osSemaphoreNew(1, 0, NULL);
+
+ osSemaphoreAcquire(post_done_sem, 1000*30);
+
+ osSemaphoreDelete(post_done_sem);
+
+ return post_srv_res;
+ }
+ else
+ {
+ return rett;
+ }
+}
+
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
\ No newline at end of file
--- /dev/null
+#===============================================================================
+# @brief cmake file
+# Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved.
+#===============================================================================
+
+set(COMPONENT_NAME "zhihui_ai_old")
+
+set(PUBLIC_HEADER
+${CMAKE_CURRENT_SOURCE_DIR}
+)
+set(PRIVATE_HEADER
+${ROOT_DIR}/open_source/openharmony/third_party/cJSON
+${ROOT_DIR}/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include
+${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include
+${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include
+)
+
+set(SOURCES
+${CMAKE_CURRENT_SOURCE_DIR}/ai_old_at_cmd.cpp
+${CMAKE_CURRENT_SOURCE_DIR}/zhihui_ws.cpp
+${CMAKE_CURRENT_SOURCE_DIR}/zhihui_ai_server.cpp
+${CMAKE_CURRENT_SOURCE_DIR}/asr_websocket_server.cpp
+${CMAKE_CURRENT_SOURCE_DIR}/tts_websocket_server.cpp
+${CMAKE_CURRENT_SOURCE_DIR}/chat_websocket_server.cpp
+)
+
+set(COMPONENT_PUBLIC_CCFLAGS
+)
+
+set(COMPONENT_CCFLAGS
+ -std=c++11
+ -Wno-error
+ -Wno-unused-parameter
+ -fno-exceptions
+ -fno-unwind-tables
+ -fno-asynchronous-unwind-tables
+ -Wno-undef
+ -Wno-bitwise-op-parentheses
+ -Wno-shift-op-parentheses
+ -nostdlibinc
+ -nostdinc
+ -nostdlib
+ -Wno-c++11-narrowing
+ -Wno-error=unused-function
+)
+
+set(WHOLE_LINK
+ true
+)
+
+set(MAIN_COMPONENT
+ false
+)
+
+set(PUBLIC_DEFINES
+SUPPORT_ZHIHUI_AI_OLD
+)
+
+set(PRIVATE_DEFINES
+)
+
+build_component()
--- /dev/null
+
+#include "at_cmd_api.h"
+#include "pm_control.h"
+#include "cat1_server.h"
+#include "ai_old_at_cmd.h"
+#include "cat1_ctl.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+// #include "phoneaddressmodel.h" // Commented out due to missing header
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+#define PPG_MAX_ARGC 8
+#define PPG_MAX_ARGV_LEN 64
+
+#define PPG_SUCCESS 0
+#define PPG_FAILED (-1)
+
+#include "app_at_process.h"
+// #include "cat1_ws.h" // Commented out due to missing header
+#include "zhihui_ws.h"
+#include "websocket_test.h"
+#include "cmsis_os2.h"
+
+static int ConvertCmdToParam(uint8_t *para, uint32_t paraLen,
+ uint8_t argv[PPG_MAX_ARGC][PPG_MAX_ARGV_LEN], uint32_t* argc)
+{
+ uint32_t pos = 0;
+ *argc = 0;
+
+ for (uint32_t i = 0; i < paraLen; i++) {
+ if ((para[i] == ' ' || para[i] == '\n') && pos != 0) {
+ argv[*argc][pos] = '\0';
+ *argc += 1;
+ pos = 0;
+ if (*argc >= PPG_MAX_ARGC) {
+ return PPG_FAILED;
+ }
+ } else {
+ argv[*argc][pos] = para[i];
+ pos++;
+ if (pos >= PPG_MAX_ARGV_LEN) {
+ return PPG_FAILED;
+ }
+ }
+ }
+ if (pos != 0) {
+ *argc += 1;
+ }
+
+ return PPG_SUCCESS;
+}
+
+#include "audio_manager_c_wrapper.h"
+#include "cat1_tcpServer.h"
+#include "audio_capture_wrapper.h"
+#include "dev_authentication.h"
+
+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 uapi_start_tts(void);
+extern void uapi_start_tts_single();
+extern void uapi_UI_start_asr(uint8_t type);
+extern void read_file_put(void);
+extern bool deinit_ai_task(void);
+
+// 定义缺失的全局变量和函数
+int g_test_server_index = 0;
+
+// 缺失函数的空实现
+void start_send_voice(void) { printf("start_send_voice: not implemented\n"); }
+void uapi_UI_start_asr(uint8_t type) { printf("uapi_UI_start_asr: not implemented\n"); }
+void uapi_start_tts_single(void) { printf("uapi_start_tts_single: not implemented\n"); }
+int get_websocket_test_state(void) { printf("get_websocket_test_state: not implemented\n"); return 0; }
+int at_test_websocket_handler(const char *cmd, const char *params) { printf("at_test_websocket_handler: not implemented\n"); return 0; }
+void stop_websocket_test(void) { printf("stop_websocket_test: not implemented\n"); }
+void get_websocket_test_stats(uint32_t *send_count, uint32_t *recv_count, uint32_t *lost_count) {
+ printf("get_websocket_test_stats: not implemented\n");
+ if(send_count) *send_count = 0;
+ if(recv_count) *recv_count = 0;
+ if(lost_count) *lost_count = 0;
+}
+void startAudioCaptureTask(void) { printf("startAudioCaptureTask: not implemented\n"); }
+void set_AIStreamend(uint8_t state) { printf("set_AIStreamend: not implemented\n"); }
+
+static uint32_t wstest(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;
+
+ int ret = ConvertCmdToParam(para, paraLen, argv, &argc);
+ if (ret != PPG_SUCCESS) {
+ printf("CAT1TEST ATCMD Convert Command Failed\n");
+ return ERRCODE_FAIL;
+ }
+
+ // g_test_server_index already defined above
+ if( argc == 1)
+ {
+ g_test_server_index = atoi((char*)argv[0]);
+ }
+ printf("AT^WSTEST g_test_server_index %d\n",g_test_server_index);
+ init_ai_task(AI_TYPE_JIU);
+ return 0;
+}
+
+static uint32_t ai_msg(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;
+ }
+ ai_chat_send_txt(0,(char *)argv[0]);
+ return 0;
+}
+
+static uint32_t start_record(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();
+ streamingRecordAndSend_AI(0);
+ return 0;
+}
+
+static uint32_t start_asr(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;
+ }
+ start_send_voice();
+ return 0;
+}
+
+static uint32_t sendbindata(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_cat1_keep_wakeup(1); // Function not available, commented out
+ uapi_UI_start_asr(AI_TYPE_JIU);
+ return 0;
+}
+
+static uint32_t sendfilebindata(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_cat1_keep_wakeup(1); // Function not available, commented out
+ return 0;
+}
+
+static uint32_t active(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;
+ }
+ aut_active();
+ return 0;
+}
+
+static uint32_t readfileput(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;
+ }
+ read_file_put();
+ return 0;
+}
+
+static uint32_t autlogin(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;
+ }
+ pre_AI_init();
+ return 0;
+}
+
+static uint32_t stopwstest(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;
+ }
+ deinit_ai_task();
+ return 0;
+}
+
+static uint32_t starttts(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_tts();
+ return 0;
+}
+
+static uint32_t sendttstxt(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_tts_single();
+ return 0;
+}
+
+static uint32_t sendfotaurl(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;
+ }
+ return 0;
+}
+
+static uint32_t test_websocket(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(strCommonCmd);
+ char params[256] = {0};
+ if (paraLen > 0 && paraLen < sizeof(params)) {
+ memcpy(params, para, paraLen);
+ params[paraLen] = '\0';
+ }
+ printf("WebSocket测试命令: AT^TESTWEBSOCKET=%s\n", params);
+ int current_state = get_websocket_test_state();
+ printf("当前测试状态: %d\n", current_state);
+ if (current_state != 1)
+ {
+ int result = at_test_websocket_handler("AT^TESTWEBSOCKET", params);
+ if (result == 0) {
+ printf("WebSocket测试启动成功\n");
+ } else {
+ printf("WebSocket测试启动失败,错误码: %d\n", result);
+ }
+ }else{
+ printf("WebSocket测试已经运行\n");
+ }
+ return ERRCODE_SUCC;
+}
+
+static uint32_t stop_websocket_test_cmd(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(strCommonCmd);
+ UNUSED(para);
+ UNUSED(paraLen);
+ printf("WebSocket停止测试命令: AT^STOPWEBSOCKET\n");
+ int current_state = get_websocket_test_state();
+ printf("停止前测试状态: %d\n", current_state);
+ stop_websocket_test();
+ printf("停止后测试状态: %d\n", get_websocket_test_state());
+ printf("WebSocket测试已停止\n");
+ return ERRCODE_SUCC;
+}
+
+static uint32_t websocket_status(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ UNUSED(strCommonCmd);
+ UNUSED(para);
+ UNUSED(paraLen);
+ printf("WebSocket状态查询命令: AT^WEBSOCKETSTATUS\n");
+ int state = get_websocket_test_state();
+ uint32_t send_count, recv_count, lost_count;
+ get_websocket_test_stats(&send_count, &recv_count, &lost_count);
+ const char *state_str[] = {"空闲", "运行中", "已完成", "错误"};
+ printf("测试状态: %s\n", state_str[state]);
+ printf("发送数据包: %u\n", send_count);
+ printf("接收数据包: %u\n", recv_count);
+ printf("丢失数据包: %u\n", lost_count);
+ return ERRCODE_SUCC;
+}
+
+static const at_cmd_table_t g_ai_old_cmd[] = {
+ { (char *)"AT^WSTEST", wstest, (char *)"1" },
+ { (char *)"AT^AI_MSG", ai_msg, (char *)"1" },
+ { (char *)"AT^AI_ASR", start_record, (char *)"1" },
+ { (char *)"AT^STARTASR", start_asr, (char *)"1" },
+ { (char *)"AT^SENDBINDATA", sendbindata, (char *)"1" },
+ { (char *)"AT^SENDFILEDATA", sendfilebindata, (char *)"1" },
+ { (char *)"AT^ACTIVE", active, (char *)"1" },
+ { (char *)"AT^READPUT", readfileput, (char *)"1" },
+ { (char *)"AT^LOGIN", autlogin, (char *)"1" },
+ { (char *)"AT^STARTTTS", starttts, (char *)"1" },
+ { (char *)"AT^SENDTTTS", sendttstxt, (char *)"1" },
+ { (char *)"AT^STOPTEST", stopwstest, (char *)"1" },
+ { (char *)"AT^OTAHTTP", sendfotaurl, (char *)"1" },
+ { (char *)"AT^TESTWEBSOCKET", test_websocket, (char *)"1" },
+ { (char *)"AT^STOPWEBSOCKET", stop_websocket_test_cmd, (char *)"1" },
+ { (char *)"AT^WEBSOCKETSTATUS", websocket_status, (char *)"1" },
+};
+
+at_cmd_table_t *get_ai_old_cmd_table(uint32_t *cmd_count)
+{
+ *cmd_count = ARRAY_COUNT(g_ai_old_cmd);
+ return (at_cmd_table_t *)g_ai_old_cmd;
+}
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. \n
+ *
+ * Description: Provides at cmd register func \n
+ * Author: @CompanyNameTag \n
+ * History: \n
+ * 2022-07-20, Create file. \n
+ */
+#ifndef AI_OLD_AT_CMD_H
+#define AI_OLD_AT_CMD_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include "errcode.h"
+#include "soc_osal.h"
+#include "at_cmd_api.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+at_cmd_table_t *get_cat1_cmd_table(uint32_t *cmd_count);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif
--- /dev/null
+/**
+ * @file asr_websocket_server.cpp
+ * @brief ASR WebSocket服务器实现
+ * @version 1.0.0
+ * @date 2024-09-05
+ *
+ * 从 zhihui_ai_server.cpp 中提取的 ASR 相关功能
+ */
+
+#include "asr_websocket_server.h"
+#include "zhihui_ws.h"
+#include "bk_websocket.h"
+#include "ws_queue_common.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "wearable_log.h"
+#include "custom_runtime.h"
+#include "cmsis_os2.h"
+#include "osal_msgqueue.h"
+#include "thread_init.h"
+#include "soc_osal.h"
+#include "common_def.h"
+#include "los_printf.h"
+#include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ASR 相关全局变量 */
+char *asr_recv_buf = NULL;
+WebSocketClient *asr_client = NULL;
+static ws_message_queue_t asr_msg_queue = {0};
+static uint8_t asr_need_stop = 0;
+static uint8_t asr_is_stop = 0;
+static uint8_t is_bk_websocket_asr = 0;
+static uint8_t ai_asr_service_init_flag = 0;
+static uint8_t asr_is_ready_start = 0;
+static uint8_t asr_client_ping = 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
+extern void startAudioCaptureTask(void);
+extern bool stop_AudioCaptureProcess_AI(void);
+extern void ble_set_ai_Question_str(char *str);
+extern int send_msg_data_into_Ai_msg_queue(void *data, uint16_t len, uint16_t type);
+extern void msleep(uint32_t ms);
+
+/* ASR 配置和常量 */
+#define AI_RESULT_STR_LEN 512
+#define AI_MAX_RETRY_COUNT 3
+#define AI_WS_TCP_CHECK_INTERVAL_MS 100
+#define AI_WS_HANDSHAKE_TIMEOUT_MS 10000
+#define AI_RECEIVE_TIMEOUT_MS 30000
+
+static char result_str_asr[AI_RESULT_STR_LEN] = {0};
+static uint8_t type_ai = 0;
+
+/**
+ * @brief ASR WebSocket数据包回调函数
+ */
+void asr_on_ws_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type)
+{
+ if(asr_client != NULL && asr_client->msg_queue != NULL)
+ {
+ ws_queue_put(asr_client->msg_queue, data, datalen, type);
+ }
+}
+
+/**
+ * @brief 获取ASR服务初始化标志
+ */
+uint8_t get_ai_asr_service_init_flag(void)
+{
+ return ai_asr_service_init_flag;
+}
+
+/**
+ * @brief 设置ASR服务初始化标志
+ */
+void set_ai_asr_service_init_flag(uint8_t flag)
+{
+ ai_asr_service_init_flag = flag;
+}
+
+/**
+ * @brief 获取ASR准备开始标志
+ */
+uint8_t get_asr_is_ready_start(void)
+{
+ return asr_is_ready_start;
+}
+
+/**
+ * @brief ASR URL初始化
+ */
+bool asr_ws_url_init(char * put_buf)
+{
+ char token[64] = {0};
+ if(read_dev_token(token))
+ {
+ sprintf(put_buf, "ws://amp.raisound.com/asr/ws?dt=%s&platform=aliyun&noCache=1", token);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/**
+ * @brief ASR结果分析
+ */
+bool asr_result_analysis(char *buf)
+{
+ // 这里可以添加ASR结果解析逻辑
+ if (buf && strlen(buf) > 0) {
+ strncpy(result_str_asr, buf, AI_RESULT_STR_LEN - 1);
+ result_str_asr[AI_RESULT_STR_LEN - 1] = '\0';
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief 启动ASR服务
+ */
+void uapi_start_asr(void)
+{
+ startAudioCaptureTask();
+ asr_is_ready_start = 1;
+}
+
+/**
+ * @brief 停止ASR服务
+ */
+void uapi_UI_stop_asr(void)
+{
+ if(strlen(result_str_asr))
+ {
+ // 发送ASR结果到聊天服务
+ extern bool ai_chat_send_txt(uint8_t type, char *msg);
+ ai_chat_send_txt(0, result_str_asr);
+ ble_set_ai_Question_str(result_str_asr);
+
+ memset(result_str_asr, 0, AI_RESULT_STR_LEN);
+ }
+
+ // 启动TTS服务
+ extern bool uapi_start_tts(void);
+ uapi_start_tts();
+
+ if(stop_AudioCaptureProcess_AI())
+ {
+ asr_is_stop = 1;
+ }
+}
+
+/**
+ * @brief 启动ASR任务
+ */
+void uapi_start_asr_task(void)
+{
+ // ASR任务启动逻辑
+ asr_is_ready_start = 1;
+}
+
+/**
+ * @brief ASR录音测试
+ */
+void asr_record_test(void)
+{
+ // ASR录音测试逻辑
+ printf("ASR record test started\n");
+}
+
+/**
+ * @brief ASR WebSocket主线程
+ */
+void bk_websocket_asr(void *data)
+{
+ uint32_t start_time = (uint32_t)osKernelGetTickCount();
+ uint32_t retry_count = 0;
+
+ if (asr_msg_queue.initialized) {
+ ws_queue_deinit(&asr_msg_queue);
+ }
+ ws_queue_init(&asr_msg_queue, 0);
+
+ASR_START:
+ // uapi_cat1_keep_wakeup(1); // Commented out due to missing implementation
+ ws_queue_cleanup(&asr_msg_queue);
+ WebSocketMessageType type;
+ bool state = true;
+
+ is_bk_websocket_asr = 1;
+
+ uint8_t is_raisound_url = 0;
+
+ {
+ char url_buf[124] = {0};
+
+ if (asr_ws_url_init(url_buf)) {
+ printf("ASR URL: %s\n", url_buf);
+ is_raisound_url = 1;
+ } else {
+ is_raisound_url = 0;
+ }
+
+ uint8_t client_count = AI_MAX_RETRY_COUNT;
+
+ while (client_count > 0 && asr_need_stop == 0) {
+ uint32_t connect_start = (uint32_t)osKernelGetTickCount();
+
+ // 根据测试服务器索引选择URL
+ extern uint8_t g_test_server_index;
+ if (g_test_server_index == 1) {
+ asr_client = init_websocket_client("ws://118.145.158.207:22080/asr/ws", NULL, 0);
+ if (asr_client != NULL) {
+ asr_client->msg_queue = &asr_msg_queue;
+ ws_set_packet_callback(asr_client, asr_on_ws_packet_callback);
+ }
+ } else if (g_test_server_index == 2) {
+ asr_client = init_websocket_client("ws://110.42.224.129:8910/asr/ws", NULL, 0);
+ if (asr_client != NULL) {
+ asr_client->msg_queue = &asr_msg_queue;
+ ws_set_packet_callback(asr_client, asr_on_ws_packet_callback);
+ }
+ } else if (is_raisound_url == 1) {
+ asr_client = init_websocket_client(url_buf, NULL, 1);
+ if (asr_client != NULL) {
+ asr_client->msg_queue = &asr_msg_queue;
+ ws_set_packet_callback(asr_client, asr_on_ws_packet_callback);
+ }
+ } else {
+ asr_client = init_websocket_client("ws://118.145.158.207:22080/asr/ws", NULL, 0);
+ if (asr_client != NULL) {
+ asr_client->msg_queue = &asr_msg_queue;
+ ws_set_packet_callback(asr_client, asr_on_ws_packet_callback);
+ }
+ }
+
+ if (asr_client == NULL) {
+ printf("ASR WebSocket client creation failed, retrying... (%d/%d)\n",
+ AI_MAX_RETRY_COUNT - client_count + 1, AI_MAX_RETRY_COUNT);
+ client_count--;
+ msleep(2000);
+ continue;
+ }
+
+ printf("ASR WebSocket client created successfully\n");
+ break;
+ }
+
+ if (asr_client == NULL) {
+ printf("ASR WebSocket: Failed to create client after %d retries\n", AI_MAX_RETRY_COUNT);
+ goto asr_cleanup_and_exit;
+ }
+
+ printf("ASR WebSocket: 连接建立成功,开始握手等待\n");
+
+ // 等待握手完成
+ uint32_t asr_handshake_start_time = (uint32_t)osKernelGetTickCount();
+ const uint32_t asr_handshake_timeout_ms = AI_WS_HANDSHAKE_TIMEOUT_MS;
+
+ while (asr_client->handshake == 0) {
+ if ((uint32_t)osKernelGetTickCount() - asr_handshake_start_time > asr_handshake_timeout_ms) {
+ printf("ASR WebSocket握手超时 (%d ms),重新连接\n", asr_handshake_timeout_ms);
+ close_websocket_client(asr_client);
+ asr_client = NULL;
+ goto asr_cleanup_and_exit;
+ }
+
+ msleep(1000);
+ printf("等待ASR WebSocket握手完成... (%d ms)\n",
+ (uint32_t)osKernelGetTickCount() - asr_handshake_start_time);
+ }
+
+ printf("ASR WebSocket握手完成,耗时 %d ms\n",
+ (uint32_t)osKernelGetTickCount() - asr_handshake_start_time);
+ }
+
+ // uapi_cat1_keep_wakeup(1); // Commented out due to missing implementation
+
+ // 声明变量在goto标签之前
+ WebSocketMessageType msg_type;
+ uint32_t last_activity;
+
+ // 分配ASR接收缓冲区
+ asr_recv_buf = (char *)malloc(0x400);
+ if (!asr_recv_buf) {
+ printf("Failed to allocate ASR receive buffer\n");
+ goto asr_cleanup_and_exit;
+ }
+
+ set_ai_asr_service_init_flag(1);
+
+ printf("ASR client initialized successfully\n");
+
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ while (asr_need_stop == 0) {
+ // 检查超时
+ if ((uint32_t)osKernelGetTickCount() - last_activity > AI_RECEIVE_TIMEOUT_MS) {
+ printf("ASR receive timeout, reconnecting...\n");
+ break;
+ }
+
+ memset(asr_recv_buf, 0, 0x400);
+ 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);
+ int len = receive_websocket_message_from_queue(&asr_msg_queue, (unsigned char *)asr_recv_buf,
+ 0x400, &msg_type, AI_RECEIVE_TIMEOUT_MS);
+
+ if (len < 0x400) {
+ asr_recv_buf[len] = '\0';
+ }
+
+ if (len == -1 || msg_type == WS_CLOSE) {
+ printf("ASR WebSocket connection closed\n");
+ break;
+ }
+
+ if (len > 0) {
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ if (msg_type == WS_TEXT) {
+ printf("ASR text data received: %s\n", asr_recv_buf);
+ asr_result_analysis(asr_recv_buf);
+ } else if (msg_type == WS_BINARY) {
+ printf("ASR binary data received: %d bytes\n", len);
+ send_msg_data_into_Ai_msg_queue((uint8_t *)asr_recv_buf, len, 100);
+ }
+ }
+
+ msleep(10);
+ }
+
+asr_cleanup_and_exit:
+ printf("ASR WebSocket cleanup and exit\n");
+
+ if (asr_recv_buf) {
+ free(asr_recv_buf);
+ asr_recv_buf = NULL;
+ }
+
+ if (asr_client) {
+ close_websocket_client(asr_client);
+ asr_client = NULL;
+ }
+
+ ws_queue_deinit(&asr_msg_queue);
+ is_bk_websocket_asr = 0;
+ set_ai_asr_service_init_flag(0);
+
+ printf("ASR WebSocket thread ended\n");
+}
+
+/**
+ * @brief ASR服务器初始化
+ */
+errcode_t asr_server_init(void)
+{
+ app_task_definition_t asr_taskcfg;
+
+ osThreadState_t osThreadGetState_t = osThreadGetState(asr_taskcfg.task_handle);
+ const char *buf = osThreadGetName(asr_taskcfg.task_handle);
+
+ printf("ASR osThreadGetState_t %d %s!\r\n", osThreadGetState_t, buf);
+
+ if(strcmp("asr_server_init", buf) == 0)
+ {
+ if(osThreadGetState_t != osThreadReady && osThreadGetState_t != osThreadError &&
+ osThreadGetState_t != osThreadInactive)
+ {
+ printf("ASR osThreadGetState is %d!\r\n", osThreadGetState_t);
+ return ERRCODE_FAIL;
+ }
+ }
+
+ if(is_bk_websocket_asr || asr_client != NULL)
+ {
+ printf("ASR server is already connected!\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ printf("asr_server_init!\r\n");
+
+ asr_taskcfg.create_queue_handle = NULL;
+ asr_taskcfg.attr.name = "asr_server_init";
+ asr_taskcfg.attr.priority = (osPriority_t)TASK_PRIORITY_APP;
+ asr_taskcfg.attr.stack_size = 0x1000;
+ asr_taskcfg.func = bk_websocket_asr;
+ asr_taskcfg.use_mem = USE_EXTEND_MEM;
+
+ extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg);
+ errcode_t ret = uapi_create_osThread_custom(&asr_taskcfg);
+
+ if(ret != ERRCODE_SUCC)
+ {
+ printf("create ASR service thread failed %d!\r\n", ret);
+ return ERRCODE_FAIL;
+ }
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief ASR服务器反初始化
+ */
+void asr_server_deinit(void)
+{
+ printf("asr_server_deinit\n");
+
+ // 发送关闭消息
+ if (asr_client) {
+ if(send_websocket_message(asr_client, (uint8_t *)"WS_CLOSE", strlen("WS_CLOSE"), WS_CLOSE))
+ {
+ printf("ASR WS_CLOSE sent\n");
+ }
+ }
+
+ asr_need_stop = 1;
+
+ if (asr_client) {
+ close_websocket_client(asr_client);
+ asr_client = NULL;
+ }
+
+ is_bk_websocket_asr = 0;
+ set_ai_asr_service_init_flag(0);
+}
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
--- /dev/null
+/**
+ * @file asr_websocket_server.h
+ * @brief ASR WebSocket服务器头文件
+ * @version 1.0.0
+ * @date 2024-09-05
+ */
+
+#ifndef __ASR_WEBSOCKET_SERVER_H__
+#define __ASR_WEBSOCKET_SERVER_H__
+
+#include "bk_websocket.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include "errcode.h"
+#include "thread_init.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ASR 服务相关类型定义 - errcode_t is defined in errcode.h */
+
+/* 任务配置结构体 - 使用系统定义的 app_task_definition_t */
+
+/* 常量定义 */
+#define TASK_PRIORITY_APP 24
+#define USE_EXTEND_MEM 1
+
+/* ASR 服务接口函数 */
+
+/**
+ * @brief ASR WebSocket数据包回调函数
+ * @param data 数据指针
+ * @param datalen 数据长度
+ * @param type WebSocket消息类型
+ */
+void asr_on_ws_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type);
+
+/**
+ * @brief 获取ASR服务初始化标志
+ * @return 初始化标志
+ */
+uint8_t get_ai_asr_service_init_flag(void);
+
+/**
+ * @brief 设置ASR服务初始化标志
+ * @param flag 标志值
+ */
+void set_ai_asr_service_init_flag(uint8_t flag);
+
+/**
+ * @brief 获取ASR准备开始标志
+ * @return 准备开始标志
+ */
+uint8_t get_asr_is_ready_start(void);
+
+/**
+ * @brief ASR URL初始化
+ * @param put_buf 输出缓冲区
+ * @return 成功返回true,失败返回false
+ */
+bool asr_ws_url_init(char * put_buf);
+
+/**
+ * @brief ASR结果分析
+ * @param buf 结果字符串
+ * @return 成功返回true,失败返回false
+ */
+bool asr_result_analysis(char *buf);
+
+/**
+ * @brief 启动ASR服务
+ */
+void uapi_start_asr(void);
+
+/**
+ * @brief 停止ASR服务
+ */
+void uapi_UI_stop_asr(void);
+
+/**
+ * @brief 启动ASR任务
+ */
+void uapi_start_asr_task(void);
+
+/**
+ * @brief ASR录音测试
+ */
+void asr_record_test(void);
+
+/**
+ * @brief ASR WebSocket主线程
+ * @param data 线程参数
+ */
+void bk_websocket_asr(void *data);
+
+/**
+ * @brief ASR服务器初始化
+ * @return 成功返回ERRCODE_SUCC,失败返回ERRCODE_FAIL
+ */
+errcode_t asr_server_init(void);
+
+/**
+ * @brief ASR服务器反初始化
+ */
+void asr_server_deinit(void);
+
+/* 全局变量声明 */
+extern char *asr_recv_buf;
+extern WebSocketClient *asr_client;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ASR_WEBSOCKET_SERVER_H__ */
\ No newline at end of file
--- /dev/null
+
+#ifndef __BK_WEBSOCKET_H__
+#define __BK_WEBSOCKET_H__
+
+// 包含新的WebSocket解析器接口
+#include "websocket_parser_wrapper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+// 使用websocket_parser_wrapper.h中的枚举值,避免重复定义
+// 为保持向后兼容性,创建类型别名
+typedef ws_opcode_t WebSocketMessageType;
+
+// 定义向后兼容的常量(如果需要的话)
+#define WS_CONT WS_CONTINUATION
+
+/* WebSocket数据包回调函数类型 */
+typedef void (*on_ws_packet_callback)(uint8_t* data, size_t datalen, WebSocketMessageType type);
+
+/* WebSocket消息队列系统 */
+#define WS_QUEUE_SIZE 128
+#define WS_MESSAGE_MAX_SIZE 1024
+
+typedef struct {
+ uint8_t *data;//data[WS_MESSAGE_MAX_SIZE];
+ uint16_t length;
+ WebSocketMessageType type;
+ uint32_t timestamp;
+ bool in_use;
+} ws_message_item_t;
+
+typedef struct {
+#ifdef SUPPORT_WEBSOCKET_TEST
+ // ws_message_item_t messages[WS_QUEUE_SIZE];
+ // uint8_t head;
+ // uint8_t tail;
+ // uint8_t count;
+ // osMutexId_t mutex;
+ // osSemaphoreId_t semaphore;
+#endif
+ unsigned long queue_id; // osal消息队列ID
+ bool initialized;
+} ws_message_queue_t;
+
+// WebSocket 客户端结构体
+typedef struct {
+ void* tcp_handle;
+ ws_message_queue_t *msg_queue;
+ char host[128];
+ char path[128];
+ char query[128];
+ int port;
+
+ int handshake;
+
+ /* WebSocket解析器 */
+ ws_parser_t* parser;
+ int sockfd; /* socket文件描述符 */
+
+ /* 数据包回调函数 */
+ on_ws_packet_callback packet_callback;
+} WebSocketClient;
+
+// 初始化 WebSocket 客户端
+WebSocketClient *init_websocket_client(const char *url, const char *headers,uint8_t use_dns);
+
+// 设置WebSocket数据包回调函数
+void ws_set_packet_callback(WebSocketClient *client, on_ws_packet_callback callback);
+
+// 处理接收到的原始数据
+int ws_process_received_data(WebSocketClient *client, const uint8_t *data, size_t len);
+
+// 关闭 WebSocket 客户端
+void close_websocket_client(WebSocketClient *client) ;
+
+// 接收 WebSocket 消息
+int receive_websocket_message(WebSocketClient *client, unsigned char *buf,int len,WebSocketMessageType *code);
+int send_websocket_message(WebSocketClient *client, uint8_t *message, size_t message_len, WebSocketMessageType type) ;
+
+// WebSocket测试客户端(当SUPPORT_WEBSOCKET_TEST定义时可用)
+#ifdef SUPPORT_WEBSOCKET_TEST
+extern WebSocketClient *test_client;
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+// 发送 WebSocket 消息
+// int send_websocket_message(WebSocketClient *client, uint8_t *message, size_t message_len, WebSocketMessageType type) ;
+
+#endif
--- /dev/null
+/**
+ * @file chat_websocket_server.cpp
+ * @brief Chat WebSocket服务器实现
+ * @version 1.0.0
+ * @date 2024-09-05
+ *
+ * 从 zhihui_ai_server.cpp 中提取的 Chat 相关功能
+ */
+
+#include "chat_websocket_server.h"
+#include "zhihui_ws.h"
+#include "bk_websocket.h"
+#include "ws_queue_common.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "wearable_log.h"
+#include "custom_runtime.h"
+#include "cmsis_os2.h"
+#include "osal_msgqueue.h"
+#include "thread_init.h"
+#include "soc_osal.h"
+#include "common_def.h"
+#include "los_printf.h"
+#include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Chat 相关全局变量 */
+WebSocketClient *chat_client = NULL;
+static ws_message_queue_t chat_msg_queue = {0};
+static uint8_t chat_reconnect = 0;
+static uint8_t is_bk_websocket_chat = 0;
+static uint8_t ai_chat_service_init_flag = 0;
+static uint8_t chat_client_ping = 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
+extern void ble_set_ai_Answer_str(char *str, uint8_t type);
+extern void msleep(uint32_t ms);
+
+/* Chat 配置和常量 */
+#define AI_MAX_RETRY_COUNT 3
+#define AI_WS_HANDSHAKE_TIMEOUT_MS 10000
+#define AI_RECEIVE_TIMEOUT_MS 30000
+
+static uint8_t type_ai = 0;
+static char recv_buf[1024] = {0};
+
+/**
+ * @brief Chat WebSocket数据包回调函数
+ */
+void chat_on_ws_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type)
+{
+ if(chat_client != NULL && chat_client->msg_queue != NULL)
+ {
+ ws_queue_put(chat_client->msg_queue, data, datalen, type);
+ }
+}
+
+/**
+ * @brief 获取Chat服务初始化标志
+ */
+uint8_t get_ai_chat_service_init_flag(void)
+{
+ return ai_chat_service_init_flag;
+}
+
+/**
+ * @brief 设置Chat服务初始化标志
+ */
+void set_ai_chat_service_init_flag(uint8_t flag)
+{
+ ai_chat_service_init_flag = flag;
+}
+
+/**
+ * @brief Chat结果分析
+ */
+bool chat_result_analysis(char *buf)
+{
+ 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);
+
+ cJSON* root = cJSON_Parse((const char *)buf);
+ if (root == NULL) {
+ printf("Chat result parse failed\n");
+ return false;
+ }
+
+ char *type_str = NULL;
+ char *content_str = NULL;
+
+ cJSON* type = cJSON_GetObjectItem(root, "type");
+ if (type && cJSON_IsString(type)) {
+ type_str = type->valuestring;
+ }
+
+ cJSON* content = cJSON_GetObjectItem(root, "content");
+ if (content && cJSON_IsString(content)) {
+ 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);
+ }
+ }
+
+ if(strcmp("end", type_str) == 0)
+ {
+ char end_str_chat[128] = {0};
+ if(strlen(content_str) > 0)
+ {
+ memset(end_str_chat, 0, 128);
+ memcpy(end_str_chat, content_str, strlen(content_str));
+ ble_set_ai_Answer_str(end_str_chat, 2);
+ }
+ }
+
+ cJSON_Delete(root);
+ return true;
+}
+
+/**
+ * @brief Chat URL初始化
+ */
+bool chat_ws_url_init(char * put_buf, uint8_t type)
+{
+ char token[64] = {0};
+ if(read_dev_token(token))
+ {
+ char device_id[128] = {0};
+ get_dev_id(device_id);
+
+ char * product_id = "1927646275585744898";
+ sprintf(put_buf, "ws://amp.raisound.com/chat/ws?device_id=%s&product_id=%s&dt=%s&platform=aliyun&noCache=1",
+ device_id, product_id, token);
+ return true;
+ }
+ else
+ {
+ char * Service_url = "118.145.158.207:22080";
+ char device_id[128] = {0};
+ get_dev_id(device_id);
+ char * product_id = "1927646275585744898";
+
+ if(type_ai == AI_TYPE_JIU)
+ {
+ sprintf(put_buf, "ws://%s/chat/ws?device_id=%s&product_id=%s&platform=chinamobiledevice&bot_type=jiutian-lan",
+ Service_url, device_id, product_id);
+ }
+ else if(type_ai == AI_TYPE_DOU)
+ {
+ sprintf(put_buf, "ws://%s/chat/ws?platform=huoshan", Service_url);
+ }
+ else if(type_ai == AI_TYPE_WEN)
+ {
+ sprintf(put_buf, "ws://%s/chat/ws?platform=baidu", Service_url);
+ }
+ else if(type_ai == AI_TYPE_KIMI)
+ {
+ sprintf(put_buf, "ws://%s/chat/ws?platform=moonshot", Service_url);
+ }
+ else if(type_ai == AI_TYPE_DE)
+ {
+ sprintf(put_buf, "ws://%s/chat/ws?platform=huoshan&model=ep-20250205103849-582m7", Service_url);
+ }
+ else
+ {
+ sprintf(put_buf, "ws://%s/chat/ws?platform=aliyun", Service_url);
+ }
+ return false;
+ }
+}
+
+/**
+ * @brief 发送聊天文本
+ */
+bool ai_chat_send_txt(uint8_t type, char *msg)
+{
+ if(msg)
+ printf("ai_chat_send_txt message: %s\n", 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);
+ extern void cJSON_free(void *ptr);
+ extern void cJSON_Delete(cJSON *c);
+
+ cJSON* dataJSON = cJSON_CreateObject();
+
+ if (dataJSON == NULL) {
+ printf("Error creating JSON object\n");
+ return false;
+ }
+
+ cJSON_AddStringToObject(dataJSON, "type", "jiutian-lan");
+ cJSON_AddStringToObject(dataJSON, "content", msg);
+
+ char *jsonString = cJSON_PrintUnformatted(dataJSON);
+
+ if (jsonString == NULL) {
+ printf("Error printing JSON to string\n");
+ cJSON_Delete(dataJSON);
+ return false;
+ }
+
+ uint32_t data_len = strlen(jsonString);
+ cJSON_Delete(dataJSON);
+
+ if (data_len == 0) {
+ printf("Error: JSON string length is 0\n");
+ cJSON_free(jsonString);
+ return false;
+ }
+
+ if (send_websocket_message(chat_client, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0)
+ {
+ chat_reconnect = 0;
+ printf("Chat message sent successfully\n");
+ cJSON_free(jsonString);
+ return true;
+ }
+ else
+ {
+ if(chat_client == NULL)
+ {
+ printf("chat_client is NULL\n");
+ }
+ cJSON_free(jsonString);
+ printf("Chat message send failed\n");
+ return false;
+ }
+}
+
+/**
+ * @brief Chat WebSocket主线程
+ */
+void bk_websocket_demo(void *data)
+{
+ // 声明所有变量在函数开始,避免goto跳转问题
+ uint32_t start_time = (uint32_t)osKernelGetTickCount();
+ uint32_t retry_count = 0;
+ WebSocketMessageType type;
+ bool state = true;
+ WebSocketMessageType msg_type;
+ uint32_t last_activity;
+ uint32_t chat_handshake_start_time;
+ uint32_t chat_handshake_timeout_ms;
+
+ if (chat_msg_queue.initialized) {
+ ws_queue_deinit(&chat_msg_queue);
+ }
+ ws_queue_init(&chat_msg_queue, 0);
+
+CHAT_START:
+ // uapi_cat1_keep_wakeup(1); // Commented out due to missing implementation
+ ws_queue_cleanup(&chat_msg_queue);
+ state = true;
+
+ is_bk_websocket_chat = 1;
+
+ uint8_t is_raisound_url = 0;
+
+ {
+ char url_buf[124] = {0};
+
+ if (chat_ws_url_init(url_buf, type_ai)) {
+ printf("Chat URL: %s\n", url_buf);
+ is_raisound_url = 1;
+ } else {
+ is_raisound_url = 0;
+ }
+
+ uint8_t client_count = AI_MAX_RETRY_COUNT;
+
+ while (client_count > 0) {
+ uint32_t connect_start = (uint32_t)osKernelGetTickCount();
+
+ // 根据测试服务器索引选择URL
+ extern uint8_t g_test_server_index;
+ if (g_test_server_index == 1) {
+ chat_client = init_websocket_client("ws://118.145.158.207:22080/chat/ws", NULL, 0);
+ if (chat_client != NULL) {
+ chat_client->msg_queue = &chat_msg_queue;
+ ws_set_packet_callback(chat_client, chat_on_ws_packet_callback);
+ }
+ } else if (g_test_server_index == 2) {
+ chat_client = init_websocket_client("ws://110.42.224.129:8910/chat/ws", NULL, 0);
+ if (chat_client != NULL) {
+ chat_client->msg_queue = &chat_msg_queue;
+ ws_set_packet_callback(chat_client, chat_on_ws_packet_callback);
+ }
+ } else if (is_raisound_url == 1) {
+ chat_client = init_websocket_client(url_buf, NULL, 1);
+ if (chat_client != NULL) {
+ chat_client->msg_queue = &chat_msg_queue;
+ ws_set_packet_callback(chat_client, chat_on_ws_packet_callback);
+ }
+ } else {
+ chat_client = init_websocket_client("ws://118.145.158.207:22080/chat/ws", NULL, 0);
+ if (chat_client != NULL) {
+ chat_client->msg_queue = &chat_msg_queue;
+ ws_set_packet_callback(chat_client, chat_on_ws_packet_callback);
+ }
+ }
+
+ if (chat_client == NULL) {
+ printf("Chat WebSocket client creation failed, retrying... (%d/%d)\n",
+ AI_MAX_RETRY_COUNT - client_count + 1, AI_MAX_RETRY_COUNT);
+ client_count--;
+ msleep(2000);
+ continue;
+ }
+
+ printf("Chat WebSocket client created successfully\n");
+ break;
+ }
+
+ if (chat_client == NULL) {
+ printf("Chat WebSocket: Failed to create client after %d retries\n", AI_MAX_RETRY_COUNT);
+ goto chat_cleanup_and_exit;
+ }
+
+ printf("Chat WebSocket: 连接建立成功,开始握手等待\n");
+
+ // 等待握手完成
+ chat_handshake_start_time = (uint32_t)osKernelGetTickCount();
+ chat_handshake_timeout_ms = AI_WS_HANDSHAKE_TIMEOUT_MS;
+
+ while (chat_client->handshake == 0) {
+ if ((uint32_t)osKernelGetTickCount() - chat_handshake_start_time > chat_handshake_timeout_ms) {
+ printf("Chat WebSocket握手超时 (%d ms),重新连接\n", chat_handshake_timeout_ms);
+ close_websocket_client(chat_client);
+ chat_client = NULL;
+ goto chat_cleanup_and_exit;
+ }
+
+ msleep(1000);
+ printf("等待Chat WebSocket握手完成... (%d ms)\n",
+ (uint32_t)osKernelGetTickCount() - chat_handshake_start_time);
+ }
+
+ printf("Chat WebSocket握手完成,耗时 %d ms\n",
+ (uint32_t)osKernelGetTickCount() - chat_handshake_start_time);
+ }
+
+ // uapi_cat1_keep_wakeup(1); // Commented out due to missing implementation
+ set_ai_chat_service_init_flag(1);
+
+ printf("Chat client initialized successfully\n");
+
+ // 初始化变量
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ extern uint8_t disconnect_server;
+ while (disconnect_server == 0) {
+ // 检查超时
+ if ((uint32_t)osKernelGetTickCount() - last_activity > AI_RECEIVE_TIMEOUT_MS) {
+ printf("Chat receive timeout, reconnecting...\n");
+ break;
+ }
+
+ memset(recv_buf, 0, 1024);
+ 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);
+ int len = receive_websocket_message_from_queue(&chat_msg_queue, (unsigned char *)recv_buf,
+ 1024, &msg_type, AI_RECEIVE_TIMEOUT_MS);
+ if (len < 1024)
+ {
+ recv_buf[len] = '\0';
+ }
+
+ if (len == -1 || msg_type == WS_CLOSE) {
+ printf("Chat WebSocket connection closed\n");
+ break;
+ }
+
+ if (len > 0)
+ {
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ if (msg_type == WS_TEXT) {
+ printf("Chat text data received: %s\n", recv_buf);
+ chat_result_analysis(recv_buf);
+ } else if (msg_type == WS_BINARY) {
+ printf("Chat binary data received: %d bytes\n", len);
+ }
+ }
+
+ msleep(10);
+ }
+
+chat_cleanup_and_exit:
+ printf("Chat WebSocket cleanup and exit\n");
+
+ if (chat_client) {
+ close_websocket_client(chat_client);
+ chat_client = NULL;
+ }
+
+ ws_queue_deinit(&chat_msg_queue);
+ is_bk_websocket_chat = 0;
+ set_ai_chat_service_init_flag(0);
+
+ printf("Chat WebSocket thread ended\n");
+}
+
+/**
+ * @brief Chat服务器初始化
+ */
+errcode_t chat_server_init(void)
+{
+ app_task_definition_t chat_taskcfg;
+
+ int osThreadGetState_t = osThreadGetState(chat_taskcfg.task_handle);
+ const char *buf = osThreadGetName(chat_taskcfg.task_handle);
+
+ printf("Chat osThreadGetState_t %d %s!\r\n", osThreadGetState_t, buf);
+
+ if(strcmp("chat_server_init", buf) == 0)
+ {
+ if(osThreadGetState_t != osThreadReady && osThreadGetState_t != osThreadError &&
+ osThreadGetState_t != osThreadInactive)
+ {
+ printf("Chat osThreadGetState is %d!\r\n", osThreadGetState_t);
+ return ERRCODE_FAIL;
+ }
+ }
+
+ if(is_bk_websocket_chat || chat_client != NULL)
+ {
+ printf("Chat server is already connected!\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ printf("chat_server_init!\r\n");
+
+ chat_taskcfg.create_queue_handle = NULL;
+ chat_taskcfg.attr.name = "chat_server_init";
+ chat_taskcfg.attr.priority = (osPriority_t)TASK_PRIORITY_APP;
+ chat_taskcfg.attr.stack_size = 0x1000;
+ chat_taskcfg.func = bk_websocket_demo;
+ chat_taskcfg.use_mem = USE_EXTEND_MEM;
+
+ extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg);
+ errcode_t ret = uapi_create_osThread_custom(&chat_taskcfg);
+
+ if(ret != ERRCODE_SUCC)
+ {
+ printf("create Chat service thread failed %d!\r\n", ret);
+ return ERRCODE_FAIL;
+ }
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief Chat服务器反初始化
+ */
+void chat_server_deinit(void)
+{
+ printf("chat_server_deinit\n");
+
+ // 发送关闭消息
+ if (chat_client) {
+ if(send_websocket_message(chat_client, (uint8_t *)"WS_CLOSE", strlen("WS_CLOSE"), WS_CLOSE))
+ {
+ printf("Chat WS_CLOSE sent\n");
+ }
+ }
+
+ if (chat_client) {
+ close_websocket_client(chat_client);
+ chat_client = NULL;
+ }
+
+ is_bk_websocket_chat = 0;
+ set_ai_chat_service_init_flag(0);
+}
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
--- /dev/null
+/**
+ * @file chat_websocket_server.h
+ * @brief Chat WebSocket服务器头文件
+ * @version 1.0.0
+ * @date 2024-09-05
+ */
+
+#ifndef __CHAT_WEBSOCKET_SERVER_H__
+#define __CHAT_WEBSOCKET_SERVER_H__
+
+#include "bk_websocket.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include "zhihui_ws.h"
+#include "errcode.h"
+#include "thread_init.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Chat 服务相关类型定义 */
+// errcode_t is defined in errcode.h
+
+/* AI类型枚举 - 使用zhihui_ws.h中的定义 */
+
+/* 任务配置结构体 - 使用系统定义的 app_task_definition_t */
+
+/* 常量定义 */
+#define TASK_PRIORITY_APP 24
+#define USE_EXTEND_MEM 1
+
+/* Chat 服务接口函数 */
+
+/**
+ * @brief Chat WebSocket数据包回调函数
+ * @param data 数据指针
+ * @param datalen 数据长度
+ * @param type WebSocket消息类型
+ */
+void chat_on_ws_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type);
+
+/**
+ * @brief 获取Chat服务初始化标志
+ * @return 初始化标志
+ */
+uint8_t get_ai_chat_service_init_flag(void);
+
+/**
+ * @brief 设置Chat服务初始化标志
+ * @param flag 标志值
+ */
+void set_ai_chat_service_init_flag(uint8_t flag);
+
+/**
+ * @brief Chat结果分析
+ * @param buf 结果字符串
+ * @return 成功返回true,失败返回false
+ */
+bool chat_result_analysis(char *buf);
+
+/**
+ * @brief Chat URL初始化
+ * @param put_buf 输出缓冲区
+ * @param type AI类型
+ * @return 成功返回true,失败返回false
+ */
+bool chat_ws_url_init(char * put_buf, uint8_t type);
+
+/**
+ * @brief 发送聊天文本
+ * @param type 消息类型
+ * @param msg 消息内容
+ * @return 成功返回true,失败返回false
+ */
+bool ai_chat_send_txt(uint8_t type, char *msg);
+
+/**
+ * @brief Chat WebSocket主线程
+ * @param data 线程参数
+ */
+void bk_websocket_demo(void *data);
+
+/**
+ * @brief Chat服务器初始化
+ * @return 成功返回ERRCODE_SUCC,失败返回ERRCODE_FAIL
+ */
+errcode_t chat_server_init(void);
+
+/**
+ * @brief Chat服务器反初始化
+ */
+void chat_server_deinit(void);
+
+/* 全局变量声明 */
+extern WebSocketClient *chat_client;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CHAT_WEBSOCKET_SERVER_H__ */
\ No newline at end of file
--- /dev/null
+/**
+ * @file tts_websocket_server.cpp
+ * @brief TTS WebSocket服务器实现
+ * @version 1.0.0
+ * @date 2024-09-05
+ *
+ * 从 zhihui_ai_server.cpp 中提取的 TTS 相关功能
+ */
+
+#include "tts_websocket_server.h"
+#include "zhihui_ws.h"
+#include "bk_websocket.h"
+#include "ws_queue_common.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "wearable_log.h"
+#include "custom_runtime.h"
+#include "cmsis_os2.h"
+#include "osal_msgqueue.h"
+#include "thread_init.h"
+#include "soc_osal.h"
+#include "common_def.h"
+#include "los_printf.h"
+#include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* TTS 相关全局变量 */
+WebSocketClient *tts_client = NULL;
+static ws_message_queue_t tts_msg_queue = {0};
+static uint8_t tts_break = 0;
+static uint8_t tts_reconnect = 0;
+static uint8_t is_bk_websocket_tts = 0;
+static uint8_t ai_tts_server_init_flag = 0;
+static uint8_t tts_is_ready = 0;
+static uint8_t tts_client_ping = 0;
+static uint32_t bk_websocket_tts_tick = 0;
+
+/* 外部函数声明 */
+extern bool read_dev_token(char *data_token);
+// extern void uapi_cat1_keep_wakeup(uint8_t state); // Commented out due to missing implementation
+extern void set_AIStreamend(uint8_t state);
+extern int send_msg_data_into_Ai_msg_queue(void *data, uint16_t len, uint16_t type);
+extern void msleep(uint32_t ms);
+extern int32_t VoiceCallVolteSample(int32_t argc, const char **argv);
+
+/* TTS 配置和常量 */
+#define AI_MAX_RETRY_COUNT 3
+#define AI_WS_HANDSHAKE_TIMEOUT_MS 10000
+#define AI_RECEIVE_TIMEOUT_MS 30000
+
+static char tts_task_id_buf[128] = {0};
+
+/**
+ * @brief TTS WebSocket数据包回调函数
+ */
+void tts_on_ws_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type)
+{
+ if(tts_client != NULL && tts_client->msg_queue != NULL)
+ {
+ ws_queue_put(tts_client->msg_queue, data, datalen, type);
+ }
+}
+
+/**
+ * @brief 设置TTS中断标志
+ */
+void uapi_set_tts_break(uint8_t data)
+{
+ tts_break = data;
+}
+
+/**
+ * @brief 获取TTS中断标志
+ */
+uint8_t uapi_get_tts_break(void)
+{
+ return tts_break;
+}
+
+/**
+ * @brief 获取TTS服务初始化标志
+ */
+uint8_t get_ai_tts_server_init_flag(void)
+{
+ return ai_tts_server_init_flag;
+}
+
+/**
+ * @brief 设置TTS服务初始化标志
+ */
+void set_ai_tts_server_init_flag(uint8_t flag)
+{
+ ai_tts_server_init_flag = flag;
+}
+
+/**
+ * @brief 获取TTS时间戳
+ */
+uint32_t get_bk_websocket_tts_tick(void)
+{
+ return bk_websocket_tts_tick;
+}
+
+/**
+ * @brief TTS URL初始化
+ */
+bool tts_ws_url_init(char * put_buf)
+{
+ char token[64] = {0};
+ if(read_dev_token(token))
+ {
+ sprintf(put_buf, "ws://amp.raisound.com/tts/stream?dt=%s&platform=aliyun&noCache=1", token);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/**
+ * @brief TTS播放初始化
+ */
+int32_t uapi_tts_paly_init(void)
+{
+ printf("uapi_tts_paly_init\n");
+
+ int32_t ret = ERRCODE_FAIL;
+ int32_t argc = 1;
+ uint8_t *argv = (uint8_t *)"StartAIpaly";
+ {
+ ret = VoiceCallVolteSample(argc, (const char **)&argv);
+ }
+
+ printf("TTS play init ret: %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * @brief TTS播放停止
+ */
+int32_t uapi_tts_paly_stop(void)
+{
+ printf("uapi_tts_paly_stop\n");
+
+ int32_t ret = ERRCODE_FAIL;
+ int32_t argc = 1;
+ uint8_t *argv = (uint8_t *)"StopAIpaly";
+ {
+ ret = VoiceCallVolteSample(argc, (const char **)&argv);
+ }
+
+ printf("TTS play stop ret: %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * @brief 启动TTS服务
+ */
+bool uapi_start_tts(void)
+{
+ char *jsonString =
+ "{"
+ "\"type\":\"start\","
+ "\"task_id\":\"\","
+ "\"audio_config\":{"
+ "\"sample_rate\":16000,"
+ "\"format\":\"mp3\","
+ "\"channel\":1"
+ "},"
+ "\"request_config\":{"
+ "\"protocol\":1,"
+ "\"audio_buffer_size\":1024"
+ "}"
+ "}";
+
+ if (tts_client == NULL) {
+ printf("TTS client is NULL\n");
+ return false;
+ }
+
+ if (send_websocket_message(tts_client, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0) {
+ tts_reconnect = 0;
+ printf("TTS start message sent successfully\n");
+ return true;
+ } else {
+ printf("TTS start message send failed\n");
+ return false;
+ }
+}
+
+/**
+ * @brief 启动单个TTS文本合成
+ */
+bool uapi_start_tts_single_txt(char *buf)
+{
+ if (!buf || strlen(buf) == 0) {
+ printf("TTS text buffer is empty\n");
+ return false;
+ }
+
+ if (tts_client == NULL) {
+ printf("TTS client is NULL\n");
+ return false;
+ }
+
+ // 构建TTS请求JSON
+ char *json_template =
+ "{"
+ "\"type\":\"text\","
+ "\"task_id\":\"%s\","
+ "\"text\":\"%s\""
+ "}";
+
+ 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");
+ return false;
+ }
+
+ sprintf(jsonString, json_template, tts_task_id_buf, buf);
+
+ 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;
+ } else {
+ printf("TTS text message send failed\n");
+ }
+
+ free(jsonString);
+ return result;
+}
+
+/**
+ * @brief 停止TTS服务
+ */
+void uapi_stop_tts(void)
+{
+ tts_break = 1;
+
+ if (tts_client) {
+ char *stop_json = "{\"type\":\"stop\",\"task_id\":\"\"}";
+ send_websocket_message(tts_client, (uint8_t *)stop_json, strlen(stop_json), WS_TEXT);
+ }
+
+ uapi_tts_paly_stop();
+}
+
+/**
+ * @brief TTS WebSocket主线程
+ */
+void bk_websocket_tts(void *data)
+{
+ // 声明所有变量在函数开始,避免goto跳转问题
+ uint32_t start_time = (uint32_t)osKernelGetTickCount();
+ uint32_t retry_count = 0;
+ WebSocketMessageType type;
+ bool state = true;
+ WebSocketMessageType msg_type;
+ uint32_t last_activity;
+ char *tts_recv_buf = NULL;
+
+ if (tts_msg_queue.initialized) {
+ ws_queue_deinit(&tts_msg_queue);
+ }
+ ws_queue_init(&tts_msg_queue, 0);
+
+TTS_START:
+ // uapi_cat1_keep_wakeup(1); // Commented out due to missing implementation
+ ws_queue_cleanup(&tts_msg_queue);
+ state = true;
+
+ is_bk_websocket_tts = 1;
+
+ uint8_t is_raisound_url = 0;
+
+ {
+ char url_buf[124] = {0};
+ if (tts_ws_url_init(url_buf)) {
+ printf("TTS URL: %s\n", url_buf);
+ is_raisound_url = 1;
+ } else {
+ is_raisound_url = 0;
+ }
+
+ uint8_t client_count = AI_MAX_RETRY_COUNT;
+
+ while (client_count > 0) {
+ uint32_t connect_start = (uint32_t)osKernelGetTickCount();
+
+ // 根据测试服务器索引选择URL
+ extern uint8_t g_test_server_index;
+ if (g_test_server_index == 1) {
+ tts_client = init_websocket_client("ws://118.145.158.207:22080/tts/stream", NULL, 0);
+ if (tts_client != NULL) {
+ tts_client->msg_queue = &tts_msg_queue;
+ ws_set_packet_callback(tts_client, tts_on_ws_packet_callback);
+ }
+ } else if (g_test_server_index == 2) {
+ tts_client = init_websocket_client("ws://110.42.224.129:8910/tts/stream", NULL, 0);
+ if (tts_client != NULL) {
+ tts_client->msg_queue = &tts_msg_queue;
+ ws_set_packet_callback(tts_client, tts_on_ws_packet_callback);
+ }
+ } else if (is_raisound_url == 1) {
+ tts_client = init_websocket_client(url_buf, NULL, 1);
+ if (tts_client != NULL) {
+ tts_client->msg_queue = &tts_msg_queue;
+ ws_set_packet_callback(tts_client, tts_on_ws_packet_callback);
+ }
+ } else {
+ tts_client = init_websocket_client("ws://118.145.158.207:22080/tts/stream", NULL, 0);
+ if (tts_client != NULL) {
+ tts_client->msg_queue = &tts_msg_queue;
+ ws_set_packet_callback(tts_client, tts_on_ws_packet_callback);
+ }
+ }
+
+ if (tts_client == NULL) {
+ printf("TTS WebSocket client creation failed, retrying... (%d/%d)\n",
+ AI_MAX_RETRY_COUNT - client_count + 1, AI_MAX_RETRY_COUNT);
+ client_count--;
+ msleep(2000);
+ continue;
+ }
+
+ printf("TTS WebSocket client created successfully\n");
+ break;
+ }
+
+ if (tts_client == NULL) {
+ printf("TTS WebSocket: Failed to create client after %d retries\n", AI_MAX_RETRY_COUNT);
+ goto tts_cleanup_and_exit;
+ }
+
+ printf("TTS WebSocket: 连接建立成功,开始握手等待\n");
+
+ // 等待握手完成
+ uint32_t tts_handshake_start_time = (uint32_t)osKernelGetTickCount();
+ const uint32_t tts_handshake_timeout_ms = AI_WS_HANDSHAKE_TIMEOUT_MS;
+
+ while (tts_client->handshake == 0) {
+ if ((uint32_t)osKernelGetTickCount() - tts_handshake_start_time > tts_handshake_timeout_ms) {
+ printf("TTS WebSocket握手超时 (%d ms),重新连接\n", tts_handshake_timeout_ms);
+ close_websocket_client(tts_client);
+ tts_client = NULL;
+ goto tts_cleanup_and_exit;
+ }
+
+ msleep(1000);
+ printf("等待TTS WebSocket握手完成... (%d ms)\n",
+ (uint32_t)osKernelGetTickCount() - tts_handshake_start_time);
+ }
+
+ printf("TTS WebSocket握手完成,耗时 %d ms\n",
+ (uint32_t)osKernelGetTickCount() - tts_handshake_start_time);
+ }
+
+ // uapi_cat1_keep_wakeup(1); // Commented out due to missing implementation
+ set_ai_tts_server_init_flag(1);
+
+ printf("TTS client initialized successfully\n");
+
+ // 分配接收缓冲区
+ tts_recv_buf = (char *)malloc(1024 * 4);
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ if (!tts_recv_buf) {
+ printf("Failed to allocate TTS receive buffer\n");
+ goto tts_cleanup_and_exit;
+ }
+
+ while (tts_break == 0) {
+ // 检查超时
+ if ((uint32_t)osKernelGetTickCount() - last_activity > AI_RECEIVE_TIMEOUT_MS) {
+ printf("TTS receive timeout, reconnecting...\n");
+ break;
+ }
+
+ memset(tts_recv_buf, 0, 1024 * 4);
+ 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);
+ int len = receive_websocket_message_from_queue(&tts_msg_queue, (unsigned char *)tts_recv_buf,
+ 1024 * 4, &msg_type, AI_RECEIVE_TIMEOUT_MS);
+
+ if (len == -1 || msg_type == WS_CLOSE) {
+ printf("TTS WebSocket connection closed\n");
+ break;
+ }
+
+ if (len > 0) {
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ if (msg_type == WS_BINARY) {
+ if (bk_websocket_tts_tick == 0) {
+ set_AIStreamend(0);
+ bk_websocket_tts_tick = (uint32_t)osKernelGetTickCount();
+ }
+ send_msg_data_into_Ai_msg_queue((uint8_t *)tts_recv_buf, len, 200);
+ printf("TTS binary data received: %d bytes\n", len);
+ msleep(1);
+ } else if (msg_type == WS_TEXT) {
+ printf("TTS text data received: %s\n", tts_recv_buf);
+ }
+ }
+
+ msleep(10);
+ }
+
+tts_cleanup_and_exit:
+ printf("TTS WebSocket cleanup and exit\n");
+
+ if (tts_recv_buf) {
+ free(tts_recv_buf);
+ }
+
+ if (tts_client) {
+ close_websocket_client(tts_client);
+ tts_client = NULL;
+ }
+
+ ws_queue_deinit(&tts_msg_queue);
+ is_bk_websocket_tts = 0;
+ set_ai_tts_server_init_flag(0);
+ bk_websocket_tts_tick = 0;
+
+ printf("TTS WebSocket thread ended\n");
+}
+
+/**
+ * @brief TTS服务器初始化
+ */
+errcode_t tts_server_init(void)
+{
+ app_task_definition_t tts_taskcfg;
+
+ osThreadState_t osThreadGetState_t = osThreadGetState(tts_taskcfg.task_handle);
+ const char *buf = osThreadGetName(tts_taskcfg.task_handle);
+
+ printf("TTS osThreadGetState_t %d!\r\n", osThreadGetState_t);
+
+ if(strcmp("tts_server_init", buf) == 0)
+ {
+ if(osThreadGetState_t != osThreadReady && osThreadGetState_t != osThreadError &&
+ osThreadGetState_t != osThreadInactive)
+ {
+ printf("TTS osThreadGetState is %d!\r\n", osThreadGetState_t);
+ return ERRCODE_FAIL;
+ }
+ }
+
+ if(tts_client != NULL || is_bk_websocket_tts)
+ {
+ printf("TTS server is already connected!\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ printf("tts_server_init!\r\n");
+
+ tts_taskcfg.create_queue_handle = NULL;
+ tts_taskcfg.attr.name = "tts_server_init";
+ tts_taskcfg.attr.priority = (osPriority_t)TASK_PRIORITY_APP;
+ tts_taskcfg.attr.stack_size = 0x2000;
+ tts_taskcfg.func = bk_websocket_tts;
+ tts_taskcfg.use_mem = USE_EXTEND_MEM;
+
+ extern errcode_t uapi_create_osThread_custom(app_task_definition_t* task_cfg);
+ errcode_t ret = uapi_create_osThread_custom(&tts_taskcfg);
+
+ if(ret != ERRCODE_SUCC)
+ {
+ printf("create TTS service thread failed %d!\r\n", ret);
+ return ERRCODE_FAIL;
+ }
+
+ return ERRCODE_SUCC;
+}
+
+/**
+ * @brief TTS服务器反初始化
+ */
+void tts_server_deinit(void)
+{
+ printf("tts_server_deinit\n");
+
+ // 发送关闭消息
+ if (tts_client) {
+ if(send_websocket_message(tts_client, (uint8_t *)"WS_CLOSE", strlen("WS_CLOSE"), WS_CLOSE))
+ {
+ printf("TTS WS_CLOSE sent\n");
+ }
+ }
+
+ tts_break = 1;
+
+ if (tts_client) {
+ close_websocket_client(tts_client);
+ tts_client = NULL;
+ }
+
+ is_bk_websocket_tts = 0;
+ set_ai_tts_server_init_flag(0);
+}
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
--- /dev/null
+/**
+ * @file tts_websocket_server.h
+ * @brief TTS WebSocket服务器头文件
+ * @version 1.0.0
+ * @date 2024-09-05
+ */
+
+#ifndef __TTS_WEBSOCKET_SERVER_H__
+#define __TTS_WEBSOCKET_SERVER_H__
+
+#include "bk_websocket.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include "errcode.h"
+#include "thread_init.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* TTS 服务相关类型定义 - errcode_t is defined in errcode.h */
+
+/* 任务配置结构体 - 使用系统定义的 app_task_definition_t */
+
+/* 常量定义 */
+#define TASK_PRIORITY_APP 24
+#define USE_EXTEND_MEM 1
+
+/* TTS 服务接口函数 */
+
+/**
+ * @brief TTS WebSocket数据包回调函数
+ * @param data 数据指针
+ * @param datalen 数据长度
+ * @param type WebSocket消息类型
+ */
+void tts_on_ws_packet_callback(uint8_t* data, size_t datalen, WebSocketMessageType type);
+
+/**
+ * @brief 设置TTS中断标志
+ * @param data 标志值
+ */
+void uapi_set_tts_break(uint8_t data);
+
+/**
+ * @brief 获取TTS中断标志
+ * @return 中断标志
+ */
+uint8_t uapi_get_tts_break(void);
+
+/**
+ * @brief 获取TTS服务初始化标志
+ * @return 初始化标志
+ */
+uint8_t get_ai_tts_server_init_flag(void);
+
+/**
+ * @brief 设置TTS服务初始化标志
+ * @param flag 标志值
+ */
+void set_ai_tts_server_init_flag(uint8_t flag);
+
+/**
+ * @brief 获取TTS时间戳
+ * @return 时间戳
+ */
+uint32_t get_bk_websocket_tts_tick(void);
+
+/**
+ * @brief TTS URL初始化
+ * @param put_buf 输出缓冲区
+ * @return 成功返回true,失败返回false
+ */
+bool tts_ws_url_init(char * put_buf);
+
+/**
+ * @brief TTS播放初始化
+ * @return 成功返回ERRCODE_SUCC,失败返回ERRCODE_FAIL
+ */
+int32_t uapi_tts_paly_init(void);
+
+/**
+ * @brief TTS播放停止
+ * @return 成功返回ERRCODE_SUCC,失败返回ERRCODE_FAIL
+ */
+int32_t uapi_tts_paly_stop(void);
+
+/**
+ * @brief 启动TTS服务
+ * @return 成功返回true,失败返回false
+ */
+bool uapi_start_tts(void);
+
+/**
+ * @brief 启动单个TTS文本合成
+ * @param buf 文本内容
+ * @return 成功返回true,失败返回false
+ */
+bool uapi_start_tts_single_txt(char *buf);
+
+/**
+ * @brief 停止TTS服务
+ */
+void uapi_stop_tts(void);
+
+/**
+ * @brief TTS WebSocket主线程
+ * @param data 线程参数
+ */
+void bk_websocket_tts(void *data);
+
+/**
+ * @brief TTS服务器初始化
+ * @return 成功返回ERRCODE_SUCC,失败返回ERRCODE_FAIL
+ */
+errcode_t tts_server_init(void);
+
+/**
+ * @brief TTS服务器反初始化
+ */
+void tts_server_deinit(void);
+
+/* 全局变量声明 */
+extern WebSocketClient *tts_client;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TTS_WEBSOCKET_SERVER_H__ */
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+#include <lwip/netdb.h>
+#include "lwip/inet.h"
+
+#include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h"
+#include "wearable_log.h"
+#include "custom_runtime.h"
+#include "cmsis_os2.h"
+#include "bk_websocket.h"
+#include "voicechat_websocket_server.h"
+#include "ws_queue_common.h"
+#include "osal_msgqueue.h"
+#include "thread_init.h"
+#include "soc_osal.h"
+#include "common_def.h"
+#include "los_printf.h"
+
+// 内存管理和错误处理优化
+#define AI_SAFE_MALLOC(size) ({ \
+ void* ptr = malloc(size); \
+ if (!ptr) { \
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Memory allocation failed: %s:%d", __FILE__, __LINE__); \
+ } \
+ ptr; \
+})
+
+#define AI_SAFE_FREE(ptr) do { \
+ if (ptr) { \
+ free(ptr); \
+ ptr = NULL; \
+ } \
+} while(0)
+
+// 超时和重试配置
+#define AI_CONNECT_TIMEOUT_MS 30000 // 30秒连接超时
+#define AI_RECEIVE_TIMEOUT_MS 10000 // 10秒接收超时
+#define AI_MAX_RETRY_COUNT 3 // 最大重试次数
+
+// 外部变量声明
+extern uint32_t disconnect_server;
+extern uint32_t bk_websocket_tts_tick;
+extern osMutexId_t g_ai_mutex;
+static uint32_t g_ai_memory_usage = 0;
+static uint32_t g_ai_max_memory = 1024 * 1024; // 1MB内存限制
+
+// 外部函数声明
+extern bool ai_is_json_string(const char *str, size_t len);
+extern WebSocketClient* init_websocket_client(const char* url, const char* protocol, uint8_t is_ssl);
+extern int send_websocket_message(WebSocketClient* client, const uint8_t* data, size_t length, WebSocketMessageType type);
+extern void close_websocket_client(WebSocketClient* client);
+
+// 内存使用监控
+static inline bool ai_check_memory_limit(uint32_t size) {
+ if (g_ai_memory_usage + size > g_ai_max_memory) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Memory limit exceeded: %u + %u > %u",
+ g_ai_memory_usage, size, g_ai_max_memory);
+ return false;
+ }
+ return true;
+}
+
+static inline void ai_update_memory_usage(int32_t delta) {
+ if (delta > 0) {
+ g_ai_memory_usage += delta;
+ } else {
+ g_ai_memory_usage = (g_ai_memory_usage > (uint32_t)(-delta)) ?
+ g_ai_memory_usage + delta : 0;
+ }
+}
+// 线程安全保护
+#define AI_LOCK() do { \
+ if (g_ai_mutex) { \
+ osMutexAcquire(g_ai_mutex, osWaitForever); \
+ } \
+} while(0)
+
+#define AI_UNLOCK() do { \
+ if (g_ai_mutex) { \
+ osMutexRelease(g_ai_mutex); \
+ } \
+} while(0)
+
+// VoiceChat server global variables
+WebSocketClient *voice_chat_client = NULL;
+uint8_t is_bk_websocket_voice_chat = 0;
+char dialog_id_str_buf[128] = {0};
+uint8_t ai_voice_chat_state = 0;
+
+
+/**
+ * @brief 处理WebSocket连接失败的统一函数
+ * @param client_type 客户端类型字符串
+ * @param retry_count 当前重试次数
+ * @param max_retries 最大重试次数
+ * @return true 如果应该继续重试,false 如果应该停止重试
+ */
+static bool handle_websocket_connection_failure(const char* client_type, uint8_t retry_count, uint8_t max_retries) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s WebSocket connection failed, retries left: %d", client_type, max_retries - retry_count);
+
+ if (retry_count >= max_retries) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s WebSocket connection failed after %d retries, giving up", client_type, max_retries);
+ return false; // 停止重试
+ }
+
+ // 等待一段时间后重试
+ msleep(1000);
+ return true; // 继续重试
+}
+
+// VoiceChat server global variables access functions
+WebSocketClient* get_voice_chat_client(void) {
+ return voice_chat_client;
+}
+
+uint8_t get_is_bk_websocket_voice_chat(void) {
+ return is_bk_websocket_voice_chat;
+}
+
+void set_is_bk_websocket_voice_chat(uint8_t flag) {
+ is_bk_websocket_voice_chat = flag;
+}
+
+char* get_dialog_id_str_buf(void) {
+ return dialog_id_str_buf;
+}
+
+uint8_t get_ai_voice_chat_state(void) {
+ return ai_voice_chat_state;
+}
+
+void set_ai_voice_chat_state(uint8_t state) {
+ ai_voice_chat_state = state;
+}
+
+// VoiceChat server functions
+void ai_asr_send_bin(uint8_t *buf, uint32_t len)
+{
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,"ai_asr_send_bin %d\n",len);
+ // voice_chat_client
+
+ // if (send_websocket_message(asr_client,( uint8_t *)buf,len,WS_BINARY)==0)
+ extern WebSocketClient *asr_client;
+ if (send_websocket_message(asr_client,( uint8_t *)buf,len,WS_BINARY)==0)
+ {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,"send_websocket_message succ 0\n");
+ msleep(100);
+ }
+ else
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"send_websocket_message fail 0\n");
+ }
+}
+
+bool start_ai_voice_chat()
+{
+ unsigned char deviceId[20] = {0};
+ memset(deviceId, 0, 6);
+
+ bluetooth_get_local_addr(&deviceId[0], 6);
+
+ char deviceId_temp[20] = {0};
+ sprintf(deviceId_temp, "%02X%02x%02X%02x%02X%02x", deviceId[5], deviceId[4], deviceId[3], deviceId[2], deviceId[1], deviceId[0]);
+
+ time_t timestamp;
+ timestamp = time(NULL);
+ long long timestamp_ms = (long long)timestamp;
+
+ char* timestamp_str_2 = (char *)AI_SAFE_MALLOC(20);
+ if (!timestamp_str_2) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to allocate timestamp buffer");
+ return false;
+ }
+ ai_update_memory_usage(20);
+
+ memset(timestamp_str_2, 0, 20);
+ sprintf(timestamp_str_2, "%lld", timestamp_ms);
+
+ char md5_buf[33] = {0};
+ active_md5_buf((char *)timestamp_str_2, md5_buf);
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "MD5 buffer: %s", md5_buf);
+
+ // 释放timestamp_str_2内存
+ AI_SAFE_FREE(timestamp_str_2);
+ ai_update_memory_usage(-20);
+
+ char jsonString[800] = {0};
+ sprintf(jsonString, "{ \
+ \"header\": { \
+ \"trace_id\": \"%s\", \
+ \"event\":\"Start\" \
+ }, \
+ \"payload\": { \
+ \"device_id\": \"%s\", \
+ \"user_agent\": \"python\", \
+ \"dialog_id\":\"\", \
+ \"in_format\": \"pcm\", \
+ \"out_format\": \"mp3\", \
+ \"in_sample_rate\": 16000, \
+ \"volume\": \"10\", \
+ \"sample_rate\": 16000, \
+ \"voice\": \"zh_female_shuangkuaisisi_moon_bigtts\", \
+ \"voice_detection\": \"disable\" \
+ } \
+ } ", md5_buf, deviceId_temp);
+
+ if (send_websocket_message(voice_chat_client, (uint8_t *)jsonString, strlen(jsonString), WS_TEXT) == 0) {
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "Voice chat start message sent successfully: %s", jsonString);
+ return true;
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to send voice chat start message");
+ return false;
+ }
+}
+
+void start_send_voice(void)
+{
+ char *jsonString = (char *)malloc(150);// [512] ={0};
+ memset(jsonString,0,150);
+
+ sprintf(jsonString,
+ "{\"header\":{\"trace_id\":\"ailaqbo0r6o\",\"event\":\"SendSpeech\"},\
+ \"payload\":{\"dialog_id\":\"%s\"}}"
+
+ ,dialog_id_str_buf);
+
+ printf("start_send_voice : %s\r\n",jsonString);
+
+ if (send_websocket_message(voice_chat_client,( uint8_t *)jsonString,strlen(jsonString),WS_TEXT)==0)
+ {
+ startAudioCaptureTask();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,"send_websocket_message succ 0\n");
+
+ }
+
+ free(jsonString);
+}
+
+void back_voice_RequestToSpeak(void)
+{
+ char *jsonString = (char *)malloc(150);// [512] ={0};
+
+ sprintf(jsonString,
+
+ "{\"header\":{\"trace_id\":\"ailaqbo0r6o\",\"event\":\"RequestToSpeak\"},\
+ \"payload\":{\"dialog_id\":\"%s\"}}"
+
+ ,dialog_id_str_buf);
+
+ printf("uapi_start_tts : %s\r\n",jsonString);
+
+ if (send_websocket_message(voice_chat_client,( uint8_t *)jsonString,strlen(jsonString),WS_TEXT)==0)
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,"send_websocket_message succ 0\n");
+ free(jsonString);
+}
+
+bool voice_chat_analysis(char *buf)
+{
+ cJSON* root = cJSON_Parse((const char *)buf);
+ if (root == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"解析失败\n");
+ return false;
+ }
+ char *event_str=NULL;
+ char *dialog_id_str=NULL;
+ char *state_str=NULL;
+ char *text_str=NULL;
+
+ cJSON* event = cJSON_GetObjectItem(root, "event");
+ if (event && cJSON_IsString(event)) {
+ event_str= event->valuestring;
+ }
+
+
+ cJSON* payload = cJSON_GetObjectItem(root, "payload");
+ if(payload)
+ {
+ cJSON* dialog_id = cJSON_GetObjectItem(payload, "dialog_id");
+ if (dialog_id && cJSON_IsString(dialog_id)) {
+ dialog_id_str= dialog_id->valuestring;
+ memset(dialog_id_str_buf,0,128);
+ memcpy(dialog_id_str_buf,dialog_id_str,strlen(dialog_id_str));
+ }
+
+ cJSON* state = cJSON_GetObjectItem(payload, "state");
+ if (state && cJSON_IsString(state)) {
+ state_str= dialog_id->valuestring;
+
+ if(strcmp("Responding",state_str)==0)
+ {
+ ai_voice_chat_state=Responding;
+ }
+ else if (strcmp("Listening",state_str)==0)
+ {
+ ai_voice_chat_state=Listening;
+ }
+ else if (strcmp("Thinking",state_str)==0)
+ {
+ ai_voice_chat_state=Thinking;
+ }
+ }
+
+ cJSON* text = cJSON_GetObjectItem(payload, "text");
+
+ if (text && cJSON_IsString(state)) {
+ state_str= dialog_id->valuestring;
+ }
+
+ }
+
+ if(strcmp("Started",event_str)==0)
+ {
+
+ }
+
+ if(strcmp("SpeechEnded",event_str)==0)
+ {
+ stop_AudioCaptureProcess_AI();
+ }
+
+
+ cJSON_Delete(root);
+ return true;
+}
+
+void voice_chat_websocket(void *data) {
+ uint32_t start_time = (uint32_t)osKernelGetTickCount();
+ uint32_t retry_count = 0;
+
+ uapi_cat1_keep_wakeup(1);
+
+ WebSocketMessageType type;
+ bool state = true;
+
+ AI_LOCK();
+ is_bk_websocket_voice_chat = 1;
+ AI_UNLOCK();
+
+ uint8_t is_raisound_url = 0;
+
+ {
+ char url_buf[124] = {0};
+
+ uint8_t client_count = AI_MAX_RETRY_COUNT;
+
+ // 检查内存使用情况
+ if (!ai_check_memory_limit(1024 * 4)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Memory limit exceeded for voice chat buffer");
+ goto voice_chat_cleanup_and_exit;
+ }
+
+ while (client_count > 0) {
+ uint32_t connect_start = (uint32_t)osKernelGetTickCount();
+
+ if (is_raisound_url == 1) {
+ voice_chat_client = init_websocket_client(url_buf, NULL, 1);
+ if (voice_chat_client != NULL) {
+ // voice_chat_client->msg_queue = &voice_msg_queue;
+ }
+ } else {
+ voice_chat_client = init_websocket_client("ws://118.145.158.207:22080/voice-chat/v2/ws?platform=huoshan", NULL, 0);
+ if (voice_chat_client != NULL) {
+ // voice_chat_client->msg_queue = &voice_msg_queue;
+ }
+ }
+
+ if (voice_chat_client == NULL) {
+ client_count--;
+
+ // 使用统一的错误处理函数
+ if (!handle_websocket_connection_failure("Voice chat", AI_MAX_RETRY_COUNT - client_count, AI_MAX_RETRY_COUNT)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Voice chat WebSocket connection failed after all retries");
+ break;
+ }
+
+ // 检查超时
+ if ((uint32_t)osKernelGetTickCount() - connect_start > AI_CONNECT_TIMEOUT_MS) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Voice chat connection timeout");
+ break;
+ }
+
+ continue;
+ } else {
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "Voice chat WebSocket connection established");
+ break;
+ }
+ }
+
+ if (voice_chat_client == NULL) {
+ goto voice_chat_cleanup_and_exit;
+ }
+
+ // 安全分配voice chat接收缓冲区
+ char* voice_chat_client_buf = (char *)AI_SAFE_MALLOC(1024 * 4);
+ if (!voice_chat_client_buf) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to allocate voice chat receive buffer");
+ goto voice_chat_cleanup_and_exit;
+ }
+ ai_update_memory_usage(1024 * 4);
+
+ // 设置socket超时
+ // struct timeval timeout;
+ // timeout.tv_sec = AI_RECEIVE_TIMEOUT_MS / 1000;
+ // timeout.tv_usec = (AI_RECEIVE_TIMEOUT_MS % 1000) * 1000;
+ // lwip_setsockopt(voice_chat_client->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+ if (!start_ai_voice_chat()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to start AI voice chat");
+ AI_SAFE_FREE(voice_chat_client_buf);
+ ai_update_memory_usage(-1024 * 4);
+ goto voice_chat_cleanup_and_exit;
+ }
+
+ uint32_t last_activity = (uint32_t)osKernelGetTickCount();
+
+ while (disconnect_server == 0) {
+ // 检查超时
+ if ((uint32_t)osKernelGetTickCount() - last_activity > AI_RECEIVE_TIMEOUT_MS) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Voice chat receive timeout, reconnecting...");
+ break;
+ }
+
+ memset_s(voice_chat_client_buf, 1024 * 4, 0, 1024 * 4);
+ int len = -1;
+ // len = receive_websocket_message_from_queue(&voice_msg_queue, (unsigned char *)voice_chat_client_buf, 1024 * 4, &type, AI_RECEIVE_TIMEOUT_MS);
+
+ if (type == WS_CLOSE || len == -1) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Voice chat WebSocket connection closed");
+ break;
+ }
+
+ if (len > 0) {
+ last_activity = (uint32_t)osKernelGetTickCount();
+
+ if (type == WS_BINARY) {
+ if (bk_websocket_tts_tick == 0) {
+ set_AIStreamend(0);
+ bk_websocket_tts_tick = (uint32_t)osKernelGetTickCount();
+ }
+ if(ai_is_json_string(voice_chat_client_buf, len))
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Voice chat bin received: buffer %s\n",voice_chat_client_buf);
+ }
+ send_msg_data_into_Ai_msg_queue((uint8_t *)voice_chat_client_buf, len, 200);
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "Voice chat binary data received: %d bytes", len);
+ msleep(1);
+ } else if (type == WS_TEXT) {
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "Voice chat text data received: %s", voice_chat_client_buf);
+ voice_chat_analysis(voice_chat_client_buf);
+ }
+ }
+
+ // 定期检查资源状态
+ if (retry_count++ % 100 == 0) {
+ ai_update_memory_usage(0);
+ }
+
+ msleep(1); // 避免过度占用CPU
+ }
+
+ bk_websocket_tts_tick = 0;
+ close_websocket_client(voice_chat_client);
+ voice_chat_client = NULL;
+
+ // 释放voice_chat_client_buf内存
+ AI_SAFE_FREE(voice_chat_client_buf);
+ ai_update_memory_usage(-1024 * 4);
+ }
+
+voice_chat_cleanup_and_exit:
+ AI_LOCK();
+ is_bk_websocket_voice_chat = 0;
+ AI_UNLOCK();
+
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "Voice chat WebSocket ended");
+ return;
+}
+
+errcode_t voice_chat_server_init(void)
+{
+ extern WebSocketClient *tts_client;
+ extern uint8_t is_bk_websocket_tts;
+
+ if(tts_client!=NULL || is_bk_websocket_tts)
+ {
+ return ERRCODE_FAIL;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"voice_chat_server_init !\r\n");
+ app_task_definition_t taskcfg={0};
+ taskcfg.create_queue_handle = NULL;
+ taskcfg.attr.name="voice_chat_server_init";
+ taskcfg.attr.priority = TASK_PRIORITY_APP;
+ taskcfg.attr.stack_size = 0x2000;
+ taskcfg.func = voice_chat_websocket;
+ taskcfg.use_mem = USE_EXTEND_MEM;
+
+ errcode_t ret=uapi_create_osThread_custom(&taskcfg);
+
+ if(ret != ERRCODE_SUCC)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"create voice_chat_service_file failed %d!\r\n",ret);
+ return ERRCODE_FAIL;
+ }
+ return ERRCODE_SUCC;
+}
\ No newline at end of file
--- /dev/null
+#ifndef VOICECHAT_WEBSOCKET_SERVER_H
+#define VOICECHAT_WEBSOCKET_SERVER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "bk_websocket.h"
+#include "common_def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// VoiceChat server state enum
+typedef enum
+{
+ AI_STATE_0=0,
+ Listening,
+ Responding,
+ Thinking,
+} AI_VOICE_STATE;
+
+// External function declarations
+extern void bluetooth_get_local_addr(unsigned char *addr, int len);
+extern int active_md5_buf(char *input, char *output);
+extern void uapi_cat1_keep_wakeup(uint8_t state);
+extern void set_AIStreamend(uint8_t state);
+extern void startAudioCaptureTask(void);
+extern void stop_AudioCaptureProcess_AI(void);
+extern int send_msg_data_into_Ai_msg_queue(void *data, uint16_t len, uint8_t type);
+extern errcode_t uapi_create_osThread_custom(app_task_definition_t *task_cfg);
+
+// VoiceChat server global variables access functions
+extern WebSocketClient* get_voice_chat_client(void);
+extern uint8_t get_is_bk_websocket_voice_chat(void);
+extern void set_is_bk_websocket_voice_chat(uint8_t flag);
+extern char* get_dialog_id_str_buf(void);
+extern uint8_t get_ai_voice_chat_state(void);
+extern void set_ai_voice_chat_state(uint8_t state);
+
+// VoiceChat server functions
+extern void ai_asr_send_bin(uint8_t *buf, uint32_t len);
+extern bool start_ai_voice_chat(void);
+extern void start_send_voice(void);
+extern void back_voice_RequestToSpeak(void);
+extern bool voice_chat_analysis(char *buf);
+extern void voice_chat_websocket(void *data);
+extern errcode_t voice_chat_server_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // VOICECHAT_WEBSOCKET_SERVER_H
\ No newline at end of file
--- /dev/null
+#ifndef WS_QUEUE_COMMON_H
+#define WS_QUEUE_COMMON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include "bk_websocket.h"
+#include "osal_msgqueue.h"
+#include "cmsis_os2.h"
+#include "wearable_log.h"
+#include "soc_osal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* WebSocket消息队列管理函数 */
+
+/**
+ * @brief 初始化WebSocket消息队列
+ */
+static inline int ws_queue_init(ws_message_queue_t* queue, uint32_t queue_size)
+{
+ if (!queue) return -1;
+
+ memset(queue, 0, sizeof(ws_message_queue_t));
+
+ /* 创建osal消息队列 */
+ uint32_t true_queue_size;
+ if(queue_size != 0)
+ true_queue_size = queue_size;
+ else
+ true_queue_size = WS_QUEUE_SIZE;
+
+ int ret = osal_msg_queue_create("ws_queue", true_queue_size, &queue->queue_id, 0, sizeof(ws_message_item_t));
+ if (ret != OSAL_SUCCESS) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to create osal message queue for ws queue");
+ return -1;
+ }
+
+ queue->initialized = true;
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "WebSocket queue initialized successfully");
+ return 0;
+}
+
+/**
+ * @brief 反初始化WebSocket消息队列
+ */
+static inline void ws_queue_deinit(ws_message_queue_t* queue)
+{
+ if (!queue || !queue->initialized) return;
+ queue->initialized = false;
+
+ if (queue->queue_id != 0) {
+ osal_msg_queue_delete(queue->queue_id);
+ queue->queue_id = 0;
+ }
+
+ memset(queue, 0, sizeof(ws_message_queue_t));
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "WebSocket queue deinitialized");
+}
+
+/**
+ * @brief 向队列中添加消息
+ */
+static inline int ws_queue_put(ws_message_queue_t* queue, const uint8_t* data, uint16_t length, WebSocketMessageType type)
+{
+ if (!queue || !queue->initialized || !data || length == 0 || length > WS_MESSAGE_MAX_SIZE) {
+ return -1;
+ }
+
+ /* 创建消息项 */
+ ws_message_item_t item;
+ item.data = (uint8_t*)malloc(length);
+ if (item.data == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to malloc message data");
+ return -1;
+ }
+
+ memcpy(item.data, data, length);
+ item.length = length;
+ item.type = type;
+ item.timestamp = osKernelGetTickCount();
+ item.in_use = true;
+
+ /* 将消息写入osal消息队列 */
+ int ret = osal_msg_queue_write_copy(queue->queue_id, &item, sizeof(ws_message_item_t), 0);
+ if (ret != OSAL_SUCCESS) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to write message to osal queue");
+ free(item.data);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief 从队列中获取消息
+ */
+static inline int ws_queue_get(ws_message_queue_t* queue, uint8_t* data, uint16_t max_length, WebSocketMessageType* type, uint32_t timeout_ms)
+{
+ if (!queue || !queue->initialized || !data || !type) {
+ return -1;
+ }
+
+ /* 从osal消息队列读取消息 */
+ ws_message_item_t item;
+ unsigned int buffer_size = sizeof(ws_message_item_t);
+ int ret = osal_msg_queue_read_copy(queue->queue_id, &item, &buffer_size, timeout_ms);
+ if (ret != OSAL_SUCCESS) {
+ return 0; /* 超时或错误 */
+ }
+
+ /* 检查消息是否有效 */
+ if (!item.in_use || item.data == NULL) {
+ return 0;
+ }
+
+ /* 复制数据 */
+ uint16_t copy_len = (item.length > max_length) ? max_length : item.length;
+ memcpy(data, item.data, copy_len);
+ *type = item.type;
+
+ /* 释放消息数据 */
+ free(item.data);
+
+ return copy_len;
+}
+
+static inline int ws_queue_cleanup(ws_message_queue_t* queue) {
+ if (!queue) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ws_queue_cleanup: queue is NULL");
+ return -1;
+ }
+
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "开始清理WebSocket消息队列...");
+
+ // 清空osal消息队列中的所有消息
+ ws_message_item_t item;
+ unsigned int buffer_size = sizeof(ws_message_item_t);
+ while (osal_msg_queue_read_copy(queue->queue_id, &item, &buffer_size, 0) == OSAL_SUCCESS) {
+ if (item.data != NULL) {
+ free(item.data);
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "清理队列消息: type=%d, length=%d", item.type, item.length);
+ }
+ }
+
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "WebSocket消息队列清理完成");
+ return 0;
+}
+
+/**
+ * @brief 从队列中接收WebSocket消息(替代原来的receive_websocket_message)
+ */
+static inline int receive_websocket_message_from_queue(ws_message_queue_t* queue, uint8_t* buffer, uint16_t buffer_size, WebSocketMessageType* type, uint32_t timeout_ms)
+{
+ if (!queue || !buffer || !type) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "!queue || !buffer || !type");
+ return -1;
+ }
+
+ return ws_queue_get(queue, buffer, buffer_size, type, timeout_ms);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WS_QUEUE_COMMON_H
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+// #include "../tcpmanager/tcp_simple_manager.h" // Removed as not needed in simplified version
+
+#include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h"
+#include "ConfigModelC.h"
+#include "ConfigModel.h"
+#include "wearable_log.h"
+#include "custom_runtime.h"
+#include "osal_timer.h"
+// #include "lwip/apps/sntp.h" // Commented out due to missing header
+
+#include <sys/time.h>
+#include <time.h>
+
+#include "cmsis_os2.h"
+
+// #include "bletest.h" // Commented out due to missing header
+#include "custom_utils.h"
+#include "custom_runtime.h"
+#include "bleANCS.h"
+#include "cat1_tcpServer.h"
+#include "custom_utils.h"
+#include "broadcast_feature.h"
+#include "cmsis_os2.h"
+#include "cat1_ctl.h"
+#include "voice_call_volte_sample_wrapper.h"
+
+#include "audio_capture_wrapper.h"
+// #include <ui_service.h> // Commented out due to missing header
+#include <bleService.h>
+// #include "cat1_ws.h" // Commented out due to missing header
+#include "dev_authentication.h"
+#include "bk_websocket.h"
+#include "voicechat_websocket_server.h"
+
+// Include separate server headers
+#include "asr_websocket_server.h"
+#include "tts_websocket_server.h"
+#include "chat_websocket_server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "cmsis_os2.h"
+#include "bk_websocket.h"
+
+#include <stdint.h>
+#include <stdint.h>
+#include <asm/delay.h>
+// #include "bts_br_gap.h"
+
+
+// 资源保护
+static osMutexId_t g_ai_mutex = NULL;
+
+
+/**
+ * @brief 更合理判断字符串是否为JSON语句(只判断前20字节,忽略前导空白,要求以'{'或'['开头,且'{'后20字节内必须有'"',以判断为对象属性)
+ * @param str 字符串指针
+ * @param len 字符串长度
+ * @return 如果字符串看起来像JSON,则返回true,否则返回false
+ */
+static bool ai_is_json_string(const char *str, size_t len)
+{
+ if (!str || len == 0) return false;
+ size_t max_check = len < 20 ? len : 20;
+ size_t i = 0;
+ // 跳过前导空白
+ while (i < max_check && (str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\r')) {
+ i++;
+ }
+ if (i >= max_check) return false;
+
+ char start = str[i];
+ if (start == '{') {
+ // 对象类型,20字节内必须有'"'
+ for (size_t j = i + 1; j < len && j < max_check; ++j) {
+ if (str[j] == '"') {
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+}
+
+
+#define AI_RESULT_STR_LEN 512
+
+extern bool read_dev_token(char *data_token);
+
+errcode_t ai_system_init(void);
+
+// 外部函数声明
+extern void bluetooth_get_local_addr(unsigned char *addr, int len);
+extern int active_md5_buf(char *input, char *output);
+extern void uapi_cat1_keep_wakeup(uint8_t state);
+extern void set_AIStreamend(uint8_t state);
+extern void set_clear_queue_stream(uint8_t state);
+extern void startAudioCaptureTask(void);
+extern void stop_AudioCaptureProcess_AI(void);
+extern void ble_set_ai_Question_str(char *str);
+extern void ble_set_ai_Answer_str(char *str, uint8_t type);
+extern void SendMsgToUIService(uint32_t msg_id, uint32_t param, void *data, uint32_t len);
+extern int send_msg_data_into_Ai_msg_queue(void *data, uint16_t len, uint8_t type);
+extern bool aut_login(void);
+extern void get_dev_id(char *device_id);
+extern void AIStreamFlush(void);
+extern void AIStreamWrite(uint8_t *buf, uint32_t len);
+
+static osTimerId_t g_timer_time_sync = NULL;
+
+static uint8_t server_state = 0;
+uint8_t disconnect_server = 0;
+static uint8_t type_ai = 0;
+
+// External server init functions - now implemented in separate files
+extern errcode_t chat_server_init(void);
+extern errcode_t tts_server_init(void);
+extern errcode_t asr_server_init(void);
+
+// External server deinit functions
+extern void asr_server_deinit(void);
+extern void chat_server_deinit(void);
+extern void tts_server_deinit(void);
+
+// External TTS functions
+extern int32_t uapi_tts_paly_init(void);
+extern int32_t uapi_tts_paly_stop(void);
+
+// External ASR functions
+extern void uapi_start_asr(void);
+
+// External TTS functions
+extern bool uapi_start_tts(void);
+
+static uint8_t is_init_ai = 1;
+
+bool init_ai_task(uint8_t type)
+{
+ if (is_init_ai) {
+ // 初始化AI系统
+ if (ai_system_init() != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to initialize AI system");
+ return false;
+ }
+
+
+ disconnect_server = 0;
+
+ type_ai = type;
+
+ // 初始化TTS播放
+ if (uapi_tts_paly_init() != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to initialize TTS playback");
+ }
+
+ // 初始化TTS服务器
+ if (tts_server_init() != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to initialize TTS server");
+ }
+
+ // 初始化聊天服务器
+ if (chat_server_init() != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to initialize chat server");
+ }
+
+ // 初始化ASR服务器
+ if (asr_server_init() != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to initialize asr server");
+ }
+
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "AI task initialized successfully, type: %d", type);
+ }
+
+ return true;
+}
+
+bool deinit_ai_task(void)
+{
+ if (is_init_ai) {
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "Deinitializing AI task...");
+
+ disconnect_server = 1;
+
+ if (g_timer_time_sync) {
+ osTimerDelete(g_timer_time_sync);
+ g_timer_time_sync = NULL;
+ }
+
+ // 停止TTS播放
+ if (uapi_tts_paly_stop() != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to stop TTS playback");
+ }
+
+ // 清理服务器连接
+ asr_server_deinit();
+ chat_server_deinit();
+ tts_server_deinit();
+
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "AI task deinitialized successfully");
+ }
+
+ return true;
+}
+
+// // 预初始化AI
+// bool pre_AI_init(void)
+// {
+// if(read_dev_token(NULL) == false)
+// {
+// return aut_login();
+// }
+// return true;
+// }
+
+// 初始化AI系统
+errcode_t ai_system_init(void) {
+
+ // 创建互斥锁(检查是否已存在)
+ if (!g_ai_mutex) {
+ g_ai_mutex = osMutexNew(NULL);
+ if (!g_ai_mutex) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to create AI mutex");
+ return ERRCODE_FAIL;
+ }
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "AI mutex created in ai_system_init");
+ } else {
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "AI mutex already exists, reusing");
+ }
+
+ WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "AI system initialized successfully");
+ return ERRCODE_SUCC;
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+// Standard socket headers commented out to avoid conflicts with lwIP
+// #include <sys/socket.h>
+// #include <netinet/in.h>
+// #include <arpa/inet.h>
+// #include <netdb.h>
+
+#include <dirent.h>
+// lwip headers commented out due to compilation issues
+// #include "lwip/netif.h"
+// #include "lwip/init.h"
+// #include "lwip/inet_chksum.h"
+// #include "lwip/dns.h"
+// #include "lwip/err.h"
+// #include "lwip/sockets.h"
+// #include "lwip/sys.h"
+// #include <lwip/netdb.h>
+
+/* 添加缺失的定义 */
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
+// #include <netinet/in.h>
+// #include "lwip/inet.h"
+#include <stdio.h>
+// #include <string.h>
+// #include <sys/param.h>
+#include <string.h>
+
+#include "../../../../../open_source/openharmony/third_party/cJSON/cJSON.h"
+#include "ConfigModelC.h"
+#include "ConfigModel.h"
+#include "wearable_log.h"
+#include "custom_runtime.h"
+#include "osal_timer.h"
+// #include "lwip/apps/sntp.h" // Commented out due to missing header
+
+#include <sys/time.h>
+
+// #include "bletest.h" // Commented out due to missing header
+#include "custom_utils.h"
+#include "custom_runtime.h"
+#include "bleANCS.h"
+#include "custom_utils.h"
+#include "broadcast_feature.h"
+#include "cmsis_os2.h"
+#include "cat1_ctl.h"
+#include "voice_call_volte_sample_wrapper.h"
+
+#include "audio_capture_wrapper.h"
+// #include <ui_service.h> // Commented out due to missing header
+#include <bleService.h>
+#include "zhihui_ws.h"
+#include "dev_authentication.h"
+// #include "tcp_simple_manager.h" // Commented out due to missing header
+
+// 包含新的WebSocket解析器
+#include "websocket_parser_wrapper.h"
+
+#define TEST_AI_WS 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+// Define to avoid timeval redefinition
+#define LWIP_TIMEVAL_PRIVATE 0
+#include "lwip/sockets.h"
+#include "lwip/netdb.h"
+#include "lwip/inet.h"
+#include "cmsis_os2.h"
+#ifdef CONFIG_CRYPTO_MBEDTLS
+#ifdef CONFIG_CRYPTO_MBEDTLS_2
+#include "mbedtls/net.h"
+#else
+#include "mbedtls/net_sockets.h"
+#endif
+#include <mbedtls/ssl.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/error.h>
+#include <mbedtls/x509_crt.h>
+
+#endif
+
+#include "mbedtls/base64.h"
+#include "mbedtls/sha1.h"
+
+
+#define BUFFER_SIZE 1024
+#define WEBSOCKET_KEY_LEN 24
+#define WEBSOCKET_GUID "1234567890123456"
+
+
+#include "bk_websocket.h"
+
+// 函数声明
+static int start_websocket_receive_thread(WebSocketClient* client);
+
+#include <stdint.h>
+#include <stdint.h>
+#include <asm/delay.h>
+
+#if 0
+// WebSocket日志控制宏
+#define WS_LOG_ERROR(fmt, ...) printf("[WS_ERROR] " fmt "\n", ##__VA_ARGS__)
+#define WS_LOG_WARNING(fmt, ...) printf("[WS_WARN] " fmt "\n", ##__VA_ARGS__)
+#define WS_LOG_INFO(fmt, ...) printf("[WS_INFO] " fmt "\n", ##__VA_ARGS__)
+#define WS_LOG_DEBUG(fmt, ...) printf("[WS_DEBUG] " fmt "\n", ##__VA_ARGS__)
+#else
+// 可以通过注释/取消注释来控制日志级别
+#define WS_LOG_ERROR(fmt, ...) printf("[WS_ERROR] " fmt "\n", ##__VA_ARGS__)
+#define WS_LOG_WARNING(fmt, ...) printf("[WS_WARN] " fmt "\n", ##__VA_ARGS__)
+#define WS_LOG_INFO(fmt, ...) printf("[WS_INFO] " fmt "\n", ##__VA_ARGS__)
+#define WS_LOG_DEBUG(fmt, ...)
+#endif
+extern osStatus_t osDelay(uint32_t ticks);
+#define cat1_msleep(ms) LOS_Msleep(ms) /* osDelay(1) : 1ms */
+
+// 旧的解析器函数声明已移除
+
+
+void bk_sleep(uint32_t time)
+{
+ msleep(time);
+}
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+void * aic_zalloc(unsigned int len)
+{
+ return malloc(len);
+}
+
+
+
+// 定义一个结构体来存储解析后的URL部分
+typedef struct {
+ char *protocol;
+ char *host;
+ int port;
+ char *path;
+ char *query;
+ int error;
+ char *error_message;
+} BK_URL;
+
+// 自定义的 strdup 函数,添加错误处理
+char* bk_strdup(const char* str) {
+ if (str == NULL) {
+ return NULL;
+ }
+ size_t len = strlen(str) + 1;
+ char* new_str = (char*)aic_zalloc(len);
+ if (new_str == NULL) {
+ return NULL;
+ }
+ return (char*)memcpy(new_str, str, len);
+}
+
+// 设置错误信息
+void set_url_error(BK_URL *url, const char *error_msg) {
+ url->error = 1;
+ url->error_message = bk_strdup(error_msg);
+ if (!url->error_message) {
+ // 若分配失败,输出提示信息
+ WS_LOG_ERROR("Memory allocation failed for error message.");
+ }
+
+}
+
+// 解析URL的函数
+BK_URL parse_url(const char *url) {
+ WS_LOG_WARNING("parse_url url=%s", url);
+ BK_URL result = {NULL, NULL, 0, NULL, NULL, 0, NULL};
+ char *temp_url_t = bk_strdup(url);
+ char *temp_url = temp_url_t;
+ if (!temp_url) {
+ set_url_error(&result, "Failed to allocate memory for temp_url");
+ return result;
+ }
+
+ // 解析协议
+ char *colon = strchr(temp_url, ':');
+ if (!colon) {
+ set_url_error(&result, "Invalid URL: No protocol specified");
+ free(temp_url_t);
+ return result;
+ }
+ *colon = '\0';
+ result.protocol = bk_strdup(temp_url);
+ if (!result.protocol) {
+ set_url_error(&result, "Failed to allocate memory for protocol");
+ free(temp_url_t);
+ return result;
+ }
+ temp_url = colon + 3; // 跳过 "://"
+
+ // 解析主机和端口
+ char *slash = strchr(temp_url, '/');
+ if (!slash) {
+ set_url_error(&result, "Invalid URL: No path specified");
+ free(temp_url_t);
+ return result;
+ }
+ *slash = '\0';
+ char *at = strchr(temp_url, '@');
+ if (at) {
+ temp_url = at + 1; // 跳过用户名和密码部分
+ }
+ char *colon_port = strchr(temp_url, ':');
+ if (colon_port) {
+ *colon_port = '\0';
+ result.host = bk_strdup(temp_url);
+ if (!result.host) {
+ set_url_error(&result, "Failed to allocate memory for host");
+ free(temp_url_t);
+ return result;
+ }
+ result.port = atoi(colon_port + 1);
+ if (!result.port) {
+ set_url_error(&result, "Invalid port number");
+ free(temp_url_t);
+ return result;
+ }
+ } else {
+ result.host = bk_strdup(temp_url);
+ if (!result.host) {
+ set_url_error(&result, "Failed to allocate memory for host");
+ free(temp_url_t);
+ return result;
+ }
+ if (!strcmp(result.protocol, "http") || !strcmp(result.protocol, "ws")) {
+ result.port = 80;
+ } else if (!strcmp(result.protocol, "https")|| !strcmp(result.protocol, "wss")) {
+ result.port = 443;
+ }
+ }
+ temp_url = slash+1;
+ //printf("parse_url slash=%s\n",temp_url);
+ // 解析路径和查询字符串
+ char *question = strchr(temp_url, '?');
+ if (question) {
+ *question = '\0';
+ result.path = bk_strdup(temp_url);
+ if (!result.path) {
+ set_url_error(&result, "Failed to allocate memory for path");
+ free(temp_url_t);
+ return result;
+ }
+ result.query = bk_strdup(question + 1);
+ if (!result.query) {
+ set_url_error(&result, "Failed to allocate memory for query");
+ free(temp_url_t);
+ return result;
+ }
+ } else {
+ result.path = bk_strdup(temp_url);
+ if (!result.path) {
+ set_url_error(&result, "Failed to allocate memory for path");
+ free(temp_url_t);
+ return result;
+ }
+ result.query = NULL;
+ }
+
+ free(temp_url_t);
+
+ return result;
+}
+
+// 释放URL结构体中分配的内存
+void free_url(BK_URL url) {
+
+ // 释放已分配的内存
+
+ if (url.protocol) {
+ free(url.protocol);
+ url.protocol = NULL;
+ }
+ if (url.host) {
+ free(url.host);
+ url.host = NULL;
+ }
+ if (url.path) {
+ free(url.path);
+ url.path = NULL;
+ }
+ if (url.query) {
+ free(url.query);
+ url.query = NULL;
+ }
+ if (url.error_message) {
+ free(url.error_message);
+ url.error_message = NULL;
+ }
+
+}
+
+// // 仿写 htonll 函数
+static uint64_t htonll(uint64_t hostlonglong) {
+ // 检查主机是否为大端字节序
+ static const union {
+ uint32_t i;
+ char c[4];
+ } test = {0x01000000};
+ if (test.c[0] == 0x01) {
+ // 主机是大端字节序,不需要转换
+ return hostlonglong;
+ }
+
+ // 将 64 位整数拆分为两个 32 位整数
+ uint32_t high = (hostlonglong >> 32) & 0xFFFFFFFF;
+ uint32_t low = hostlonglong & 0xFFFFFFFF;
+
+ // 分别转换两个 32 位整数的字节序
+ high = htonl(high);
+ low = htonl(low);
+
+ // 合并转换后的两个 32 位整数
+ return ((uint64_t)low << 32) | high;
+}
+// // 仿写 ntohll 函数
+static uint64_t ntohll(uint64_t netlonglong) {
+ // 将 64 位整数拆分为两个 32 位整数
+ uint32_t high = (netlonglong >> 32) & 0xFFFFFFFF;
+ uint32_t low = netlonglong & 0xFFFFFFFF;
+
+ // 分别转换两个 32 位整数的字节序
+ high = ntohl(high);
+ low = ntohl(low);
+
+ // 合并转换后的两个 32 位整数
+ return ((uint64_t)high << 32) | low;
+}
+// 生成随机的 WebSocket 密钥
+void generate_websocket_key(char *key) {
+ static const char charset[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ for (int i = 0; i < WEBSOCKET_KEY_LEN - 1; ++i) {
+ key[i] = charset[rand() % (sizeof(charset) - 1)];
+ }
+ key[WEBSOCKET_KEY_LEN] = '\0';
+
+ unsigned char encoded[WEBSOCKET_KEY_LEN * 2];
+ size_t olen;
+ mbedtls_base64_encode(encoded, sizeof(encoded), &olen, (const unsigned char *)key, strlen(key));
+ WS_LOG_WARNING("generate_websocket_key %s %d", encoded, olen);
+
+ if(olen>=(WEBSOCKET_KEY_LEN))
+ olen=WEBSOCKET_KEY_LEN;
+
+ strncpy(key, (const char *)encoded, olen);
+
+ key[olen] = '\0';
+}
+
+// 生成 WebSocket 握手响应密钥
+void generate_accept_key(const char *key, char *accept_key) {
+ char combined[128]={0};
+ strcpy(combined, key);
+ strcat(combined, WEBSOCKET_GUID);
+
+ unsigned char sha1_result[20]={0};
+ mbedtls_sha1((const unsigned char *)combined, strlen(combined), sha1_result);
+
+ unsigned char encoded[64]={0};
+ size_t olen;
+ mbedtls_base64_encode(encoded, sizeof(encoded), &olen, sha1_result, 16);//sizeof(sha1_result));
+ strncpy(accept_key, (const char *)encoded, olen);
+ accept_key[olen] = '\0';
+}
+
+// 发送 WebSocket 握手请求
+/**
+ * @brief 发送 WebSocket 握手请求
+ * @param client 客户端指针
+ * @param headers 头信息
+ * @return 成功返回0,失败返回-1
+ */
+#if (TEST_AI_WS==0)
+int send_handshake(WebSocketClient *client,const char *headers,char *response,int response_len) {
+ char key[WEBSOCKET_KEY_LEN+1];
+ WS_LOG_INFO("send_handshake1");
+
+ if(client==NULL|| response==NULL || response_len<=0)
+ {
+ WS_LOG_ERROR("send_handshake client or response is NULL , or response_len <= 0");
+ return -1;
+ }
+
+ generate_websocket_key(key);
+ char accept_key[64];
+ int ret = 0;
+ generate_accept_key(key, accept_key);
+ WS_LOG_INFO("send_handshake2");
+
+ char *request =( char *)aic_zalloc(BUFFER_SIZE);
+
+ if(request==NULL)
+ {
+ WS_LOG_ERROR("request aic_zalloc error");
+ return -1;
+ }
+
+ memset(request,0,BUFFER_SIZE);
+
+ if(headers!= NULL)
+ {
+ snprintf(request, BUFFER_SIZE,
+ "GET /%s?%s HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Origin: http://%s:%d\r\n"
+ "%s\r\n"
+ "\r\n",
+ client->path,client->query ,client->host,client->port, accept_key,client->host,client->port,headers);
+ }else
+ {
+ snprintf(request, BUFFER_SIZE,
+ "GET /%s?%s HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Origin: http://%s:%d\r\n"
+ "\r\n",
+ client->path,client->query , client->host,client->port, accept_key,client->host,client->port);
+ }
+
+ WS_LOG_WARNING("send_handshake request:\r\n%s", request);
+
+ // 发送WebSocket握手请求并等待响应
+ WS_LOG_INFO("发送WebSocket握手请求...");
+
+ // 发送握手请求
+ int send_ret = lwip_send(client->sockfd, request, strlen(request), 0);
+ if (send_ret < 0) {
+ WS_LOG_ERROR("发送握手请求失败: %d", send_ret);
+ ret = -1;
+ } else {
+ // 接收响应
+ ret = lwip_recv(client->sockfd, response, response_len - 1, 0);
+ if (ret > 0) {
+ response[ret] = '\0'; // 确保字符串结束
+ }
+ }
+
+ if (ret < 0) {
+ WS_LOG_ERROR("WebSocket握手发送失败: %d", ret);
+ ret = -1;
+ } else {
+ WS_LOG_INFO("WebSocket握手发送成功,接收到 %d 字节响应", ret);
+
+ // 检查响应内容
+ if (ret > 0 && response[0] != '\0') {
+ WS_LOG_WARNING("WebSocket %s:\r\n", client->path);
+ WS_LOG_WARNING("WebSocket握手响应:\r\n%s", response);
+
+ // 检查是否包含"HTTP/1.1 101"
+ if (strstr(response, "HTTP/1.1 101") != NULL) {
+ WS_LOG_INFO("WebSocket握手成功!");
+ client->handshake = 1;
+ } else {
+ WS_LOG_ERROR("WebSocket握手失败:响应不是101状态码");
+ ret = -1;
+ }
+ } else {
+ WS_LOG_ERROR("WebSocket握手失败:无响应数据");
+ ret = -1;
+ }
+ }
+
+ free(request);
+ WS_LOG_INFO("send_handshake3 返回: %d", ret);
+ return ret;
+}
+#else
+
+int send_handshake(WebSocketClient *client,const char *headers) {
+ char key[WEBSOCKET_KEY_LEN+1];
+ printf("send_handshake1");
+ generate_websocket_key(key);
+ char accept_key[64];
+ int ret = 0;
+ generate_accept_key(key, accept_key);
+ printf("send_handshake2");
+ char *request =( char *)aic_zalloc(BUFFER_SIZE);
+ if(request==NULL)
+ {
+ printf("request aic_zalloc erro");
+ return -1;
+ }
+
+
+ if(headers!= NULL)
+ {
+ snprintf(request, BUFFER_SIZE,
+ "GET /%s?%s HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Origin: http://%s:%d\r\n"
+ "%s\r\n"
+ "\r\n",
+ client->path,client->query ,client->host,client->port, accept_key,client->host,client->port,headers);
+ }else
+ {
+ snprintf(request, BUFFER_SIZE,
+ "GET /%s?%s HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: %s\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Origin: http://%s:%d\r\n"
+ "\r\n",
+ client->path,client->query , client->host,client->port, accept_key,client->host,client->port);
+ }
+
+ printf("send_handshake request:\r\n%s",request);
+ if (lwip_send(client->sockfd, request, strlen(request), 0) < 0) {
+ printf("send handshake failed");
+ ret = -1;
+ }
+
+ free(request);
+ printf("send_handshake3 %d",ret);
+ return ret;
+}
+#endif
+
+
+// 发送 WebSocket 消息
+int send_websocket_message(WebSocketClient *client, uint8_t *message, size_t message_len, WebSocketMessageType type) {
+
+ int ret_tcp_send_data=0;
+ if(client==NULL||message==NULL )
+ {
+ WS_LOG_ERROR("send_websocket_message client or message is NULL %s %s", client, message);
+ return -1;
+ }
+
+ // 检查连接状态
+ if (client->sockfd < 0) {
+ WS_LOG_ERROR("send_websocket_message: Socket is invalid");
+ return -1;
+ }
+
+ if (client->handshake == 0) {
+ WS_LOG_ERROR("send_websocket_message: WebSocket handshake not completed");
+ return -1;
+ }
+ uint8_t* frame=( uint8_t *)aic_zalloc(message_len + 50);
+ int offset = 0;
+ int ret = -1;
+ if(!frame)
+ {
+ WS_LOG_ERROR("send_websocket_message zalloc error");
+ return -1;
+ }
+ // 第一个字节:FIN = 1, 设置 opcode
+ frame[offset++] = 0x80 | type;
+
+ // 第二个字节:设置掩码位和负载长度
+ if (message_len < 126) {
+
+ frame[offset++] = 0x80 | message_len;
+
+ }
+ else if (message_len < 65536)
+ {
+ frame[offset++] = 0x80 | 126;
+ uint16_t len = htons((uint16_t)message_len);
+ memcpy(frame + offset, &len, 2);
+ offset += 2;
+ } else {
+ frame[offset++] = 0x80 | 127;
+ uint64_t len = htonll(message_len);
+ memcpy(frame + offset, &len, 8);
+ offset += 8;
+ }
+
+ // 生成掩码密钥
+ unsigned char masking_key[4];
+ for (int i = 0; i < 4; ++i) {
+ masking_key[i] = (unsigned char)rand();
+ }
+ memcpy(frame + offset, masking_key, 4);
+ offset += 4;
+
+ // 掩码处理消息
+ for (size_t i = 0; i < message_len; ++i) {
+ frame[offset + i] = message[i] ^ masking_key[i % 4];
+ }
+ offset += message_len;
+
+ // 发送帧
+ WS_LOG_INFO("offset %d", offset);
+
+ // 再次检查socket有效性
+ if (client->sockfd < 0) {
+ WS_LOG_ERROR("send_websocket_message: Socket became invalid before send");
+ ret = -1;
+ goto send_websocket_message_ERROR;
+ }
+
+
+
+ ret_tcp_send_data = lwip_send(client->sockfd, frame, offset, 0);
+ if (ret_tcp_send_data < 0) {
+ WS_LOG_ERROR("send message failed %d", ret_tcp_send_data);
+ ret = -1;
+ goto send_websocket_message_ERROR;
+ }
+ ret = 0;
+
+send_websocket_message_ERROR:
+ bk_sleep(message_len/1000+1);
+ free(frame);
+ // bk_sleep(message_len/1000+1);
+ return ret;
+}
+
+// TCP回调函数已废弃,现在使用标准socket和接收线程
+/*
+void _tcp_on_connect_cb(tcp_handle_t handle, bool success)
+{
+ // 此函数已废弃,现在使用标准socket连接
+}
+*/
+/*
+void _tcp_on_disconnect_cb(tcp_handle_t handle)
+{
+ // 此函数已废弃,现在使用标准socket
+}
+*/
+/*
+void _tcp_on_data_received_cb(tcp_handle_t handle, uint8_t* data, uint16_t datalen)
+{
+ // 此函数已废弃,现在使用接收线程处理数据
+}
+*/
+#if TEST_AI_WS
+
+struct cat1_ws_init_t
+{
+ osSemaphoreId_t dns_done_sem;
+ ip_addr_t ip;
+};
+WebSocketClient *init_websocket_client(const char *url, const char *headers,uint8_t use_dns) {
+ //printf("init_websocket_client 1\n");
+ char *buffer = NULL;
+ WebSocketClient *client =NULL;
+ BK_URL parsed_url = {0};
+ struct sockaddr_in server_addr;
+ ssize_t bytes_received;
+ struct in_addr in_addr_t={0};
+ ip_addr_t ip;
+ struct cat1_ws_init_t ip_sem={0};
+ int opt = 1;
+ int sockfd = -1;
+
+ int ret=0;
+
+ parsed_url = parse_url(url);//解析url
+
+ if (parsed_url.error) {
+ printf("init_websocket_client Error: %s", parsed_url.error_message);
+ goto client_ERROR;
+ } else {
+ printf("Protocol: %s", parsed_url.protocol);
+ printf("Host: %s\n", parsed_url.host);
+ printf("Port: %d\n", parsed_url.port);
+ printf("Path: %s\n", parsed_url.path);
+ printf("Query: %s\n", parsed_url.query);
+
+ }
+ client = (WebSocketClient *)aic_zalloc(sizeof(WebSocketClient)) ;
+ if(!client)
+ {
+ printf("init_websocket_client alloc error");
+ goto client_ERROR;
+ }
+
+
+
+ sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ client->sockfd=sockfd;
+
+ if (sockfd < 0) {
+
+ printf("socket creation failed %d errno: %d\r\n",sockfd,errno);
+ goto client_ERROR;
+ }
+ lwip_setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const void*)&opt, sizeof(opt));
+ lwip_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,(const void*)&opt, sizeof(opt));
+ // printf("init_websocket_client 3\n");
+ if(use_dns)
+ {
+ // ip_sem.dns_done_sem= osSemaphoreNew(1, 0, NULL);
+ // if(ip_sem.dns_done_sem==0)
+ // {
+ // goto client_ERROR;
+ // }
+
+ // ret =dns_gethostbyname(parsed_url.host,&ip,dns_found,&ip_sem);
+ // // if(ERR_OK==ret)
+ // {
+ // osSemaphoreAcquire(ip_sem.dns_done_sem, 1000*30);
+ // }
+ // osSemaphoreDelete(ip_sem.dns_done_sem);
+ // printf("dns_gethostbyname %d\r\n",ip_sem.ip.u_addr.ip4);
+ // memcpy(&server_addr.sin_addr.s_addr, &ip_sem.ip.u_addr.ip4, 4);
+
+ }
+ else
+ {
+ inet_pton(AF_INET, parsed_url.host, &server_addr.sin_addr);
+ }
+
+
+
+ server_addr.sin_family = AF_INET;
+
+ server_addr.sin_port = htons(parsed_url.port);
+
+
+ //
+
+ if (lwip_connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
+ printf("init_websocket_client connection failed\r\n");
+ msleep(100);
+ close(sockfd);
+
+ goto client_ERROR;
+
+ }
+ printf("socket creation succ\r\n");
+
+
+ strcpy(client->host, parsed_url.host);
+ strcpy(client->path, parsed_url.path);
+ strcpy(client->query, parsed_url.query);
+ client->port = parsed_url.port;
+
+
+ if (send_handshake(client,headers) < 0) { //发送头
+ printf("socket send_handshake client_ERROR");
+
+ close(sockfd);
+ goto client_ERROR;
+ }
+
+ buffer=(char *)aic_zalloc(BUFFER_SIZE);
+
+ if(buffer==NULL)
+ {
+ printf("aic_zalloc(BUFFER_SIZE);null ");
+ goto client_ERROR;
+ }
+ printf("recv: ??? ");
+
+ bytes_received = lwip_recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
+
+ printf("recv; ");
+
+ if (bytes_received > 0) {
+ buffer[bytes_received] = '\0';
+ printf("Received handshake response:\n%s\n", buffer);
+ }
+
+
+
+ if(strstr(buffer,"HTTP/1.1 101") == NULL)
+ {
+
+ goto client_ERROR;
+ }
+ printf("init_websocket_client end 1");
+
+
+ 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:
+ WS_LOG_ERROR("init_websocket_client Error: client_ERROR");
+ if(buffer) {
+ free(buffer);
+ buffer=NULL;
+ }
+ if(client){
+ free(client);
+ client = NULL;
+ }
+ free_url(parsed_url);
+
+ return NULL;
+}
+#else
+// 初始化 WebSocket 客户端
+WebSocketClient *init_websocket_client(const char *url, const char *headers,uint8_t use_dns) {
+ //printf("init_websocket_client 1\n");
+ WebSocketClient *client = NULL;
+ BK_URL parsed_url = {0};
+ int opt = 1;
+ int ret = 0;
+ int opt_val = 1;
+ struct sockaddr_in server_addr;
+ struct hostent *host_entry;
+ int connect_result;
+ fd_set write_fds;
+ struct timeval timeout;
+ int select_result;
+ int error = 0;
+ socklen_t len = sizeof(error);
+ int flags;
+
+ if (!url) {
+ WS_LOG_ERROR("init_websocket_client Error: URL is NULL");
+ return NULL;
+ }
+
+ parsed_url = parse_url(url);//解析url
+
+ if (parsed_url.error) {
+ WS_LOG_ERROR("init_websocket_client Error: %s", parsed_url.error_message);
+ goto client_ERROR;
+ } else {
+ WS_LOG_INFO("Protocol: %s", parsed_url.protocol);
+ WS_LOG_INFO("Host: %s", parsed_url.host);
+ WS_LOG_INFO("Port: %d", parsed_url.port);
+ WS_LOG_INFO("Path: %s", parsed_url.path);
+ WS_LOG_INFO("Query: %s", parsed_url.query);
+ }
+
+ client = (WebSocketClient *)aic_zalloc(sizeof(WebSocketClient)) ;
+ memset_s(client,sizeof(WebSocketClient),0,sizeof(WebSocketClient));
+ if(!client)
+ {
+ WS_LOG_ERROR("init_websocket_client alloc error: Failed to allocate memory for WebSocketClient");
+ goto client_ERROR;
+ }
+
+ // 使用标准 socket 替代 TCP 管理器
+ client->sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ if (client->sockfd < 0) {
+ WS_LOG_ERROR("init_websocket_client Error: Socket creation failed");
+ goto client_ERROR;
+ }
+
+ // 设置 socket 选项
+ 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);
+
+ // 解析主机地址
+ memset(&server_addr, 0, sizeof(server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(parsed_url.port);
+
+ // DNS 解析或直接使用 IP
+ if (use_dns || !inet_aton(parsed_url.host, &server_addr.sin_addr)) {
+ host_entry = lwip_gethostbyname(parsed_url.host);
+ if (host_entry == NULL) {
+ WS_LOG_ERROR("init_websocket_client Error: DNS resolution failed for %s", parsed_url.host);
+ goto client_ERROR;
+ }
+ memcpy(&server_addr.sin_addr, host_entry->h_addr_list[0], host_entry->h_length);
+ }
+
+ // 连接到服务器
+ 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;
+ }
+
+ // 等待连接完成
+ if (connect_result == 0 || errno == EINPROGRESS) {
+ FD_ZERO(&write_fds);
+ FD_SET(client->sockfd, &write_fds);
+ timeout.tv_sec = 10; // 10秒超时
+ timeout.tv_usec = 0;
+
+ select_result = lwip_select(client->sockfd + 1, NULL, &write_fds, NULL, &timeout);
+ if (select_result > 0 && FD_ISSET(client->sockfd, &write_fds)) {
+ // 检查连接是否成功
+ if (lwip_getsockopt(client->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error == 0) {
+ WS_LOG_INFO("Socket connected successfully");
+
+ // 发送 WebSocket 握手
+ char *buffer = (char *)aic_zalloc(BUFFER_SIZE);
+ if (buffer != NULL) {
+ int bytes_received = send_handshake(client, NULL, buffer, BUFFER_SIZE);
+ if (bytes_received >= 0) {
+ client->handshake = 1;
+ WS_LOG_INFO("WebSocket handshake completed");
+ } else {
+ WS_LOG_ERROR("WebSocket handshake failed");
+ }
+ free(buffer);
+ }
+ } else {
+ WS_LOG_ERROR("Socket connection failed with error: %d", error);
+ goto client_ERROR;
+ }
+ } else {
+ WS_LOG_ERROR("Socket connection timeout or failed");
+ goto client_ERROR;
+ }
+ }
+
+ // 设置回阻塞模式用于后续通信
+ flags = fcntl(client->sockfd, F_GETFL, 0);
+ fcntl(client->sockfd, F_SETFL, flags & ~O_NONBLOCK);
+ WS_LOG_INFO("socket creation success");
+
+ strcpy(client->host, parsed_url.host);
+ strcpy(client->path, parsed_url.path);
+ strcpy(client->query, parsed_url.query);
+ client->port = parsed_url.port;
+
+ /* 初始化WebSocket解析器 */
+ client->parser = ws_parser_init();
+ if (!client->parser) {
+ WS_LOG_ERROR("init_websocket_client Error: Failed to initialize WebSocket parser");
+ goto client_ERROR;
+ }
+
+ /* 启动数据接收线程 */
+ WS_LOG_INFO("WebSocket客户端初始化完成,启动数据接收线程");
+
+ // 启动数据接收线程
+ if (start_websocket_receive_thread(client) != 0) {
+ WS_LOG_ERROR("Failed to start WebSocket receive thread");
+ goto client_ERROR;
+ }
+
+// if(strstr(buffer,"HTTP/1.1 101") == NULL)
+ // {
+
+ // goto client_ERROR;
+ // }
+ // printf("init_websocket_client end 1");
+ // free(buffer);
+ free_url(parsed_url);
+
+ WS_LOG_INFO("init_websocket_client end 2");
+ return client;
+
+client_ERROR:
+ WS_LOG_ERROR("init_websocket_client Error: Initialization failed, cleaning up...");
+ if(client){
+ free(client);
+ client = NULL;
+ }
+ free_url(parsed_url);
+
+ return NULL;
+}
+#endif
+
+// 关闭 WebSocket 客户端
+void close_websocket_client(WebSocketClient *client) {
+ if(client)
+ {
+ // 使用标准 socket 关闭替代 TCP 管理器
+
+ WS_LOG_INFO("close_websocket_client %s %s", client->host, client->path);
+
+ // 先关闭socket连接,避免数据继续流入
+ if (client->sockfd >= 0) {
+ close(client->sockfd);
+ client->sockfd = -1;
+ }
+
+ // 释放WebSocket解析器
+ if (client->parser) {
+ ws_parser_destroy(client->parser);
+ client->parser = NULL;
+ }
+
+ // 清理客户端状态
+ client->handshake = 0;
+ client->packet_callback = NULL;
+
+ free(client);
+ }
+}
+
+// 旧的WebSocket解析器函数已移除,现在使用新的解析器
+
+/**
+ * @brief 设置WebSocket数据包回调函数
+ */
+void ws_set_packet_callback(WebSocketClient *client, on_ws_packet_callback callback)
+{
+ if (!client) return;
+
+ client->packet_callback = callback;
+}
+
+/**
+ * @brief 处理接收到的原始数据
+ */
+int ws_process_received_data(WebSocketClient *client, const uint8_t *data, size_t len)
+{
+ if (!client || !data || len == 0 || !client->parser) {
+ WS_LOG_ERROR("ws_process_received_data: invalid parameters - client=%p, data=%p, len=%zu, parser=%p",
+ client, data, len, client->parser);
+ return -1;
+ }
+
+ // ========== 新增:打印收到的所有原始数据 ==========
+ // printf("\n=== WebSocket Raw Data Received ===\n");
+ // printf("Client: %s:%s\n", client->host, client->path);
+ // WS_LOG_WARNING("Total bytes: %zu", len);
+ // printf("Raw data (hex): ");
+ // // 打印所有数据的十六进制表示
+ // for (size_t i = 0; i < len; i++) {
+ // printf("%02X ", data[i]);
+ // // 每16字节换行,提高可读性
+ // if ((i + 1) % 16 == 0) {
+ // printf("\n ");
+ // }
+ // }
+ // printf("\n");
+
+ // // 打印ASCII表示(可打印字符)
+ // printf("Raw data (ASCII): ");
+ // for (size_t i = 0; i < len; i++) {
+ // if (data[i] >= 32 && data[i] <= 126) {
+ // printf("%c", data[i]);
+ // } else if (data[i] == '\r') {
+ // printf("\\r");
+ // } else if (data[i] == '\n') {
+ // printf("\\n");
+ // } else if (data[i] == '\t') {
+ // printf("\\t");
+ // } else {
+ // printf(".");
+ // }
+ // }
+ // printf("\n");
+
+ // 打印数据统计信息
+ // printf("Data analysis:\n");
+ // printf(" - Total bytes: %zu\n", len);
+ // printf(" - First byte: 0x%02X\n", len > 0 ? data[0] : 0);
+ // printf(" - Last byte: 0x%02X\n", len > 0 ? data[len-1] : 0);
+
+ // 检查是否可能是WebSocket帧头
+ // if (len >= 2) {
+ // uint8_t first_byte = data[0];
+ // uint8_t second_byte = data[1];
+ // uint8_t fin = (first_byte >> 7) & 0x1;
+ // uint8_t rsv = (first_byte >> 4) & 0x7;
+ // uint8_t opcode = first_byte & 0x0F;
+ // uint8_t mask = (second_byte >> 7) & 0x1;
+ // uint8_t payload_len = second_byte & 0x7F;
+
+ // printf(" - Potential WebSocket frame:\n");
+ // printf(" * FIN: %d\n", fin);
+ // printf(" * RSV: %d%d%d\n", (rsv >> 2) & 1, (rsv >> 1) & 1, rsv & 1);
+ // printf(" * Opcode: 0x%02X\n", opcode);
+ // printf(" * Mask: %d\n", mask);
+ // printf(" * Payload length: %d\n", payload_len);
+
+ // // 验证opcode是否有效
+ // bool valid_opcode = ((opcode <= 0x2) || (opcode >= 0x8 && opcode <= 0xA));
+ // printf(" * Valid opcode: %s\n", valid_opcode ? "YES" : "NO");
+
+ // // 验证RSV位
+ // bool valid_rsv = (rsv == 0);
+ // printf(" * Valid RSV: %s\n", valid_rsv ? "YES" : "NO");
+ // }
+ // WS_LOG_WARNING("=====================================");
+
+ ws_parser_t* parser = client->parser;
+ int total_processed = 0;
+ const uint8_t* current_data = data;
+ size_t remaining_len = len;
+
+ WS_LOG_DEBUG("ws_process_received_data: processing %zu bytes for client %s:%s",
+ len, client->host, client->path);
+
+ // 循环处理数据,支持连续帧
+ size_t original_len = remaining_len;
+ size_t loop_count = 0;
+ const size_t max_loops = original_len + 10; // 防止无限循环
+
+ while (remaining_len > 0 && loop_count < max_loops) {
+ loop_count++;
+ WS_LOG_DEBUG("ws_process_received_data: processing remaining %d bytes, current offset: %zu, loop %d",
+ remaining_len, current_data - data, loop_count);
+
+ // 在解析前打印当前解析器状态
+ WS_LOG_DEBUG("ws_process_received_data: parser state before parsing:");
+ // ws_parser_debug_state(parser);
+
+ // 使用新的解析器处理当前数据段
+ int result = ws_parse_stream(parser, current_data, remaining_len);
+
+ if(result <= 0)
+ {
+ WS_LOG_ERROR("ws_process_received_data: parsing error, reset parser,find next frame. Remaining: %zu bytes", remaining_len);
+ ws_parser_reset(parser);
+ // 不要立即返回,尝试跳过错误数据继续处理
+ current_data++;
+ remaining_len--;
+ continue;
+ }
+
+ // 更新处理进度
+ total_processed += result;
+ current_data += result;
+ remaining_len -= result;
+
+ // 在解析后打印解析器状态
+ WS_LOG_DEBUG("ws_process_received_data: parser state after parsing:");
+ // ws_parser_debug_state(parser);
+ ws_parse_state_t ws_stat = ws_parser_get_state(parser);
+
+ if (ws_stat == WS_STATE_COMPLETE) {
+ // 解析完成一个完整的WebSocket帧
+ const ws_header_t* header = ws_get_header(parser);
+ const uint8_t* payload = ws_get_payload(parser);
+ size_t payload_len = ws_get_payload_length(parser);
+
+ WS_LOG_DEBUG("ws_process_received_data: frame complete, total bytes: %d, payload bytes: %zu %s",
+ result, payload_len,client->path);
+
+ if (header && payload) {
+ WS_LOG_DEBUG("WebSocket frame received: opcode=0x%02X, fin=%d, payload_len=%llu",
+ header->opcode, header->fin, header->payload_len);
+ // printf("payload = %s\n",payload);
+
+ // 打印载荷数据的前几个字节(用于调试)
+ // if (payload_len > 0) {
+ // printf("Payload data (first 32 bytes): ");
+ // for (size_t i = 0; i < payload_len && i < 32; i++) {
+ // printf("%02X ", payload[i]);
+ // }
+ // if (payload_len > 32) printf("...");
+ // printf("\n");
+ // }
+
+ // 根据操作码处理不同类型的消息
+ switch (header->opcode) {
+ case WS_PING:
+ WS_LOG_WARNING("Received WebSocket PING");
+ // TODO: 自动回复PONG
+ send_websocket_message(client,( uint8_t *)"PONG",strlen("PONG"),WS_PONG);
+ break;
+
+ case WS_PONG:
+ WS_LOG_WARNING("Received WebSocket PONG");
+ break;
+
+ case WS_CLOSE:
+ WS_LOG_WARNING("Received WebSocket CLOSE");
+ // 调用回调函数处理关闭帧
+ if (client->packet_callback) {
+ client->packet_callback((uint8_t*)payload, payload_len, (WebSocketMessageType)WS_CLOSE);
+ }
+ break;
+
+ case WS_TEXT:
+ case WS_BINARY:
+ case WS_CONTINUATION:
+ // 调用回调函数处理数据包
+ if (client->packet_callback) {
+ WebSocketMessageType ws_type = (header->opcode == WS_TEXT) ? (WebSocketMessageType)WS_TEXT :
+ (header->opcode == WS_BINARY) ? (WebSocketMessageType)WS_BINARY : (WebSocketMessageType)WS_CONT;
+ client->packet_callback((uint8_t*)payload, payload_len, ws_type);
+ }
+
+ break;
+
+ default:
+ WS_LOG_DEBUG("Unknown WebSocket opcode: 0x%02X", header->opcode);
+ break;
+ }
+
+ // 重要:在数据处理完成后再重置解析器状态
+ // 这样可以确保数据被正确处理后再清理状态
+ WS_LOG_DEBUG("ws_process_received_data: data processing complete, resetting parser");
+ ws_parser_reset(parser);
+
+
+
+ WS_LOG_DEBUG("ws_process_received_data: frame processed, moving to next frame. Remaining: %d bytes", remaining_len);
+
+ } else {
+ WS_LOG_DEBUG("ws_process_received_data: invalid payload data - header=%p, payload=%p, payload_len=%zu",
+ header, payload, payload_len);
+ return -1;
+ }
+
+ } else if (ws_stat == WS_STATE_ERROR) {
+ // 解析错误,重置parser
+ ws_parser_reset(parser);
+
+ WS_LOG_ERROR("ws_process_received_data: ws_stat == WS_STATE_ERROR parsing error, find next frame. Remaining: %zu bytes", remaining_len);
+
+ } else {
+ // 数据不完整,继续等待更多数据
+ WS_LOG_DEBUG("ws_process_received_data: frame incomplete, waiting for more data");
+ }
+ }
+
+ // 检查是否达到循环限制
+ if (loop_count >= max_loops) {
+ WS_LOG_ERROR("ws_process_received_data: reached maximum loop count %zu, forcing exit", max_loops);
+ ws_parser_reset(parser);
+ }
+
+ WS_LOG_DEBUG("ws_process_received_data: total processed: %d bytes", total_processed);
+ return total_processed;
+}
+
+// 多余的函数已移除,现在只使用一个统一的解析器
+
+/**
+ * @brief WebSocket数据接收线程
+ */
+static void* websocket_receive_thread(void* arg) {
+ WebSocketClient* client = (WebSocketClient*)arg;
+ if (!client || client->sockfd < 0) {
+ WS_LOG_ERROR("websocket_receive_thread: invalid client");
+ return NULL;
+ }
+
+ WS_LOG_INFO("WebSocket receive thread started for %s:%s", client->host, client->path);
+
+ uint8_t buffer[4096]; // 接收缓冲区
+
+ while (client->sockfd >= 0) {
+ // 使用 select 检查数据可用性
+ fd_set read_fds;
+ struct timeval timeout;
+ FD_ZERO(&read_fds);
+ FD_SET(client->sockfd, &read_fds);
+ timeout.tv_sec = 1; // 1秒超时
+ timeout.tv_usec = 0;
+
+ int select_result = lwip_select(client->sockfd + 1, &read_fds, NULL, NULL, &timeout);
+ if (select_result < 0) {
+ WS_LOG_ERROR("websocket_receive_thread: select error");
+ break;
+ }
+
+ if (select_result == 0) {
+ // 超时,继续循环
+ continue;
+ }
+
+ if (FD_ISSET(client->sockfd, &read_fds)) {
+ // 有数据可读
+ int bytes_received = lwip_recv(client->sockfd, buffer, sizeof(buffer), 0);
+ if (bytes_received <= 0) {
+ WS_LOG_ERROR("websocket_receive_thread: connection closed or error");
+ break;
+ }
+
+ // 处理接收到的数据
+ int processed = ws_process_received_data(client, buffer, bytes_received);
+ if (processed < 0) {
+ WS_LOG_ERROR("websocket_receive_thread: data processing failed");
+ // 继续接收,不要因为一次解析失败就退出
+ }
+ }
+ }
+
+ WS_LOG_INFO("WebSocket receive thread ended for %s:%s", client->host, client->path);
+ return NULL;
+}
+
+/**
+ * @brief 启动WebSocket数据接收线程
+ */
+static int start_websocket_receive_thread(WebSocketClient* client) {
+ if (!client) {
+ return -1;
+ }
+
+ // 创建接收线程
+ osThreadAttr_t thread_attr = {0};
+ thread_attr.name = "ws_recv";
+ thread_attr.stack_size = 4096;
+ thread_attr.priority = osPriorityNormal;
+
+ osThreadId_t thread_id = osThreadNew((osThreadFunc_t)websocket_receive_thread, client, &thread_attr);
+ if (thread_id == NULL) {
+ WS_LOG_ERROR("Failed to create WebSocket receive thread");
+ return -1;
+ }
+
+ WS_LOG_INFO("WebSocket receive thread created successfully");
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
\ No newline at end of file
--- /dev/null
+#ifndef ZHIHUI_WS_H_
+#define ZHIHUI_WS_H_
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+////////////////////////// UI 使用接口/////////////////////////////////
+bool init_ai_task(uint8_t type);
+
+bool deinit_ai_task(void);
+
+void uapi_UI_start_asr(uint8_t type);
+
+void uapi_UI_stop_asr(void);
+
+bool uapi_start_tts(void);
+
+void uapi_stop_tts(void);
+
+bool uapi_start_tts_single_txt(char *buf);
+
+void uapi_UI_stop_start_asr_timeout(void);
+
+void ai_cleanup_resources(void);
+typedef enum
+{
+ AI_TYPE_0=0,
+ AI_TYPE_JIU, //九天
+ AI_TYPE_DE, //deepseek
+ AI_TYPE_DOU,//豆包
+ AI_TYPE_WEN,//文心一言
+ AI_TYPE_KIMI,//KIMI
+}G_AI_TYPE;
+
+bool pre_AI_init(void);
+
+
+////////////////////////////////////////////////////////////////////////
+
+void start_send_voice(void);
+
+void tts_server_deinit(void);
+
+void asr_server_deinit(void);
+
+
+void chat_server_deinit(void);
+
+void uapi_set_tts_break(uint8_t data);
+
+uint8_t uapi_get_tts_break(void);
+
+uint8_t get_ai_asr_service_init_flag(void);
+void set_ai_asr_service_init_flag(uint8_t flag);
+uint8_t get_ai_chat_service_init_flag(void);
+void set_ai_chat_service_init_flag(uint8_t flag);
+uint8_t get_ai_tts_server_init_flag(void);
+void set_ai_tts_server_init_flag(uint8_t flag);
+uint32_t get_bk_websocket_tts_tick(void);
+uint8_t get_asr_is_ready_start(void);
+
+void uapi_start_asr_task(void);
+
+void asr_record_test(void);
+
+bool ai_chat_send_txt(uint8_t type,char *msg);
+
+void uapi_start_asr(void);
+
+void uapi_stop_tts(void);
+
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
\ No newline at end of file