添加错误体at通知ui验证
authorluozheng <1732036298@qq.com>
Sun, 28 Sep 2025 06:16:21 +0000 (14:16 +0800)
committerluozheng <1732036298@qq.com>
Sun, 28 Sep 2025 06:16:21 +0000 (14:16 +0800)
code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h
code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.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 4b4bd86..e0892b4 100644 (file)
@@ -58,6 +58,7 @@ typedef enum {
     HAND_SLEEP_MODE_END_NOTIFY, // 哄睡模式结束通知
     HAND_DOWNLOAD_EMOJI_NOTIFY, // 下载表情通知
     HAND_CHAT_STAMINA_NOTIFY, // 对话体力与流量通知
+    HAND_API_ERROR_NOTIFY, // 协议错误通知
     MAX_NUM_HANDLER, 
 } UiServiceMsgId;
 
index 0b02629..9abc792 100644 (file)
@@ -94,6 +94,7 @@ static void HandSleepModeStartNotify(Request *request);
 static void HandSleepModeEndNotify(Request *request);
 static void HandChatStaminaNotify(Request *request);
 static void HandDownloadEmojiNotify(Request *request);
+static void HandApiErrorNotify(Request *request);
 // 消息处理程序项数组(每个消息类型对应一个处理函数,函数名以 Handle 开头)
 MsgHandlerEntry msgHandlers[] = {
     {HANDLE_JUMP_OTA_VIEW, HandleJumpOtaView},
@@ -104,6 +105,7 @@ MsgHandlerEntry msgHandlers[] = {
     {HAND_SLEEP_MODE_END_NOTIFY,HandSleepModeEndNotify},
     {HAND_CHAT_STAMINA_NOTIFY,HandChatStaminaNotify},
     {HAND_DOWNLOAD_EMOJI_NOTIFY,HandDownloadEmojiNotify},
+    {HAND_API_ERROR_NOTIFY,HandApiErrorNotify},
 };
 /* -------------------------------------------------------------------------- */
 void ScreenTurnOn(int32_t timeout)
@@ -509,6 +511,12 @@ static void HandChatStaminaNotify(Request *request)
         });
 }
 
+// 协议错误通知
+void HandApiErrorNotify(Request *request)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandApiErrorNotify msg_id = %d", request->msgId);
+    std::string value(static_cast<char*>(request->data), request->len);
+}
 /* -------------------------------------------------------------------------- */
 #ifdef __cplusplus
 }
index 34c8d13..5674ca1 100644 (file)
@@ -1759,6 +1759,36 @@ static uint32_t ln_check_dns_at(uint8_t *para, uint32_t paraLen, char *strCommon
     return ERRCODE_SUCC;
 }
 
+/* AT^LN_API_ERROR - 推送协议错误 */
+static uint32_t ln_api_error_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+
+    uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+    const char *message = ""; // 默认空消息
+
+    if (para != NULL && paraLen > 0) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+            printf("ERROR: Failed to parse parameters\n");
+            return ERRCODE_FAIL;
+        }
+        if (argc >= 1) {
+            message = (char*)argv[0];
+        }
+    }
+
+    if (message != NULL && strlen(message) > 512) {
+        printf("ERROR: Message too long (max 512 characters)\n");
+        return ERRCODE_FAIL;
+    }
+
+    push_api_error_notification(message);
+    printf("+LN_API_ERROR: OK\n");
+    printf("[LN][API_ERROR] message=%s\n", message);
+    return ERRCODE_SUCC;
+}
+
 static const at_cmd_table_t g_ln_api_cmd[] = {
     /* ----------------------------------- VAD ---------------------------------- */
     {(char *)"AT^VAD_INIT", vad_init, (char *)"初始化 VAD"},
@@ -1821,6 +1851,7 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_DOWNLOAD_EMOJI", ln_download_emoji_at, (char *)"下载表情: AT^LN_DOWNLOAD_EMOJI[=characterId,eyesColor,eyesShape]"},
     {(char *)"AT^LN_CHAT_STAMINA_DEMO", ln_chat_stamina_demo_at, (char *)"演示对话体力/流量UI: AT^LN_CHAT_STAMINA_DEMO=member|enough|low|empty 或自定义"},
     {(char *)"AT^LN_SWITCH_EXPRESSION", ln_switch_expression_at, (char *)"切换表情视频: AT^LN_SWITCH_EXPRESSION=<video_path>"},
+    {(char *)"AT^LN_API_ERROR", ln_api_error_at, (char *)"推送协议错误: AT^LN_API_ERROR[=message]"},
     /* 内存查看相关AT命令 */
     {(char *)"AT^LN_MEMORY_INFO", ln_memory_info_at, (char *)"查看系统内存信息: AT^LN_MEMORY_INFO"},
     {(char *)"AT^LN_FOLDER_SIZE", ln_folder_size_at, (char *)"查看文件夹大小: AT^LN_FOLDER_SIZE[=/path/to/folder]"},
index 40c75b7..be5f9fa 100644 (file)
@@ -39,6 +39,7 @@ char *g_ln_last_package_md5 = NULL;      // 最近一次查询到的 OTA 包 MD5
 int g_ln_last_package_size = 0;          // 最近一次查询到的 OTA 包大小(字节)
 int g_ln_is_device_bound = 0;           /* 设备绑定状态 */
 char g_ln_device_user_id[64] = {0};      // 设备绑定用户ID
+static int g_ln_is_file_downloading = 0; // 是否正在下载文件
 
 /* 简单的字符串读写(以.bin形式读写原始字节,末尾补'\0')*/
 static int ln_read_string_file(const char *file_path, char **out_str)
@@ -270,6 +271,12 @@ void uapi_ln_set_device_bound(int val)
 {
     g_ln_is_device_bound = val;
 }
+
+/* 下载文件 
+@param url 文件URL
+@param save_path 保存路径
+@return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+*/
 int uapi_ln_download_file(const char *url, const char *save_path)
 {
     if (!url || !save_path)
@@ -278,7 +285,13 @@ int uapi_ln_download_file(const char *url, const char *save_path)
         return ERRCODE_FAIL;
     }
 
+    if(g_ln_is_file_downloading == 1)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 已有文件正在下载中");
+        return ERRCODE_FAIL;
+    }
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url, save_path);
+    g_ln_is_file_downloading = 1;
 
     HttpClient client = {0};
     HttpClientData client_data = {0};
@@ -289,6 +302,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     if (!fd)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] fopen failed for %s", save_path);
+        g_ln_is_file_downloading = 0;
         return ERRCODE_FAIL;
     }
 
@@ -297,6 +311,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] prepare failed: %d", prep);
         fclose(fd);
+        g_ln_is_file_downloading = 0;
         return ERRCODE_FAIL;
     }
 
@@ -307,6 +322,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] connection failed, ret=0x%x", ret);
         fclose(fd);
         HttpClientUnprepare(&client_data);
+        g_ln_is_file_downloading = 0;
         return ERRCODE_FAIL;
     }
 
@@ -318,6 +334,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
         fclose(fd);
         HttpClientClse(&client);
         HttpClientUnprepare(&client_data);
+        g_ln_is_file_downloading = 0;
         return ERRCODE_FAIL;
     }
 
@@ -366,6 +383,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     HttpClientUnprepare(&client_data);
     HttpClientClse(&client);
     fclose(fd);
+    g_ln_is_file_downloading = 0;
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] download process finished,ret=%d", ret);
     return ret;
 }
index 88693f5..7e82474 100644 (file)
@@ -65,6 +65,7 @@ typedef struct
 /* 设备ID/ICCID 本地缓存文件路径(.bin)*/
 #define LN_DEVICE_ID_FILE    LN_DATA_PATH "/device_id.bin"
 #define LN_DEVICE_ICCID_FILE LN_DATA_PATH "/device_iccid.bin"
+#define LN_DEVICE_SCREEN_IMAGE_FILE LN_DATA_PATH "/device_screen_image.bin" // 屏保图片文件
 
 #define LN_CLIENT_PREPARE_SIZE 2 * 1024
 #define TEST_MP3_DOWNLOAD 0 // 测试下载mp3
index e05d24e..39e74c4 100644 (file)
@@ -225,6 +225,33 @@ void ln_parse_character_config(const char *json_buf, int json_len)
     printf("  avatar_screen_image: %s\n", g_ln_character_config.avatar_screen_image ? g_ln_character_config.avatar_screen_image : "");
     printf("  avatar_screen_image_as_emoji: %d\n", g_ln_character_config.avatar_screen_image_as_emoji);
 
+    // 如果屏保图片不为空,则保存到本地
+    int ret = ERRCODE_SUCC;
+    if (g_ln_character_config.avatar_screen_image)
+    {
+        // 下载屏保图片,eg:"https://example.com/new_avatar.bin"
+        ret = uapi_ln_download_file(g_ln_character_config.avatar_screen_image, LN_DEVICE_SCREEN_IMAGE_FILE);
+        if (ERRCODE_SUCC != ret)
+        {
+            int i = 1;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载失败,重试3次,第%d次", i);
+            // 如果下载失败重试3次
+            for (i = 1; i <= 3; i++)
+            {
+                ret = uapi_ln_download_file(g_ln_character_config.avatar_screen_image, LN_DEVICE_SCREEN_IMAGE_FILE);
+                if (ERRCODE_SUCC == ret)
+                {
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功,第%d次", i);
+                    break;
+                }
+            }
+        }
+    }
+    if (ERRCODE_SUCC != ret)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载失败,最终失败");
+    }
+
     /* 推送角色配置更改通知 */
     push_character_config_change_notification(g_ln_character_config.character_id,
                                               g_ln_character_config.character_name,
@@ -232,7 +259,7 @@ void ln_parse_character_config(const char *json_buf, int json_len)
                                               g_ln_character_config.avatar_hair_color,
                                               g_ln_character_config.avatar_eyes_color,
                                               g_ln_character_config.avatar_hair_style,
-                                              g_ln_character_config.avatar_screen_image_as_emoji);
+                                              ret == ERRCODE_SUCC ? 1 : 0);
 
     cJSON_Delete(root);
     free(text);
@@ -582,21 +609,6 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
         free(header_buf);
         return ERRCODE_FAIL;
     }
-    if (clientData.responseBuf && clientData.responseBufLen > 0)
-    {
-        int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
-        char *response_buf = (char *)malloc(real_len + 1);
-        if (!response_buf)
-        {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc failed for response_buf");
-            free(header_buf);
-            return ERRCODE_FAIL;
-        }
-        memcpy(response_buf, clientData.responseBuf, real_len);
-        response_buf[real_len] = '\0';
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response body: %s", response_buf);
-        free(response_buf);
-    }
 
     char *json_text = (char *)malloc((size_t)body_len + 1);
     if (!json_text)
@@ -610,7 +622,8 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
     json_text[body_len] = '\0';
 
     /* 若非200,打印原始响应体便于排查 */
-    if (code != 200) {
+    if (code != 200)
+    {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] HTTP code=%d", code);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] body=%s", json_text);
     }
@@ -634,6 +647,9 @@ uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *devi
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] server error: %s, msg=%s",
                       cJSON_IsString(errorCode) ? errorCode->valuestring : "<unknown>",
                       (message && cJSON_IsString(message)) ? message->valuestring : "<none>");
+        /* 推送协议错误到UI */
+        push_api_error_notification((message && cJSON_IsString(message)) ? message->valuestring : "<none>");
+
         cJSON_Delete(root);
         free(json_text);
         HttpClientUnprepare(&clientData);
index aaf1575..ceaa880 100644 (file)
@@ -3,10 +3,10 @@
 #include <stdlib.h>
 #include "upg_common_porting.h"  /* 使用统一的 UPG_FILE_NAME 路径宏 */
 #include <string.h>
-#include <stdlib.h>
 #include "mbedtls/md5.h"
 #include "errcode.h"
 #include "custom_utils.h"
+#include "cJSON.h"
 
 /* OTA下载状态枚举(对应时序图中的下载阶段)*/
 typedef enum
@@ -117,7 +117,7 @@ static void ln_ota_handle_download_msg(const OtaDownloadMsg *msg)
             break;
     }
 }
-#include "cJSON.h"
+
 
 //解析 OTA 任务 JSON,决定是否触发下载
 static void ln_handle_ota_task_response(const char *json_buf, int json_len)
@@ -304,14 +304,14 @@ uint32_t ln_ota_get_task_http(const char *device_id,
     HTTPC_RESULT res = HttpClientGetRequest(&client, url_with_params, &clientData);
     if (res != HTTP_SUCCESS)
     {
-        printf("[OTA] getTask failed %d\n", res);
+        printf("[OTA] HttpClientGetRequest error %d\n", res);
         goto end;
     }
 
     int code = HttpClientGetResponseCode(&client);
     if (code != 200)
     {
-        printf("[OTA] getTask failed %d\n", code);
+        printf("[OTA] HttpClientGetResponseCode %d\n", code);
         if (clientData.responseBuf && clientData.responseBufLen > 0)
         {
             int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
@@ -370,11 +370,6 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
     int ret = -1;
     size_t total_written = 0; // 已写入文件的总字节数
     int last_percent = -1;    // 上次上报的进度百分比
-    char *prog = (char *)malloc(64);
-    if (!prog)
-    {
-        return -1;
-    }
 
     // 打开文件准备写入
     FILE *fd = fopen(save_path, "wb+");
@@ -442,15 +437,10 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
                 if (percent != last_percent)
                 {
                     last_percent = percent;
-                    int n = snprintf(prog, 64,
-                                     "{\"status\":%d,\"progress\":%d}",
-                                     OTA_DOWNLOAD_STATUS_PROGRESS, percent);
-                    if (n > 0) {
-                        OtaDownloadMsg om = {0};
-                        om.status = OTA_DOWNLOAD_STATUS_PROGRESS;
-                        om.progress = percent;
-                        ln_ota_handle_download_msg(&om);
-                    }
+                    OtaDownloadMsg om = {0};
+                    om.status = OTA_DOWNLOAD_STATUS_PROGRESS;
+                    om.progress = percent;
+                    ln_ota_handle_download_msg(&om);
                 }
 
                 // 检查是否超过预期总大小
@@ -502,8 +492,7 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path,
     fclose(fd);
     HttpClientClse(&client);
     HttpClientUnprepare(&client_data);
-    if (prog)
-        free(prog);
+    
     printf("[OTA] download process finished\n");
 
     return ret;
@@ -559,31 +548,20 @@ uint32_t ln_ota_download_flow_http(const char *device_id,
     }
     if (dres != 0)
     {
-        char *msg = (char *)malloc(64);
-        if (msg)
-        {
-            int n = snprintf(msg, 64, "{\"status\":%d,\"error\":\"download failed\"}", OTA_DOWNLOAD_STATUS_FAILED);
-            if (n > 0) {
-                OtaDownloadMsg om = {0};
-                om.status = OTA_DOWNLOAD_STATUS_FAILED;
-                snprintf(om.error_msg, sizeof(om.error_msg), "%s", "download failed");
-                ln_ota_handle_download_msg(&om);
-            }
-            free(msg);
-        }
+        OtaDownloadMsg om = {0};
+        om.status = OTA_DOWNLOAD_STATUS_FAILED;
+        snprintf(om.error_msg, sizeof(om.error_msg), "%s", "download failed");
+        ln_ota_handle_download_msg(&om);
         free(save_path);
         return ERRCODE_FAIL;
     }
 
-    size_t done_cap = 64 + strlen(save_path) + (g_ln_last_package_md5 ? strlen(g_ln_last_package_md5) : 0);
-    char *done = (char *)malloc(done_cap);
-    if (done) {
+    {
         OtaDownloadMsg om = {0};
         om.status = OTA_DOWNLOAD_STATUS_COMPLETED;
         snprintf(om.file_path, sizeof(om.file_path), "%s", save_path);
         if (g_ln_last_package_md5) snprintf(om.md5, sizeof(om.md5), "%s", g_ln_last_package_md5);
         ln_ota_handle_download_msg(&om);
-        free(done);
     }
     free(save_path);
     return ERRCODE_SUCC;
index 3e2ed80..d957863 100644 (file)
@@ -6,6 +6,7 @@
 #include "system/include/MemoryManager.h"
 #include <string>
 #include <cstring>
+#include "wearable_log.h"
 
 // 前向声明网络检测函数,避免循环依赖
 extern "C" {
@@ -88,7 +89,7 @@ void push_schedule_reminder_notification(const char *title, const char *content)
     // 组装简易数据载荷:标题+内容(若 UI 端需要更复杂结构,可定义专用 struct)
     // 这里直接传两段以'\n'拼接的字符串给 UI,UI 再自行拆分。
     char payload[384] = {0};
-    printf("title:%s,content%s\r\n",title,content);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "title:%s,content%s\r\n",title,content);
     snprintf(payload, sizeof(payload) - 1, "%s\n%s", title, content);
     SendMsgToUIService(HAND_SCHEDULE_REMINDER_NOTIFY, 0,(void *)payload, (int32_t)strlen(payload));
 }
@@ -96,7 +97,7 @@ void push_schedule_reminder_notification(const char *title, const char *content)
 // 推送任务提醒通知
 void push_task_reminder_notification(const char *scheduleName)
 {
-    printf("scheduleName:%s\r\n",scheduleName);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "scheduleName:%s\r\n",scheduleName);
     SendMsgToUIService(HAND_TASK_REMINDER_NOTIFY, 0,(void *)scheduleName, (int32_t)strlen(scheduleName));
 }
 
@@ -163,7 +164,7 @@ void push_download_emoji_notification(char *characterId, char *eyesColor, char *
     const char *id = characterId ? characterId : "";
     const char *ec = eyesColor ? eyesColor : "";
     const char *es = eyesShape ? eyesShape : "";
-    printf("push_download_emoji_notification: characterId=%s, eyesColor=%s, eyesShape=%s\r\n", id, ec, es);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "push_download_emoji_notification: characterId=%s, eyesColor=%s, eyesShape=%s\r\n", id, ec, es);
     (void)snprintf(payload, sizeof(payload) - 1, "%s|%s|%s", id, ec, es);
     //切换表情通知ui
     SendMsgToUIService(HAND_DOWNLOAD_EMOJI_NOTIFY, 0, (void *)payload, (int32_t)strlen(payload));
@@ -189,22 +190,29 @@ void push_chat_stamina_notification(int chatLeft, const char *chatDesc,
 bool switch_expression_video(const char *video_path)
 {
     if (video_path == nullptr) {
-        printf("[LN][SWITCH_VIDEO] Error: video_path is null\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[LN][SWITCH_VIDEO] Error: video_path is null\n");
         return false;
     }
     
     OHOS::ThunderBirdModel* mainView = OHOS::ThunderBirdModel::GetInstance();
     if (mainView == nullptr) {
-        printf("[LN][SWITCH_VIDEO] Error: ThunderBirdModel instance is null\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[LN][SWITCH_VIDEO] Error: ThunderBirdModel instance is null\n");
         return false;
     }
     
     bool result = mainView->SwitchExpressionVideo(video_path);
-    printf("[LN][SWITCH_VIDEO] Switching to video: %s, result: %s\n", 
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[LN][SWITCH_VIDEO] Switching to video: %s, result: %s\n", 
            video_path, result ? "success" : "failed");
     
     return result;
 }
+// 推送协议错误通知到UI
+void push_api_error_notification(const char *message)
+{
+    const char *msg = message ? message : "";
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[API_ERROR] msg=%s", msg);
+    SendMsgToUIService(HAND_API_ERROR_NOTIFY, 0, (void *)msg, (int32_t)strlen(msg));
+}
 
 // 检测是否有外网连接
 int check_internet_connection(void)
index c2a10fd..92ddb40 100644 (file)
@@ -79,6 +79,7 @@ void push_chat_stamina_notification(int chatLeft,
                                     const char *qrcodeUrl);
 // 切换表情视频
 bool switch_expression_video(const char *video_path);
+void push_api_error_notification(const char *message);
 
 // 网络连接检测
 int check_internet_connection(void);