From 0e99272ccf2107930b1c1b953983d64a4f8d46ea Mon Sep 17 00:00:00 2001 From: luozheng <1732036298@qq.com> Date: Mon, 29 Sep 2025 12:20:37 +0800 Subject: [PATCH] =?utf8?q?=E6=B7=BB=E5=8A=A0=E5=B8=A6=E9=87=8D=E8=AF=95?= =?utf8?q?=E7=9A=84=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD=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 | 642 ++++++++++-------- code/sdk/zhihui/leiniao/imsl_ln/ln_api.h | 6 +- .../zhihui/leiniao/imsl_ln/ln_api_schedule.c | 11 + 3 files changed, 392 insertions(+), 267 deletions(-) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c index 89197ce62..f6df7e583 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c @@ -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, "") != 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, "") != 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, "") != 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, "") != 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(); diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h index d229e281d..51aed1d89 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h @@ -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; diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c index 6a7b58ff2..ef89debe9 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c @@ -3,6 +3,7 @@ #include #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 { -- 2.17.1