From 9206f481748f9b85275717ea7549b5989f2b6c90 Mon Sep 17 00:00:00 2001 From: luozheng <1732036298@qq.com> Date: Tue, 28 Oct 2025 09:58:17 +0800 Subject: [PATCH] =?utf8?q?ota=E6=B7=BB=E5=8A=A0=E6=96=AD=E7=82=B9=E7=BB=AD?= =?utf8?q?=E4=BC=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c | 230 ++++++++++++++----- 1 file changed, 170 insertions(+), 60 deletions(-) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c index ce867d04f..eac265b6b 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c @@ -80,7 +80,6 @@ static int ln_ota_is_md5_ok(const char *file_path, const char *expected_md5) static void ln_ota_handle_download_msg(const OtaDownloadMsg *msg) { - static int reset_cnt = 0; if (!msg) return; switch (msg->status) { case OTA_DOWNLOAD_STATUS_PROGRESS: @@ -110,14 +109,8 @@ static void ln_ota_handle_download_msg(const OtaDownloadMsg *msg) break; case OTA_DOWNLOAD_STATUS_FAILED: WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载失败: %s", msg->error_msg); - reset_cnt++; - if (reset_cnt < 3) { - do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0); - } - else { - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); - push_ota_status(OTA_UI_STATUS_FAILED); - } + uapi_pm_remove_sleep_veto(PM_ID_DEBUG); + push_ota_status(OTA_UI_STATUS_FAILED); break; default: WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 未知状态: %d", msg->status); @@ -374,18 +367,43 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, HttpClient client = {0}; HttpClientData client_data = {0}; int ret = -1; - size_t total_written = 0; // 已写入文件的总字节数 + size_t total_written = 0; // 本次会话写入的字节数 int last_percent = -1; // 上次上报的进度百分比 - // 打开文件准备写入 - FILE *fd = fopen(save_path, "wb+"); + /* 计算断点偏移:已存在部分文件则续传 */ + long long resume_offset = 0; + { + FILE *fexist = fopen(save_path, "rb"); + if (fexist) + { + if (fseek(fexist, 0, SEEK_END) == 0) + { + long pos = ftell(fexist); + if (pos > 0) + resume_offset = (long long)pos; + } + fclose(fexist); + } + } + if (resume_offset > 0) + { + printf("[OTA] resume enabled, offset=%lld\n", resume_offset); + if (resume_offset >= (long long)total_size) + { + printf("[OTA] local file already complete: %lld >= %d\n", resume_offset, total_size); + return 0; // 视为已完成 + } + } + + // 打开文件(续传用 ab+,全量用 wb+) + FILE *fd = fopen(save_path, resume_offset > 0 ? "ab+" : "wb+"); if (!fd) { printf("[OTA] fopen failed for %s:\n", save_path); return -1; } - HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 4); + HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 2); if (prep != HTTP_SUCCESS) { printf("[OTA] range prepare failed: %d\n", prep); @@ -393,11 +411,34 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, return -1; } + // 当存在偏移时,设置 Range 头 + char *header_buf = NULL; + if (resume_offset > 0) + { + header_buf = (char *)malloc(128); + if (!header_buf) + { + HttpClientUnprepare(&client_data); + fclose(fd); + return -1; + } + int hdr = snprintf(header_buf, 128, "Range: bytes=%lld-\r\n", resume_offset); + if (hdr <= 0) + { + free(header_buf); + HttpClientUnprepare(&client_data); + fclose(fd); + return -1; + } + HttpClientSetCustomHeader(&client, header_buf); + } + // 建立连接(与http_download流程一致) ret = HttpClientConn(&client, full_url); if (ret != 0) { printf("[OTA] connection failed, ret=0x%x\n", ret); + if (header_buf) free(header_buf); fclose(fd); HttpClientUnprepare(&client_data); return ret; @@ -408,38 +449,97 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, if (ret != 0) { printf("[OTA] send request failed, ret=0x%x\n", ret); + if (header_buf) free(header_buf); HttpClientClse(&client); fclose(fd); HttpClientUnprepare(&client_data); return ret; } - // 循环接收响应数据(参考http_download的do-while循环) printf("[OTA] starting to receive data...\n"); - do + // 先接收首包以判定状态码;如服务端未按 Range 返回(200),需覆盖写 + HTTPC_RESULT first_recv = HttpClientRecvResponse(&client, &client_data); + if (!(first_recv == HTTP_SUCCESS || first_recv == HTTP_EAGAIN)) + { + printf("[OTA] first recv failed: %d\n", first_recv); + if (header_buf) free(header_buf); + HttpClientClse(&client); + fclose(fd); + HttpClientUnprepare(&client_data); + return -1; + } + + int code = HttpClientGetResponseCode(&client); + printf("[OTA] response code: %d (offset=%lld)\n", code, resume_offset); + if (code == 200 && resume_offset > 0) + { + // 服务端未按Range返回,改为全量覆盖 + fclose(fd); + fd = fopen(save_path, "wb+"); + if (!fd) + { + if (header_buf) free(header_buf); + HttpClientClse(&client); + HttpClientUnprepare(&client_data); + return -1; + } + resume_offset = 0; + total_written = 0; + last_percent = -1; + } + + // 写入首块内容(若有) + if (client_data.contentBlockLen > 0) { - ret = HttpClientRecvResponse(&client, &client_data); - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_ota_download_flow_http:%d", ret); - if (ret == HTTP_SUCCESS || ret == HTTP_EAGAIN) + size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd); + if (written != client_data.contentBlockLen) + { + printf("[OTA] write failed on first block, written=%zu, expected=%d\n", written, client_data.contentBlockLen); + if (header_buf) free(header_buf); + fclose(fd); + HttpClientClse(&client); + HttpClientUnprepare(&client_data); + return -1; + } + total_written += written; + + // 进度上报(基于 resume_offset + total_written) + long long current_total = resume_offset + (long long)total_written; + int percent = (int)((current_total * 100) / total_size); + if (percent > 100) percent = 100; + if (percent != last_percent) + { + last_percent = percent; + OtaDownloadMsg om = {0}; + om.status = OTA_DOWNLOAD_STATUS_PROGRESS; + om.progress = percent; + ln_ota_handle_download_msg(&om); + } + } + + // 后续接收循环 + while (client_data.isMore) + { + HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data); + if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN) { - // 处理接收到的数据块 if (client_data.contentBlockLen > 0) { - // 写入文件(与http_download的写入逻辑一致) - size_t written = fwrite(client_data.responseBuf, 1, - client_data.contentBlockLen, fd); + size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd); if (written != client_data.contentBlockLen) { - printf("[OTA] write file failed, written=%zu, expected=%d\n", - written, client_data.contentBlockLen); - ret = -1; - break; + printf("[OTA] write failed, written=%zu, expected=%d\n", written, client_data.contentBlockLen); + if (header_buf) free(header_buf); + fclose(fd); + HttpClientClse(&client); + HttpClientUnprepare(&client_data); + return -1; } total_written += written; - // 进度上报(使用已知的total_size计算) - int percent = (int)((total_written * 100) / total_size); - if (percent > 100) - percent = 100; // 防止超过100% + + long long current_total = resume_offset + (long long)total_written; + int percent = (int)((current_total * 100) / total_size); + if (percent > 100) percent = 100; if (percent != last_percent) { last_percent = percent; @@ -449,56 +549,50 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, ln_ota_handle_download_msg(&om); } - // 检查是否超过预期总大小 - if (total_written > (size_t)total_size) + if (current_total > (long long)total_size) { - printf("[OTA] received more than expected! total_written=%zu > total_size=%d\n", - total_written, total_size); - ret = -1; - break; + printf("[OTA] received more than expected! current_total=%lld > total_size=%d\n", current_total, total_size); + if (header_buf) free(header_buf); + fclose(fd); + HttpClientClse(&client); + HttpClientUnprepare(&client_data); + return -1; } } else { - // 避免空块忙轮询占用CPU + // 避免空块忙轮询 LOS_Msleep(50); } } else { - printf("[OTA] receive failed, ret=0x%x\n", ret); + printf("[OTA] recv failed: %d\n", recv_res); break; } - } while (client_data.isMore && total_written < (size_t)total_size); // 双重条件判断 - // 检查下载结果(参考http_download的结果校验) - if (ret == 0 && client.responseCode == 200) + } + + int final_code = HttpClientGetResponseCode(&client); + long long downloaded = resume_offset + (long long)total_written; + printf("[OTA] final code=%d, downloaded=%lld/%d\n", final_code, downloaded, total_size); + + if ((final_code == 200 || final_code == 206) && downloaded == (long long)total_size) { - // 验证是否下载完整(使用已知的total_size) - if (total_written == (size_t)total_size) - { - printf("[OTA] download complete! total size: %d bytes\n", total_size); - ret = 0; - } - else - { - printf("[OTA] download incomplete! received=%zu, expected=%d\n", - total_written, total_size); - ret = -1; - } + ret = 0; + printf("[OTA] download complete! total size: %d bytes (%s)\n", total_size, resume_offset > 0 ? "续传resume" : "全量full"); } else { - printf("[OTA] download failed! response code: %d, ret=0x%x\n", - client.responseCode, ret); + ret = -1; + printf("[OTA] download incomplete or failed (code=%d)\n", final_code); } - printf("[OTA] received=%zu, expected=%d\n", - total_written, total_size); - // 资源清理(与http_download保持一致) + // 资源清理 + if (header_buf) free(header_buf); fclose(fd); HttpClientClse(&client); HttpClientUnprepare(&client_data); - + printf("[OTA] download process finished\n"); return ret; @@ -550,7 +644,23 @@ uint32_t ln_ota_download_flow_http(const char *device_id, int dres; if (g_ln_last_package_size > 0) { - dres = ln_download_file_to_path(fixed_url, save_path, g_ln_last_package_size); + // 若文件存在,则删除文件 + if(access(save_path, F_OK) == 0) + { + if(remove(save_path) != 0) + { + printf("[OTA] remove file failed: %s\n", save_path); + return ERRCODE_FAIL; + } + } + for(int i = 0; i < 3; i++) + { + dres = ln_download_file_to_path(fixed_url, save_path, g_ln_last_package_size); + if(dres == 0) + { + break; + } + } } if (dres != 0) { -- 2.17.1