提供对话和故事开始状态上报函数
authorcheng <chengyf@zhihuishixun.com>
Sat, 20 Sep 2025 09:06:55 +0000 (17:06 +0800)
committercheng <chengyf@zhihuishixun.com>
Sat, 20 Sep 2025 09:06:55 +0000 (17:06 +0800)
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h

index 2b900ce..6eb760d 100644 (file)
@@ -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 ============================ */
index 9face57..80e1a7a 100644 (file)
@@ -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)
         {
             // 处理接收到的数据块
index 30b94eb..5a18084 100644 (file)
@@ -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;
index dd461e0..455050d 100644 (file)
@@ -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);
 
 /*