添加带重试的文件下载函数 2437/1 2437/head 2438/target/1
authorluozheng <1732036298@qq.com>
Mon, 29 Sep 2025 04:20:37 +0000 (12:20 +0800)
committerluozheng <1732036298@qq.com>
Mon, 29 Sep 2025 04:20:37 +0000 (12:20 +0800)
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_schedule.c

index 89197ce..f6df7e5 100644 (file)
@@ -25,7 +25,7 @@ static unsigned long g_ln_send_queue = 0xFFFFFFFF;
 #define LN_HTTP_BOARD_TASK_SIZE 0x2500
 
 static char *s_cached_device_id = NULL;
-char *g_device_token = NULL;//设备token,服务器获取
+char *g_device_token = NULL; // 设备token,服务器获取
 static char *s_cached_device_iccid = NULL;
 static char *g_device_model = NULL;
 static char g_device_version[128] = {0};
@@ -37,42 +37,89 @@ char *g_ln_last_package_url = NULL;      // 最近一次查询到的 OTA 包 URL
 char *g_ln_last_target_version = NULL;   // 最近一次查询到的 OTA 包目标版本(动态分配)
 char *g_ln_last_package_md5 = NULL;      // 最近一次查询到的 OTA 包 MD5(动态分配)
 int g_ln_last_package_size = 0;          // 最近一次查询到的 OTA 包大小(字节)
-int g_ln_is_device_bound = 0;           /* 设备绑定状态 */
+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)
 {
-       if (out_str) *out_str = NULL;
-       if (!file_path || !out_str) return -1;
-       FILE *fp = fopen(file_path, "rb");
-       if (!fp) return -1;
-       if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); return -1; }
-       long len = ftell(fp);
-       if (len <= 0 || len > 4096) { fclose(fp); return -1; }
-       if (fseek(fp, 0, SEEK_SET) != 0) { fclose(fp); return -1; }
-       char *buf = (char*)malloc((size_t)len + 1);
-       if (!buf) { fclose(fp); return -1; }
-       size_t n = fread(buf, 1, (size_t)len, fp);
-       fclose(fp);
-       buf[n] = '\0';
-       *out_str = buf;
-       return 0;
+    if (out_str)
+        *out_str = NULL;
+    if (!file_path || !out_str)
+        return -1;
+    FILE *fp = fopen(file_path, "rb");
+    if (!fp)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[READ_STRING_FILE] fopen failed");
+        return -1;
+    }
+    if (fseek(fp, 0, SEEK_END) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[READ_STRING_FILE] fseek failed, SEEK_END");
+        fclose(fp);
+        return -1;
+    }
+    long len = ftell(fp);
+    if (len <= 0 || len > 4096)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[READ_STRING_FILE] len <= 0 or len > 4096");
+        fclose(fp);
+        return -1;
+    }
+    if (fseek(fp, 0, SEEK_SET) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[READ_STRING_FILE] fseek failed, SEEK_SET");
+        fclose(fp);
+        return -1;
+    }
+    char *buf = (char *)malloc((size_t)len + 1);
+    if (!buf)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[READ_STRING_FILE] malloc failed");
+        fclose(fp);
+        return -1;
+    }
+    size_t n = fread(buf, 1, (size_t)len, fp);
+    if (n != (size_t)len)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[READ_STRING_FILE] fread failed, len=%zu, expected=%d", n, len);
+        free(buf);
+        fclose(fp);
+        return -1;
+    }
+    fclose(fp);
+    buf[n] = '\0';
+    *out_str = buf;
+    return 0;
 }
 
 static int ln_write_string_file(const char *file_path, const char *str)
 {
-       if (!file_path || !str) return -1;
-       /* 确保目录存在 */
-       uapi_create_folder((char*)LN_DATA_PATH);
-       FILE *fp = fopen(file_path, "wb");
-       if (!fp) return -1;
-       size_t len = strlen(str);
-       /* 写入原始字节(不强制写入'\0')*/
-       size_t n = fwrite(str, 1, len, fp);
-       fclose(fp);
-       return (n == len) ? 0 : -1;
+    if (!file_path || !str)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[WRITE_STRING_FILE] file_path or str is NULL");
+        return -1;
+    }
+    /* 确保目录存在 */
+    uapi_create_folder((char *)LN_DATA_PATH);
+    FILE *fp = fopen(file_path, "wb");
+    if (!fp)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[WRITE_STRING_FILE] fopen failed");
+        return -1;
+    }
+    size_t len = strlen(str);
+    /* 写入原始字节(不强制写入'\0')*/
+    size_t n = fwrite(str, 1, len, fp);
+    if (n != len)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[WRITE_STRING_FILE] fwrite failed");
+        fclose(fp);
+        return -1;
+    }
+    fclose(fp);
+    return (n == len) ? 0 : -1;
 }
 
 /* 获取设备id: 优先读本地缓存文件 /user/userdata/leiniao/device_id.bin*/
@@ -80,54 +127,64 @@ char *uapi_ln_get_device_id(void)
 {
     // return "DEVICE_PC_TEST_001";
 
-       if (s_cached_device_id && s_cached_device_id[0]) {
+    if (s_cached_device_id && s_cached_device_id[0])
+    {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "device_id cached: %s", s_cached_device_id);
-               return s_cached_device_id;
-       }
-       /* 1. 尝试从文件读取 */
-       char *loaded = NULL;
-       if (ln_read_string_file(LN_DEVICE_ID_FILE, &loaded) == 0 && loaded && loaded[0]) {
-               s_cached_device_id = loaded; /* 接管指针 */
-               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "device_id loaded from %s: %s", LN_DEVICE_ID_FILE, s_cached_device_id);
-               return s_cached_device_id;
-       }
-       if (loaded) free(loaded);
-       /* 2. 文件不存在或读取失败,则通过CAT1获取并落盘 */
-       char *imei = cat1_api_runtime_get_cat1_imei();
-       if (imei && imei[0]) {
-               (void)ln_write_string_file(LN_DEVICE_ID_FILE, imei);
-               size_t tlen = strlen(imei);
-               s_cached_device_id = (char*)malloc(tlen + 1);
-               if (s_cached_device_id) memcpy(s_cached_device_id, imei, tlen + 1);
-               return s_cached_device_id;
-       }
-       return NULL;
+        return s_cached_device_id;
+    }
+    /* 1. 尝试从文件读取 */
+    char *loaded = NULL;
+    if (ln_read_string_file(LN_DEVICE_ID_FILE, &loaded) == 0 && loaded && loaded[0])
+    {
+        s_cached_device_id = loaded; /* 接管指针 */
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "device_id loaded from %s: %s", LN_DEVICE_ID_FILE, s_cached_device_id);
+        return s_cached_device_id;
+    }
+    if (loaded)
+        free(loaded);
+    /* 2. 文件不存在或读取失败,则通过CAT1获取并落盘 */
+    char *imei = cat1_api_runtime_get_cat1_imei();
+    if (imei && imei[0])
+    {
+        (void)ln_write_string_file(LN_DEVICE_ID_FILE, imei);
+        size_t tlen = strlen(imei);
+        s_cached_device_id = (char *)malloc(tlen + 1);
+        if (s_cached_device_id)
+            memcpy(s_cached_device_id, imei, tlen + 1);
+        return s_cached_device_id;
+    }
+    return NULL;
 }
 
 /* 获取设备iccid: 优先读本地缓存文件 /user/userdata/leiniao/device_iccid.bin,不存在则调用CAT1接口并落盘 */
 char *uapi_ln_get_device_iccid(void)
 {
-       if (s_cached_device_iccid && s_cached_device_iccid[0]) {
-               return s_cached_device_iccid;
-       }
-       /* 1. 尝试从文件读取 */
-       char *loaded = NULL;
-       if (ln_read_string_file(LN_DEVICE_ICCID_FILE, &loaded) == 0 && loaded && loaded[0]) {
-               s_cached_device_iccid = loaded; /* 接管指针 */
-               WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "device_iccid loaded from %s: %s", LN_DEVICE_ICCID_FILE, s_cached_device_iccid);
-               return s_cached_device_iccid;
-       }
-       if (loaded) free(loaded);
-       /* 2. 文件不存在或读取失败,则通过CAT1获取并落盘 */
-       char *iccid = cat1_api_runtime_get_cat1_iccid();
-       if (iccid && iccid[0]) {
-               (void)ln_write_string_file(LN_DEVICE_ICCID_FILE, iccid);
-               size_t tlen = strlen(iccid);
-               s_cached_device_iccid = (char*)malloc(tlen + 1);
-               if (s_cached_device_iccid) memcpy(s_cached_device_iccid, iccid, tlen + 1);
-               return s_cached_device_iccid;
-       }
-       return NULL;
+    if (s_cached_device_iccid && s_cached_device_iccid[0])
+    {
+        return s_cached_device_iccid;
+    }
+    /* 1. 尝试从文件读取 */
+    char *loaded = NULL;
+    if (ln_read_string_file(LN_DEVICE_ICCID_FILE, &loaded) == 0 && loaded && loaded[0])
+    {
+        s_cached_device_iccid = loaded; /* 接管指针 */
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "device_iccid loaded from %s: %s", LN_DEVICE_ICCID_FILE, s_cached_device_iccid);
+        return s_cached_device_iccid;
+    }
+    if (loaded)
+        free(loaded);
+    /* 2. 文件不存在或读取失败,则通过CAT1获取并落盘 */
+    char *iccid = cat1_api_runtime_get_cat1_iccid();
+    if (iccid && iccid[0])
+    {
+        (void)ln_write_string_file(LN_DEVICE_ICCID_FILE, iccid);
+        size_t tlen = strlen(iccid);
+        s_cached_device_iccid = (char *)malloc(tlen + 1);
+        if (s_cached_device_iccid)
+            memcpy(s_cached_device_iccid, iccid, tlen + 1);
+        return s_cached_device_iccid;
+    }
+    return NULL;
 }
 /* 获取设备model */
 char *uapi_ln_get_device_model(void)
@@ -139,40 +196,43 @@ char *uapi_ln_get_device_model(void)
 char *uapi_ln_get_device_version(void)
 {
     char version_buf[128] = {0};
-    uint32_t version_len = sizeof(version_buf) - 1;  // 预留1字节给字符串结束符
+    uint32_t version_len = sizeof(version_buf) - 1; // 预留1字节给字符串结束符
     uapi_get_buildversion((uint8_t *)version_buf, version_len);
-    
+
     // 原始格式:V1.0.0_20250920_222554,目标:V1.0.0_20250920
     // 步骤1:找到第一个下划线(分隔版本号和日期)
     char *first_underline = strstr(version_buf, "_");
-    if (!first_underline) {
+    if (!first_underline)
+    {
         // 无下划线,直接使用原始版本号(异常情况处理)
         snprintf(g_device_version, sizeof(g_device_version), "%s", version_buf);
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"g_device_version (no underline): %s", g_device_version);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "g_device_version (no underline): %s", g_device_version);
         return g_device_version;
     }
-    
+
     // 步骤2:找到第二个下划线(分隔日期和时间)
     char *second_underline = strstr(first_underline + 1, "_");
-    if (!second_underline) {
+    if (!second_underline)
+    {
         // 只有一个下划线(如 "V1.0.0_20250920"),直接使用
         snprintf(g_device_version, sizeof(g_device_version), "%s", version_buf);
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"g_device_version (only one underline): %s", g_device_version);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "g_device_version (only one underline): %s", g_device_version);
         return g_device_version;
     }
-    
+
     // 步骤3:截断第二个下划线后的内容(保留版本号+日期)
-    size_t target_len = second_underline - version_buf;  // 计算目标字符串长度(不含时间)
-    if (target_len >= sizeof(g_device_version)) {
+    size_t target_len = second_underline - version_buf; // 计算目标字符串长度(不含时间)
+    if (target_len >= sizeof(g_device_version))
+    {
         // 防止缓冲区溢出,取最大可容纳长度
         target_len = sizeof(g_device_version) - 1;
     }
-    
+
     // 复制并构造目标格式字符串
     strncpy(g_device_version, version_buf, target_len);
-    g_device_version[target_len] = '\0';  // 手动添加字符串结束符
-    
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"g_device_version: %s", g_device_version);
+    g_device_version[target_len] = '\0'; // 手动添加字符串结束符
+
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "g_device_version: %s", g_device_version);
     return g_device_version;
 }
 /* 获取设备token: 优先读本地缓存,判断过期,否则刷新 */
@@ -180,28 +240,34 @@ char *uapi_ln_get_device_token(void)
 {
     // return "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJXWVNaIiwiZGV2aWNlTW9kZWwiOiJhYm15Ym94XzEiLCJkZXZpY2VWZXJzaW9uIjoiMS4wLjAiLCJkZXZpY2VJZCI6IkRFVklDRV9QQ19URVNUXzAwMSIsImV4cCI6MTc2MTQ2MjQ5Mn0.Oeyu8BDopDTvtAcdNpzz46U2w1UwzEzqcvPwD3Dp08Y";
     /* 若内存已有且非占位,直接返回 */
-    if (g_device_token && strcmp(g_device_token, "<null>") != 0) {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_ln_get_device_token: cached token=%s", g_device_token);
+    if (g_device_token && strcmp(g_device_token, "<null>") != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_ln_get_device_token: cached token=%s", g_device_token);
         return g_device_token;
     }
 
     /* 尝试从本地加载 */
     char *cached = NULL;
     uint64_t exp_ts = 0;
-    if (ln_token_load_json(&cached, &exp_ts) == ERRCODE_SUCC && cached && cached[0]) {
+    if (ln_token_load_json(&cached, &exp_ts) == ERRCODE_SUCC && cached && cached[0])
+    {
         int expired = 1;
         (void)ln_token_is_expired(&expired);
-        if (!expired) {
-            if (g_device_token) free(g_device_token);
+        if (!expired)
+        {
+            if (g_device_token)
+                free(g_device_token);
             g_device_token = cached; /* 接管指针 */
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_ln_get_device_token: loaded from file, not expired(未过期)");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_ln_get_device_token: loaded from file, not expired(未过期)");
             return g_device_token;
         }
         /* 已过期:释放缓存并尝试刷新 */
         free(cached);
-        if (uapi_ln_refresh_device_token() == ERRCODE_SUCC) {
-            if (g_device_token && strcmp(g_device_token, "<null>") != 0) {
-                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_ln_get_device_token: refreshed expired token");
+        if (uapi_ln_refresh_device_token() == ERRCODE_SUCC)
+        {
+            if (g_device_token && strcmp(g_device_token, "<null>") != 0)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_ln_get_device_token: refreshed expired token");
                 return g_device_token;
             }
         }
@@ -209,33 +275,33 @@ char *uapi_ln_get_device_token(void)
     }
 
     /* 没有,尝试获取 Token */
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[TOKEN] 刷新 Token");
-    if(uapi_dev_runtime_get_cat1_networkstat())
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token");
+    if (uapi_dev_runtime_get_cat1_networkstat())
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[TOKEN] 网络在线,刷新 Token");
-        if(uapi_ln_refresh_device_token() == ERRCODE_SUCC)
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络在线,刷新 Token");
+        if (uapi_ln_refresh_device_token() == ERRCODE_SUCC)
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[TOKEN] 刷新 Token 成功");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 成功");
             return g_device_token;
         }
         else
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[TOKEN] 刷新 Token 失败");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 刷新 Token 失败");
             return NULL;
         }
     }
     else
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[TOKEN] 网络离线,无法刷新 Token");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[TOKEN] 网络离线,无法刷新 Token");
     }
     return NULL;
 }
 /* 清除设备token */
 void uapi_ln_clear_device_token(void)
 {
-    if(g_device_token != NULL)
+    if (g_device_token != NULL)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"clear device token");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "clear device token");
         free(g_device_token);
         g_device_token = NULL;
     }
@@ -259,13 +325,12 @@ const char *uapi_ln_get_audio_path(void)
 }
 
 /* 获取设备绑定用户ID */
-const char* uapi_ln_get_user_id(void)
+const char *uapi_ln_get_user_id(void)
 {
     return g_ln_device_user_id;
 }
 
-
-/* 下载文件 
+/* 下载文件
 @param url 文件URL
 @param save_path 保存路径
 @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
@@ -278,7 +343,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
         return ERRCODE_FAIL;
     }
 
-    if(g_ln_is_file_downloading == 1)
+    if (g_ln_is_file_downloading == 1)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 已有文件正在下载中");
         return ERRCODE_FAIL;
@@ -354,7 +419,7 @@ int uapi_ln_download_file(const char *url, const char *save_path)
                 total_written += written;
             }
         }
-        else 
+        else
         {
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] recv_res = %d, break", recv_res);
             break;
@@ -386,18 +451,44 @@ int uapi_ln_download_file(const char *url, const char *save_path)
     return ret;
 }
 
+/* 下载函数封装:带重试,不做UI通知与资源释放 */
+int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_retries)
+{
+    if (url == NULL || save_path == NULL) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] invalid params");
+        return ERRCODE_FAIL;
+    }
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] start: %s -> %s", url, save_path);
+    int ret = uapi_ln_download_file(url, save_path);
+    if (ERRCODE_SUCC != ret) {
+        for (int i = 1; i <= max_retries; i++) {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] retry %d/%d", i, max_retries);
+            ret = uapi_ln_download_file(url, save_path);
+            if (ERRCODE_SUCC == ret) {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] success on retry %d", i);
+                break;
+            }
+        }
+    }
+    return ret;
+}
 /* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
 HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
 {
-    if (header_size < 2048) header_size = 2048;
-    if (resp_size < 2048) resp_size = 2048;
+    if (header_size < 2048)
+        header_size = 2048;
+    if (resp_size < 2048)
+        resp_size = 2048;
     HTTPC_RESULT ret = HttpClientPrepare(clientData, header_size, resp_size);
-    if (ret == HTTP_SUCCESS) return ret;
+    if (ret == HTTP_SUCCESS)
+        return ret;
     int tries = 0;
-    while (ret != HTTP_SUCCESS && tries < 3) {
+    while (ret != HTTP_SUCCESS && tries < 3)
+    {
         header_size = header_size < 4096 ? 4096 : header_size * 2;
         resp_size = resp_size < (16 * 1024) ? (16 * 1024) : resp_size * 2;
-        if (resp_size > (256 * 1024)) resp_size = (256 * 1024);
+        if (resp_size > (256 * 1024))
+            resp_size = (256 * 1024);
         ret = HttpClientPrepare(clientData, header_size, resp_size);
         tries++;
     }
@@ -407,17 +498,17 @@ HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, i
 /* ============================ WebSocket 业务消息 上层处理 ============================ */
 void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
 {
-    if(type == WS_CMD_CONNECTED)
+    if (type == WS_CMD_CONNECTED)
     {
         printf("[LN][WS] Processing WebSocket Connected event\n");
         set_is_ln_get_device_network_success(1);
     }
-    else if(type == WS_CMD_DISCONNECTED)
+    else if (type == WS_CMD_DISCONNECTED)
     {
         printf("[LN][WS] Processing WebSocket Disconnected event\n");
         set_is_ln_get_device_network_success(0);
     }
-    
+
     if (!data_msg || !data_msg->raw_json)
     {
         printf("[WS] invalid data message\n");
@@ -430,108 +521,121 @@ void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
 
     printf("[WS] cmd=%d message_id=%s cmd_str=%s\n", (int)type, message_id ? message_id : "", cmd_str ? cmd_str : "");
 
-    switch (type) {
-        case WS_CMD_CHARACTER_CHANGE: {
-            printf("[WS] CharacterChange:角色切换\n");
-            if(json_str)
-            {
-                ln_parse_character_config(json_str, (int)strlen(json_str));
-            }
-            break;
-        }
-        case WS_CMD_CHARACTER_CONFIG_CHANGE: {
-            printf("[WS] CharacterConfigChange:角色配置更改(CharacterConfigChange)\n");
-            if(json_str)
-            {
-                ln_parse_character_config(json_str, (int)strlen(json_str));
-            }
-            break;
-        }
-        case WS_CMD_SLEEP_MODE_START: {
-            printf("[WS] SleepModeStart:哄睡模式开始\n");
-            if(json_str)
-            {
-                ln_parse_sleep_mode_start(json_str, (int)strlen(json_str));
-            }
-            break;
-        }
-        case WS_CMD_SLEEP_MODE_END: {
-            printf("[WS] SleepModeEnd:哄睡模式结束\n");
-            if(json_str)
-            {
-                ln_parse_sleep_mode_end(json_str, (int)strlen(json_str));
-            }
-            break;
-        }
-        case WS_CMD_DEVICE_BIND: {
-            printf("[WS] DeviceBind:设备绑定\n");
-            push_bind_and_unbind_notification(LN_DEVICE_BIND_STATUS_BOUND);
-            break;
-        }
-        case WS_CMD_DEVICE_UNBIND: {
-            printf("[WS] DeviceUnbind:设备解绑\n");
-            push_bind_and_unbind_notification(LN_DEVICE_BIND_STATUS_UNBOUND);
-            break;
-        }
-        case WS_CMD_SCHEDULE_REMINDER: {
-            printf("[WS] ScheduleReminder:日程提醒\n");
-            if(json_str)
-            {
-                ln_parse_schedule_reminder(json_str, (int)strlen(json_str));
-            }
-            break;
-        }
-        case WS_CMD_SCHEDULE_REMINDER_CFG: {
-            printf("[WS] ScheduleReminderConfig:日程提醒配置\n");
-            if(json_str)
-            {
-                ln_parse_schedule_reminder_config(json_str, (int)strlen(json_str));
-            }
-            break;
+    switch (type)
+    {
+    case WS_CMD_CHARACTER_CHANGE:
+    {
+        printf("[WS] CharacterChange:角色切换\n");
+        if (json_str)
+        {
+            ln_parse_character_config(json_str, (int)strlen(json_str));
         }
-        case WS_CMD_DOWNLOAD_EMOJI: {
-            printf("[WS] DownloadEmoji:下载表情\n");
-            if(json_str)
-            {
-                // ln_parse_download_emoji(json_str, (int)strlen(json_str));
-            }
-            break;
+        break;
+    }
+    case WS_CMD_CHARACTER_CONFIG_CHANGE:
+    {
+        printf("[WS] CharacterConfigChange:角色配置更改(CharacterConfigChange)\n");
+        if (json_str)
+        {
+            ln_parse_character_config(json_str, (int)strlen(json_str));
         }
-        case WS_CMD_DELETE_EMOJI: {
-            printf("[WS] DeleteEmoji:删除表情\n");
-            if(json_str)
-            {
-                ln_parse_delete_emoji(json_str, (int)strlen(json_str));
-            }
-            break;
+        break;
+    }
+    case WS_CMD_SLEEP_MODE_START:
+    {
+        printf("[WS] SleepModeStart:哄睡模式开始\n");
+        if (json_str)
+        {
+            ln_parse_sleep_mode_start(json_str, (int)strlen(json_str));
         }
-        case WS_CMD_PING: {
-            printf("[WS] Ping\n");
-            break;
+        break;
+    }
+    case WS_CMD_SLEEP_MODE_END:
+    {
+        printf("[WS] SleepModeEnd:哄睡模式结束\n");
+        if (json_str)
+        {
+            ln_parse_sleep_mode_end(json_str, (int)strlen(json_str));
         }
-        case WS_CMD_PONG: {
-            printf("[WS] Pong\n");
-            break;
+        break;
+    }
+    case WS_CMD_DEVICE_BIND:
+    {
+        printf("[WS] DeviceBind:设备绑定\n");
+        push_bind_and_unbind_notification(LN_DEVICE_BIND_STATUS_BOUND);
+        break;
+    }
+    case WS_CMD_DEVICE_UNBIND:
+    {
+        printf("[WS] DeviceUnbind:设备解绑\n");
+        push_bind_and_unbind_notification(LN_DEVICE_BIND_STATUS_UNBOUND);
+        break;
+    }
+    case WS_CMD_SCHEDULE_REMINDER:
+    {
+        printf("[WS] ScheduleReminder:日程提醒\n");
+        if (json_str)
+        {
+            do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER, NULL, 0);
         }
-        case WS_CMD_VOICE_CHAT_RESPONSE:{
-            push_text_to_ui(data_msg->text_content);
-            break;
+        break;
+    }
+    case WS_CMD_SCHEDULE_REMINDER_CFG:
+    {
+        printf("[WS] ScheduleReminderConfig:日程提醒配置\n");
+        if (json_str)
+        {
+            ln_parse_schedule_reminder_config(json_str, (int)strlen(json_str));
         }
-        case WS_CMD_CHOOSE_STORY_RESPONSE:{
-            push_text_to_ui(data_msg->text_content);
-            break;
+        break;
+    }
+    case WS_CMD_DOWNLOAD_EMOJI:
+    {
+        printf("[WS] DownloadEmoji:下载表情\n");
+        if (json_str)
+        {
+            // ln_parse_download_emoji(json_str, (int)strlen(json_str));
         }
-        default: {
-            printf("[WS] Unknown command=%s\n", cmd_str ? cmd_str : "");
-            break;
+        break;
+    }
+    case WS_CMD_DELETE_EMOJI:
+    {
+        printf("[WS] DeleteEmoji:删除表情\n");
+        if (json_str)
+        {
+            ln_parse_delete_emoji(json_str, (int)strlen(json_str));
         }
+        break;
+    }
+    case WS_CMD_PING:
+    {
+        printf("[WS] Ping\n");
+        break;
+    }
+    case WS_CMD_PONG:
+    {
+        printf("[WS] Pong\n");
+        break;
+    }
+    case WS_CMD_VOICE_CHAT_RESPONSE:
+    {
+        push_text_to_ui(data_msg->text_content);
+        break;
+    }
+    case WS_CMD_CHOOSE_STORY_RESPONSE:
+    {
+        push_text_to_ui(data_msg->text_content);
+        break;
+    }
+    default:
+    {
+        printf("[WS] Unknown command=%s\n", cmd_str ? cmd_str : "");
+        break;
+    }
     }
 }
 /* ============================ WebSocket 业务消息 上层处理 End============================ */
 
-
-
-
 /* ============================ mp3下载功能  ============================ */
 
 /**
@@ -551,7 +655,7 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
 
     // 构建完整保存路径
     size_t sp_cap = strlen(save_dir) + 1 + strlen(filename) + 1;
-    char *save_path = (char*)malloc(sp_cap);
+    char *save_path = (char *)malloc(sp_cap);
     if (!save_path)
     {
         printf("[SSE] malloc save_path failed\n");
@@ -567,21 +671,21 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
 
     printf("[SSE] downloading MP3: %s -> %s\n", audio_url, save_path);
 
-    #ifdef SUPPORT_CAT1_SERVICE
+#ifdef SUPPORT_CAT1_SERVICE
     /* CAT1 网络保活处理 */
     cat1_api_add_cat1_vote();
     cat1_api_wakeup_cat1(1);
     cat1_api_keep_wakeup(1);
     cat1_api_disable_cat1_cmd(0);
     LOS_Msleep(300);
-    #endif
+#endif
 
     HttpClient client = {0};
     HttpClientData client_data = {0};
     int ret = -1;
     size_t total_written = 0;
 
-    #if TEST_MP3_DOWNLOAD
+#if TEST_MP3_DOWNLOAD
     // 打开文件准备写入
     FILE *fd = fopen(save_path, "wb");
     if (!fd)
@@ -589,15 +693,15 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
         printf("[SSE] fopen failed for %s\n", save_path);
         return ERRCODE_FAIL;
     }
-    #endif
+#endif
 
     HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 2);
     if (prep != HTTP_SUCCESS)
     {
         printf("[SSE] prepare failed: %d\n", prep);
-        #if TEST_MP3_DOWNLOAD
+#if TEST_MP3_DOWNLOAD
         fclose(fd);
-        #endif
+#endif
         free(save_path);
         return ERRCODE_FAIL;
     }
@@ -607,9 +711,9 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
     if (ret != 0)
     {
         printf("[SSE] connection failed, ret=0x%x\n", ret);
-        #if TEST_MP3_DOWNLOAD
+#if TEST_MP3_DOWNLOAD
         fclose(fd);
-        #endif
+#endif
         HttpClientUnprepare(&client_data);
         free(save_path);
         return ERRCODE_FAIL;
@@ -620,9 +724,9 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
     if (ret != 0)
     {
         printf("[SSE] send request failed, ret=0x%x\n", ret);
-        #if TEST_MP3_DOWNLOAD
+#if TEST_MP3_DOWNLOAD
         fclose(fd);
-        #endif
+#endif
         HttpClientClse(&client);
         HttpClientUnprepare(&client_data);
         free(save_path);
@@ -638,7 +742,7 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
             if (client_data.contentBlockLen > 0)
             {
                 printf("download mp3 file advanced: contentBlockLen=%d\n", client_data.contentBlockLen);
-                #if TEST_MP3_DOWNLOAD
+#if TEST_MP3_DOWNLOAD
                 /* 一边下载到文件,一边推送到播放器队列,尝试边下边播 */
                 size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd);
                 if (written != client_data.contentBlockLen)
@@ -651,19 +755,19 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
                     return ERRCODE_FAIL;
                 }
                 total_written += written;
-                #endif
-                ret = AI_SendAudioDataToQueue((const uint8_t*)client_data.responseBuf, client_data.contentBlockLen);
+#endif
+                ret = AI_SendAudioDataToQueue((const uint8_t *)client_data.responseBuf, client_data.contentBlockLen);
                 if (ret != 0)
                 {
                     printf("download mp3 file advanced: write failed1, ret=%d\n", ret);
                     // 写失败重新写
                     LOS_Msleep(100);
-                    ret = AI_SendAudioDataToQueue((const uint8_t*)client_data.responseBuf, (uint32_t)client_data.contentBlockLen);
+                    ret = AI_SendAudioDataToQueue((const uint8_t *)client_data.responseBuf, (uint32_t)client_data.contentBlockLen);
                     if (ret != 0)
                     {
                         printf("download mp3 file advanced: write failed2, ret=%d\n", ret);
                         LOS_Msleep(100);
-                        ret = AI_SendAudioDataToQueue((const uint8_t*)client_data.responseBuf, (uint32_t)client_data.contentBlockLen);
+                        ret = AI_SendAudioDataToQueue((const uint8_t *)client_data.responseBuf, (uint32_t)client_data.contentBlockLen);
                         if (ret != 0)
                         {
                             printf("download mp3 file advanced: write failed3, ret=%d\n", ret);
@@ -684,9 +788,9 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char
         }
     } while (client_data.isMore);
 
-    #if TEST_MP3_DOWNLOAD
+#if TEST_MP3_DOWNLOAD
     fclose(fd);
-    #endif
+#endif
     HttpClientUnprepare(&client_data);
     HttpClientClse(&client);
     free(save_path);
@@ -705,7 +809,8 @@ static void ln_build_audio_save_path(int interface_type, int segment_index, int
                                      char *save_dir, size_t save_dir_len,
                                      char *filename, size_t filename_len)
 {
-    if (!save_dir || !filename) return;
+    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);
@@ -736,28 +841,33 @@ static void ln_build_audio_save_path(int interface_type, int segment_index, int
 /* 辅助函数:提取音频URL */
 static const char *ln_get_audio_url_for_interface(cJSON *data, int interface_type)
 {
-    if (!data) return NULL;
+    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;
+        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;
+        if (storyAudioUrl && cJSON_IsString(storyAudioUrl))
+            return storyAudioUrl->valuestring;
         return NULL;
     }
     cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl");
-    if (audioUrl && cJSON_IsString(audioUrl)) return audioUrl->valuestring;
+    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;
+    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));
@@ -770,18 +880,18 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf
 {
     if (!json_data)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] invalid json_data\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] invalid json_data\n");
         return ERRCODE_FAIL;
     }
 
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] parsing JSON data (interface_type=%d, segment=%d): %s\n", 
-           interface_type, segment_index, json_data);
+    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)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[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;
     }
 
@@ -793,7 +903,7 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf
         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);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] server error: %s, message=%s\n", err, msg_str);
 
         const char *audio_url_err = NULL;
         const char *text_content_err = NULL;
@@ -801,9 +911,11 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf
         if (data_err && cJSON_IsObject(data_err))
         {
             cJSON *audioUrl = cJSON_GetObjectItem(data_err, "audioUrl");
-            if (audioUrl && cJSON_IsString(audioUrl)) audio_url_err = audioUrl->valuestring;
+            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;
+            if (textContent && cJSON_IsString(textContent))
+                text_content_err = textContent->valuestring;
         }
 
         if (text_content_err && strlen(text_content_err) > 0)
@@ -838,7 +950,7 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf
     cJSON *data = cJSON_GetObjectItem(root, "data");
     if (!data || !cJSON_IsObject(data))
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[JSON_PARSE] no data field found\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] no data field found\n");
         cJSON_Delete(root);
         return ERRCODE_FAIL;
     }
@@ -934,7 +1046,7 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ
     while (fgets(line, 2048, fp))
     {
         line_count++;
-        
+
         // 去除行尾的换行符
         size_t len = strlen(line);
         if (len > 0 && line[len - 1] == '\n')
@@ -963,7 +1075,7 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ
             {
                 json_start++;
             }
-            
+
             uint32_t result = ln_parse_and_process_json_data(json_start, interface_type, line_count);
             if (result == ERRCODE_SUCC)
             {
@@ -984,8 +1096,8 @@ uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_typ
     fclose(fp);
     free(line);
 
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] processing completed: total lines=%d, success=%d, errors=%d\n", 
-           line_count, success_count, error_count);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] processing completed: total lines=%d, success=%d, errors=%d\n",
+                  line_count, success_count, error_count);
 
     /* 向UI上报结束状态 */
     if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV)
@@ -1038,10 +1150,11 @@ static void ln_http_server_send_task(void const *arg)
         }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_STORY_RANDOM)
         {
-            if (mail.data && mail.len == sizeof(ln_story_random_req_t)) 
+            if (mail.data && mail.len == sizeof(ln_story_random_req_t))
             {
                 printf("LN_HTTP_SERVICE_CMD_STORY_RANDOM:随机故事\n");
-                ln_story_random_req_t req; memcpy(&req, mail.data, sizeof(req));
+                ln_story_random_req_t req;
+                memcpy(&req, mail.data, sizeof(req));
                 ln_story_random_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), req.story_theme[0] ? req.story_theme : NULL);
             }
         }
@@ -1077,7 +1190,7 @@ static void ln_http_server_send_task(void const *arg)
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_EMOJI_REPORT)
         {
             printf("LN_HTTP_SERVICE_CMD_EMOJI_REPORT:上报角色表情组下载状态\n");
-            if(mail.data)
+            if (mail.data)
             {
                 ln_emoji_report_status(mail.data);
             }
@@ -1090,28 +1203,13 @@ static void ln_http_server_send_task(void const *arg)
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE)
         {
             printf("LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE:下载屏保文件\n");
-            if(mail.data)
+            if (mail.data)
             {
                 ln_download_file_req_t req;
                 memcpy(&req, mail.data, sizeof(req));
                 WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载URL: %s, 保存路径: %s", req.url, req.save_path);
-                errcode_t ret = uapi_ln_download_file(req.url, req.save_path);
-                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(req.url, req.save_path);
-                        if (ERRCODE_SUCC == ret)
-                        {
-                            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功,第%d次", i);
-                            break;
-                        }
-                    }
-                }
-                if(ret == ERRCODE_SUCC)
+                errcode_t ret = uapi_ln_download_with_retry(req.url, req.save_path, 3);
+                if (ret == ERRCODE_SUCC)
                 {
                     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功,通知去切换屏保");
                     push_screen_image_download_success_notification();
@@ -1121,16 +1219,28 @@ static void ln_http_server_send_task(void const *arg)
                     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载失败");
                 }
                 /* 释放由发送方分配的指针 */
-                if (req.url) { free(req.url); req.url = NULL; }
-                if (req.save_path) { free(req.save_path); req.save_path = NULL; }
+                if (req.url)
+                {
+                    free(req.url);
+                    req.url = NULL;
+                }
+                if (req.save_path)
+                {
+                    free(req.save_path);
+                    req.save_path = NULL;
+                }
             }
         }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER)
+        {
+            printf("LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER:日程提醒\n");
+            ln_parse_schedule_reminder(mail.data, mail.len);
+        }
         if (mail.data != NULL)
         {
             printf("free(mail.data)\n");
             free(mail.data);
         }
-        
     }
 }
 
@@ -1177,7 +1287,7 @@ static void ln_init_required_dirs(void)
 {
     const char *dirs[] = {
         LN_DATA_PATH,
-        LN_DATA_PATH "/token",  //存放token的
+        LN_DATA_PATH "/token", // 存放token的
         LN_DATA_PATH "/speech",
         LN_DATA_PATH "/story",
         LN_DATA_PATH "/character",
@@ -1195,7 +1305,7 @@ static void ln_init_required_dirs(void)
     }
 }
 
-//初始化一些配置信息
+// 初始化一些配置信息
 static void ln_load_init_config(void)
 {
     ln_read_character_config();
index d229e28..51aed1d 100644 (file)
@@ -66,6 +66,7 @@ typedef struct
 #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_REMINDER_AUDIO_FILE LN_DATA_PATH "/reminder_audio.mp3" // 日程提醒音频文件
 
 #define LN_CLIENT_PREPARE_SIZE 2 * 1024
 #define TEST_MP3_DOWNLOAD 0 // 测试下载mp3
@@ -110,7 +111,9 @@ typedef struct
 #define LN_HTTP_SERVICE_CMD_EMOJI_REPORT_ALL 0x1008  /*上报所有角色表情组状态*/
 
 /* 屏保下载文件相关命令 */
-#define LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE 0x1009 /* 下载屏保文件 */ 
+#define LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE 0x1009 /* 下载屏保文件 */
+#define LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER 0x1010     /* 日程提醒 */
+
 /* 下载文件相关结构体 */
 typedef struct
 {
@@ -197,6 +200,7 @@ const char *uapi_ln_get_audio_path(void);
 const char *uapi_ln_get_user_id(void);
 /*文件下载函数:从URL下载到指定路径*/
 int uapi_ln_download_file(const char *url, const char *save_path);
+int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_retries);
 
 /* ============================== 共享全局状态(跨文件) ============================== */
 extern char *g_device_token;
index 6a7b58f..ef89deb 100644 (file)
@@ -3,6 +3,7 @@
 #include <string.h>
 #include "cJSON.h"
 #include "ln_api.h"
+#include "vibrator_service.h"
 
 ScheduleReminder g_schedule_reminder = {0};
 ScheduleReminderConfig g_schedule_reminder_config = {0};
@@ -161,6 +162,7 @@ void ln_parse_schedule_reminder(const char *json_buf, int json_len)
     if (g_schedule_reminder.enableVibration)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] vibration:执行震动\n");
+        custom_VibratorFunc_int(0);
     }
     else
     {
@@ -172,6 +174,15 @@ void ln_parse_schedule_reminder(const char *json_buf, int json_len)
     {
         // 目前是立即提醒的所以直接下载并播放
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] download audio:执行下载并播放\n");
+        int ret = uapi_ln_download_with_retry(g_schedule_reminder.reminderAudioUrl, LN_REMINDER_AUDIO_FILE, 3);
+        if (ret == ERRCODE_SUCC)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] download audio:下载成功\n");
+        }
+        else
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] download audio:下载失败\n");
+        }
     }
     else
     {