From c638caea5c20eedd31b38ff126b70ee75ad99eee Mon Sep 17 00:00:00 2001 From: cheng Date: Sat, 20 Sep 2025 17:06:55 +0800 Subject: [PATCH] =?utf8?q?=E6=8F=90=E4=BE=9B=E5=AF=B9=E8=AF=9D=E5=92=8C?= =?utf8?q?=E6=95=85=E4=BA=8B=E5=BC=80=E5=A7=8B=E7=8A=B6=E6=80=81=E4=B8=8A?= =?utf8?q?=E6=8A=A5=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- code/sdk/zhihui/leiniao/imsl_ln/ln_api.c | 313 +++++++++--------- code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c | 1 + .../leiniao/ln_api_cpp/ln_api_interface.cpp | 6 +- .../leiniao/ln_api_cpp/ln_api_interface.h | 13 +- 4 files changed, 176 insertions(+), 157 deletions(-) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c index 2b900ce41..6eb760d8a 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c @@ -817,6 +817,7 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char #endif /* 非阻塞入队,队列满则忽略该块 */ + //播放下载的音频 (void)ln_audio_player_write((const uint8_t*)client_data.responseBuf, (uint32_t)client_data.contentBlockLen); } @@ -851,22 +852,88 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char * @param segment_index 片段索引 * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败 */ +/* 辅助函数:根据接口类型生成保存目录与文件名 */ +static void ln_build_audio_save_path(int interface_type, int segment_index, int is_error, + char *save_dir, size_t save_dir_len, + char *filename, size_t filename_len) +{ + if (!save_dir || !filename) return; + if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV) + { + (void)snprintf(save_dir, save_dir_len, "%s/speech", LN_DATA_PATH); + } + else if (interface_type == LN_HTTP_RECV_STORY_CHOOSE) + { + (void)snprintf(save_dir, save_dir_len, "%s/story/choose", LN_DATA_PATH); + } + else if (interface_type == LN_HTTP_RECV_STORY_RANDOM) + { + (void)snprintf(save_dir, save_dir_len, "%s/story/random", LN_DATA_PATH); + } + else + { + (void)snprintf(save_dir, save_dir_len, "%s", LN_DATA_PATH); + } + + if (is_error) + { + (void)snprintf(filename, filename_len, "%d_err.mp3", segment_index); + } + else + { + (void)snprintf(filename, filename_len, "%d.mp3", segment_index); + } +} + +/* 辅助函数:提取音频URL */ +static const char *ln_get_audio_url_for_interface(cJSON *data, int interface_type) +{ + if (!data) return NULL; + if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV) + { + cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl"); + if (audioUrl && cJSON_IsString(audioUrl)) return audioUrl->valuestring; + return NULL; + } + if (interface_type == LN_HTTP_RECV_STORY_CHOOSE || interface_type == LN_HTTP_RECV_STORY_RANDOM) + { + cJSON *storyAudioUrl = cJSON_GetObjectItem(data, "storyAudioUrl"); + if (storyAudioUrl && cJSON_IsString(storyAudioUrl)) return storyAudioUrl->valuestring; + return NULL; + } + cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl"); + if (audioUrl && cJSON_IsString(audioUrl)) return audioUrl->valuestring; + return NULL; +} + +/* 辅助函数:若存在音频URL则执行下载 */ +static void ln_download_audio_if_present(const char *audio_url, int interface_type, int segment_index, int is_error) +{ + if (!audio_url || strlen(audio_url) == 0) return; + char save_dir[256]; + char filename[128]; + ln_build_audio_save_path(interface_type, segment_index, is_error, save_dir, sizeof(save_dir), filename, sizeof(filename)); + printf("[JSON_PARSE] downloading audio to: %s/%s\n", save_dir, filename); + (void)ln_download_mp3_file_advanced(audio_url, save_dir, filename); +} + +// 解析JSON数据并处理 static uint32_t ln_parse_and_process_json_data(const char *json_data, int interface_type, int segment_index) { if (!json_data) { - printf("[JSON_PARSE] invalid json_data\n"); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] invalid json_data\n"); return ERRCODE_FAIL; } - printf("[JSON_PARSE] parsing JSON data (interface_type=%d, segment=%d): %s\n", + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] parsing JSON data (interface_type=%d, segment=%d): %s\n", interface_type, segment_index, json_data); // 解析JSON数据 cJSON *root = cJSON_Parse(json_data); if (!root) { - printf("[JSON_PARSE] JSON parse failed: %s\n", json_data); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] JSON parse failed: %s\n", json_data); return ERRCODE_FAIL; } @@ -874,175 +941,101 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode"); if (errorCode && !cJSON_IsNull(errorCode)) { - printf("[JSON_PARSE] server error: %s\n", - cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown"); - cJSON_Delete(root); - return ERRCODE_FAIL; - } + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] 失败响应,开始处理数据\n"); + const char *err = cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown"; + cJSON *message = cJSON_GetObjectItem(root, "message"); + const char *msg_str = (message && cJSON_IsString(message)) ? message->valuestring : ""; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] server error: %s, message=%s\n", err, msg_str); - // 获取data字段 - cJSON *data = cJSON_GetObjectItem(root, "data"); - if (!data || !cJSON_IsObject(data)) - { - printf("[JSON_PARSE] no data field found\n"); - cJSON_Delete(root); - return ERRCODE_FAIL; - } - - // 根据接口类型获取音频URL - char *audio_url = NULL; - if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV) - { - // 语音对话接口:使用 audioUrl 字段 - cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl"); - if (audioUrl && cJSON_IsString(audioUrl)) + const char *audio_url_err = NULL; + const char *text_content_err = NULL; + cJSON *data_err = cJSON_GetObjectItem(root, "data"); + if (data_err && cJSON_IsObject(data_err)) { - audio_url = audioUrl->valuestring; + cJSON *audioUrl = cJSON_GetObjectItem(data_err, "audioUrl"); + if (audioUrl && cJSON_IsString(audioUrl)) audio_url_err = audioUrl->valuestring; + cJSON *textContent = cJSON_GetObjectItem(data_err, "textContent"); + if (textContent && cJSON_IsString(textContent)) text_content_err = textContent->valuestring; } - } - else if (interface_type == LN_HTTP_RECV_STORY_CHOOSE || interface_type == LN_HTTP_RECV_STORY_RANDOM) - { - // 故事接口:使用 storyAudioUrl 字段 - cJSON *storyAudioUrl = cJSON_GetObjectItem(data, "storyAudioUrl"); - if (storyAudioUrl && cJSON_IsString(storyAudioUrl)) + + if (text_content_err && strlen(text_content_err) > 0) { - audio_url = storyAudioUrl->valuestring; + // 推送文本到UI + do_ln_http_server_recv_data(interface_type, (char *)text_content_err, (uint32_t)strlen(text_content_err)); } - } - else - { - // 默认尝试 audioUrl 字段 - cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl"); - if (audioUrl && cJSON_IsString(audioUrl)) + + // 下载音频,并播放 + ln_download_audio_if_present(audio_url_err, interface_type, segment_index, 1); + + if (err) { - audio_url = audioUrl->valuestring; + if (strcmp(err, "AUTH_FAILURE") == 0 || strcmp(err, "DEVICE_NOT_FOUND") == 0) + { + // 获取设备Token + do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_GET_DEVICE_TOKEN, NULL, 0); + } + else if (strcmp(err, "MODEL_NOT_FOUND") == 0 || strcmp(err, "VOICE_NOT_FOUND") == 0) + { + // 获取角色配置 + do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_GET_CHARACTER, NULL, 0); + } } - } - // 获取textContent - cJSON *textContent = cJSON_GetObjectItem(data, "textContent"); - char *text_content = NULL; - if (textContent && cJSON_IsString(textContent)) - { - text_content = textContent->valuestring; + cJSON_Delete(root); + return ERRCODE_FAIL; } - // 获取intention - cJSON *intention = cJSON_GetObjectItem(data, "intention"); - char *intention_str = NULL; - if (intention && cJSON_IsString(intention)) - { - intention_str = intention->valuestring; - } + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] 成功响应,开始处理数据\n"); - // 获取storyTheme(故事接口特有) - cJSON *storyTheme = cJSON_GetObjectItem(data, "storyTheme"); - char *story_theme = NULL; - if (storyTheme && cJSON_IsString(storyTheme)) + /* -------------------------------- 获取data字段 -------------------------------- */ + cJSON *data = cJSON_GetObjectItem(root, "data"); + if (!data || !cJSON_IsObject(data)) { - story_theme = storyTheme->valuestring; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] no data field found\n"); + cJSON_Delete(root); + return ERRCODE_FAIL; } - // 获取storyContent(故事接口特有) - cJSON *storyContent = cJSON_GetObjectItem(data, "storyContent"); - char *story_content = NULL; - if (storyContent && cJSON_IsString(storyContent)) - { - story_content = storyContent->valuestring; - } + // 获取音频URL + const char *audio_url = ln_get_audio_url_for_interface(data, interface_type); - // 创建推送数据 - cJSON *push_data = cJSON_CreateObject(); - - // 根据接口类型添加相应字段 - if (text_content && strlen(text_content) > 0) - { - cJSON_AddStringToObject(push_data, "textContent", text_content); - } - - if (story_content && strlen(story_content) > 0) - { - cJSON_AddStringToObject(push_data, "storyContent", story_content); - } - - if (intention_str && strlen(intention_str) > 0) + // 获取文本内容 + cJSON *textContent = cJSON_GetObjectItem(data, "textContent"); + if (textContent && cJSON_IsString(textContent) && strlen(textContent->valuestring) > 0) { - cJSON_AddStringToObject(push_data, "intention", intention_str); + // 推送文本到UI + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] textContent: %s\n", textContent->valuestring); + do_ln_http_server_recv_data(interface_type, textContent->valuestring, (uint32_t)strlen(textContent->valuestring)); } - - if (story_theme && strlen(story_theme) > 0) + else { - cJSON_AddStringToObject(push_data, "storyTheme", story_theme); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] no textContent found\n"); } - // 推送到相应的接收线程 - char *json_string = cJSON_Print(push_data); - if (json_string) + // 识别意图并触发相应业务 + cJSON *intention = cJSON_GetObjectItem(data, "intention"); + if (intention && cJSON_IsString(intention) && intention->valuestring && intention->valuestring[0]) { - // printf("[JSON_PARSE] pushing data to interface_type=%d: %s\n", interface_type, json_string); - // [JSON_PARSE] pushing data to interface_type=3: { - // "textContent": "在剧中,她曾利用香料和药材来达到自己的目的,比如制作迷香等。" - // } - // 解析textContent中字符串发送给ui - cJSON *textContent = cJSON_GetObjectItem(push_data, "textContent"); - if (textContent && cJSON_IsString(textContent)) - { - // 发送开始对话标志 - set_start_conversation_flag(1); - printf("[JSON_PARSE] textContent: %s\n", textContent->valuestring); - do_ln_http_server_recv_data(interface_type, textContent->valuestring, strlen(textContent->valuestring)); - } - else + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] intention: %s\n", intention->valuestring); + if (strcmp(intention->valuestring, "StartSleepMode") == 0) { - printf("[JSON_PARSE] no textContent found\n"); + // 触发故事接口(默认随机故事,无主题) + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] intention: StartSleepMode\n"); + ln_story_random_req_t req; + memset(&req, 0, sizeof(req)); + do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_STORY_RANDOM, (char *)&req, sizeof(req)); } - free(json_string); } - cJSON_Delete(push_data); - // 下载音频文件 + // 下载音频,并播放 if (audio_url && strlen(audio_url) > 0) { - char save_dir[256]; - char filename[128]; - - // 根据接口类型确定保存目录和文件名 - switch (interface_type) - { - case LN_HTTP_SERVICE_CMD_SSE_RECV: - snprintf(save_dir, sizeof(save_dir), "%s/speech", LN_DATA_PATH); - snprintf(filename, sizeof(filename), "%d.mp3", segment_index); - break; - case LN_HTTP_RECV_STORY_CHOOSE: - snprintf(save_dir, sizeof(save_dir), "%s/story/choose", LN_DATA_PATH); - snprintf(filename, sizeof(filename), "%d.mp3", segment_index); - break; - case LN_HTTP_RECV_STORY_RANDOM: - snprintf(save_dir, sizeof(save_dir), "%s/story/random", LN_DATA_PATH); - snprintf(filename, sizeof(filename), "%d.mp3", segment_index); - break; - default: - snprintf(save_dir, sizeof(save_dir), "%s", LN_DATA_PATH); - snprintf(filename, sizeof(filename), "%d.mp3", segment_index); - break; - } - - printf("[JSON_PARSE] found audio URL (interface_type=%d): %s\n", interface_type, audio_url); - printf("[JSON_PARSE] downloading audio to: %s/%s\n", save_dir, filename); - - uint32_t download_result = ln_download_mp3_file_advanced(audio_url, save_dir, filename); - if (download_result == ERRCODE_SUCC) - { - printf("[JSON_PARSE] audio downloaded successfully: %s/%s\n", save_dir, filename); - } - else - { - printf("[JSON_PARSE] audio download failed\n"); - } + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] found audio URL (interface_type=%d): %s\n", interface_type, audio_url); + ln_download_audio_if_present(audio_url, interface_type, segment_index, 0); } else { - printf("[JSON_PARSE] no audio URL found for interface_type=%d, skipping download\n", interface_type); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] no audio URL found for interface_type=%d, skipping download\n", interface_type); } cJSON_Delete(root); @@ -1059,16 +1052,16 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ { if (!file_path) { - printf("[FILE_PARSE] invalid file path\n"); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] invalid file path\n"); return ERRCODE_FAIL; } - printf("[FILE_PARSE] processing file: %s (interface_type=%d)\n", file_path, interface_type); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] processing file: %s (interface_type=%d)\n", file_path, interface_type); FILE *fp = fopen(file_path, "r"); if (!fp) { - printf("[FILE_PARSE] failed to open file: %s\n", file_path); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] failed to open file: %s\n", file_path); return ERRCODE_FAIL; } @@ -1077,6 +1070,16 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ int success_count = 0; int error_count = 0; + /* 向UI上报开始状态 */ + if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV) + { + set_start_conversation_flag(START_CONVERSATION_FLAG_START); + } + else if (interface_type == LN_HTTP_RECV_STORY_CHOOSE || interface_type == LN_HTTP_RECV_STORY_RANDOM) + { + set_start_conversation_flag(START_CONVERSATION_FLAG_STORY_START); + } + if(g_ln_audio_download_cancel)//如果之前中断了播放,新的对话就恢复播放 { uapi_ln_audio_download_is_cancel(0); @@ -1085,7 +1088,7 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ while (fgets(line, sizeof(line), fp)) { if (g_ln_audio_download_cancel) { - printf("[SSE] download canceled by user\n"); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[SSE] download canceled by user\n"); break; } @@ -1119,8 +1122,6 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ { json_start++; } - - printf("[FILE_PARSE] line %d: processing JSON: %s\n", line_count, json_start); uint32_t result = ln_parse_and_process_json_data(json_start, interface_type, line_count); if (result == ERRCODE_SUCC) @@ -1134,18 +1135,26 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ } else { - printf("[FILE_PARSE] line %d: invalid format (not SSE data): %s\n", line_count, line); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] line %d: invalid format (not SSE data): %s\n", line_count, line); error_count++; } } fclose(fp); - printf("[FILE_PARSE] processing completed: total lines=%d, success=%d, errors=%d\n", + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] processing completed: total lines=%d, success=%d, errors=%d\n", line_count, success_count, error_count); - //语音对话结束 - set_start_conversation_flag(0); + /* 向UI上报结束状态 */ + if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV) + { + set_start_conversation_flag(START_CONVERSATION_FLAG_END); + } + else if (interface_type == LN_HTTP_RECV_STORY_CHOOSE || interface_type == LN_HTTP_RECV_STORY_RANDOM) + { + set_start_conversation_flag(START_CONVERSATION_FLAG_STORY_END); + } + return (error_count == 0) ? ERRCODE_SUCC : ERRCODE_FAIL; } /* ============================ mp3下载功能 End ============================ */ diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c index 9face579e..80e1a7a1b 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c @@ -192,6 +192,7 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, do { ret = HttpClientRecvResponse(&client, &client_data); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_ota_download_flow_http", "ret = %d", ret); if (ret == HTTP_SUCCESS || ret == HTTP_EAGAIN) { // 处理接收到的数据块 diff --git a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp index 30b94ebc7..5a18084d7 100644 --- a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp +++ b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp @@ -14,14 +14,14 @@ void push_text_to_ui(const char *text) { OHOS::ThunderBirdModel::GetInstance()->SetTextStr(text); } -// 是否开始对话标志 0=否 1=是 -static int g_start_conversation_flag = 0; +// 是否开始对话标志:开始对话、结束对话、故事开始、故事结束 +static int g_start_conversation_flag = START_CONVERSATION_FLAG_UNKNOWN; void set_start_conversation_flag(int flag) { g_start_conversation_flag = flag; } -// 获取是否开始对话标志 +// 获取是否开始对话标志:开始对话、结束对话、故事开始、故事结束 int get_start_conversation_flag(void) { return g_start_conversation_flag; diff --git a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h index dd461e0c0..455050d09 100644 --- a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h +++ b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h @@ -7,9 +7,18 @@ extern "C" // 推送文本给ui void push_text_to_ui(const char *text); -//设置是否开始对话标志 + +enum { + START_CONVERSATION_FLAG_UNKNOWN = 0, // 未知 + START_CONVERSATION_FLAG_START, // 开始对话 + START_CONVERSATION_FLAG_END, // 结束对话 + START_CONVERSATION_FLAG_STORY_START, // 故事开始 + START_CONVERSATION_FLAG_STORY_END, // 故事结束 +}; + +//设置是否开始对话标志:开始对话、结束对话、故事开始、故事结束 void set_start_conversation_flag(int flag); -//获取开始对话标志 +//获取开始对话标志:开始对话、结束对话、故事开始、故事结束 int get_start_conversation_flag(void); /* -- 2.17.1