/***************************************************************************** * @file PathStorage.c * @brief 路径文件存储模块实现 * @author AI Assistant * @date 2025-12-19 *****************************************************************************/ #include "PathStorage.h" #include "MCUFlash.h" #include "HTTPClient.h" #include "Internet.h" #include "HIDO_Debug.h" #include "main.h" #include #include /***************************************************************************** * 私有宏定义 *****************************************************************************/ #define FLASH_WRITE_ALIGN 32 // FLASH写入对齐要求(32字节) #define SECTOR_SIZE (128 * 1024) // 扇区大小128KB #define DOWNLOAD_BUFFER_SIZE (32 * 1024) // HTTP下载缓冲区32KB #define MAX_RETRY_COUNT 3 // 最大重试次数 // CRC32存储位置:路径数据末尾4字节 #define CRC32_OFFSET(data_size) (data_size) /***************************************************************************** * 私有数据结构 *****************************************************************************/ typedef struct { E_PathStorageStatus status; // 当前状态 HIDO_UINT8 progress; // 下载进度0-100 HIDO_UINT32 total_size; // 文件总大小 HIDO_UINT32 received_size; // 已接收大小 HIDO_UINT32 write_offset; // 当前写入偏移 HIDO_UINT8 align_buffer[FLASH_WRITE_ALIGN]; // 对齐缓冲区 HIDO_UINT8 align_buffer_len; // 对齐缓冲区当前长度 HIDO_UINT32 crc32; // 累积CRC32 HIDO_UINT8 retry_count; // 重试计数 HIDO_CHAR url[256]; // 下载URL } PathStorageContext_t; /***************************************************************************** * 私有全局变量 *****************************************************************************/ static PathStorageContext_t s_context = { .status = PATH_STORAGE_STATUS_IDLE, .progress = 0, .total_size = 0, .received_size = 0, .write_offset = 0, .align_buffer_len = 0, .crc32 = 0, .retry_count = 0 }; /***************************************************************************** * 私有函数声明 *****************************************************************************/ static HIDO_INT32 PathStorage_HTTPCallback(HIDO_UINT32 resp_code, ST_HTTPGetFileResponse *resp, HIDO_UINT8 *data, HIDO_UINT32 len, HIDO_VOID *arg); static HIDO_INT32 PathStorage_WriteToFlash(HIDO_UINT8 *data, HIDO_UINT32 len); static HIDO_INT32 PathStorage_FlushAlignBuffer(void); static HIDO_UINT32 PathStorage_SoftwareCRC32(const HIDO_UINT8 *data, HIDO_UINT32 len, HIDO_UINT32 crc); /***************************************************************************** * CRC32查表法实现(标准CRC-32,多项式0x04C11DB7,初始值0) *****************************************************************************/ static const HIDO_UINT32 crc32_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; /** * @brief 软件CRC32计算(查表法)- 匹配binascii.crc32(data, 0) * @param data 数据指针 * @param len 数据长度(字节) * @param crc 当前CRC值(传入上次结果,首次传入0) * @return 计算得到的CRC32 * * 注意:这个实现匹配Python的binascii.crc32(data, 0) * 内部使用标准CRC32查表,但需要对输入和输出做XOR转换 */ static HIDO_UINT32 PathStorage_SoftwareCRC32(const HIDO_UINT8 *data, HIDO_UINT32 len, HIDO_UINT32 crc) { // 转换:binascii.crc32(data, crc) = ~crc32_standard(data, ~crc) // 首次调用时crc=0,转换为标准CRC32的初始值0xFFFFFFFF crc = ~crc; // 标准查表CRC32算法 while (len--) { crc = crc32_table[(crc ^ *data++) & 0xFF] ^ (crc >> 8); } // 转换回binascii.crc32格式 return ~crc; } /***************************************************************************** * 函数实现 *****************************************************************************/ /** * @brief 初始化路径存储模块 */ HIDO_INT32 PathStorage_Init(void) { HIDO_INT32 ret; // STM32硬件CRC已在main.c中初始化 // 初始化FLASH ret = MCUFlash_Init(); if (ret != 0) { HIDO_Debug("[PathStorage] MCUFlash_Init failed: %d\r\n", ret); return ret; } // 初始化上下文 memset(&s_context, 0, sizeof(s_context)); s_context.status = PATH_STORAGE_STATUS_IDLE; HIDO_Debug("[PathStorage] Init success, base addr: 0x%08X\r\n", PATH_STORAGE_BASE_ADDR); return 0; } /** * @brief 从HTTP下载路径文件并存储到FLASH */ HIDO_INT32 PathStorage_DownloadFromHTTP(const HIDO_CHAR *url) { HIDO_INT32 ret; if (url == NULL) { HIDO_Debug("[PathStorage] Invalid URL\r\n"); return -1; } // 检查网络状态 if (!Internet_IsIPReady()) { HIDO_Debug("[PathStorage] Network not ready\r\n"); s_context.status = PATH_STORAGE_STATUS_FAIL_NETWORK; return -2; } // 检查状态 if (s_context.status == PATH_STORAGE_STATUS_DOWNLOADING) { HIDO_Debug("[PathStorage] Already downloading\r\n"); return -3; } // 重置上下文 memset(&s_context, 0, sizeof(s_context)); strncpy(s_context.url, url, sizeof(s_context.url) - 1); s_context.status = PATH_STORAGE_STATUS_DOWNLOADING; s_context.retry_count = 0; // 擦除FLASH(擦除4个扇区=512KB) HIDO_Debug("[PathStorage] Erasing FLASH...\r\n"); ret = MCUFlash_Erase(PATH_STORAGE_BASE_ADDR, PATH_STORAGE_MAX_SIZE); if (ret != 0) { HIDO_Debug("[PathStorage] MCUFlash_Erase failed: %d\r\n", ret); s_context.status = PATH_STORAGE_STATUS_FAIL_FLASH; return -4; } // ===== 详细诊断开始 ===== HIDO_Debug("[PathStorage] ===== Pre-Download Diagnostics =====\r\n"); // 1. 检查网络状态 HIDO_BOOL network_ready = Internet_IsIPReady(); HIDO_Debug("[PathStorage] 1. Network Status: %s\r\n", network_ready ? "READY" : "NOT READY"); if (!network_ready) { HIDO_Debug("[PathStorage] ERROR: Network not ready!\r\n"); s_context.status = PATH_STORAGE_STATUS_FAIL_NETWORK; return -5; } // 2. 检查HTTPClient状态 extern HIDO_INT32 HTTPClient_GetState(HIDO_VOID); HIDO_INT32 http_state = HTTPClient_GetState(); HIDO_Debug("[PathStorage] 2. HTTPClient State: %d (0=IDLE, 1=CONNECTING, 2=CONNECTED)\r\n", http_state); if (http_state != 0) { HIDO_Debug("[PathStorage] WARNING: HTTPClient not IDLE, trying to reset...\r\n"); extern HIDO_VOID HTTPClient_Reset(HIDO_VOID); HTTPClient_Reset(); // 短暂延迟确保重置生效 for(volatile HIDO_UINT32 i = 0; i < 100000; i++); http_state = HTTPClient_GetState(); HIDO_Debug("[PathStorage] HTTPClient State after reset: %d\r\n", http_state); } // 3. 检查URL参数 HIDO_Debug("[PathStorage] 3. URL: %s\r\n", url); HIDO_Debug("[PathStorage] 4. URL Length: %u\r\n", strlen(url)); // 准备HTTP下载选项(不使用Range头,下载完整文件) ST_HTTPGetFileOpt opt; memset(&opt, 0, sizeof(opt)); opt.m_u32RangeBegin = 0; opt.m_u32RangeEnd = 0; // 0表示不限制结束位置,让服务器返回完整文件 opt.m_acIfRange[0] = '\0'; HIDO_Debug("[PathStorage] 5. HTTP Options: Range=%u-%u\r\n", opt.m_u32RangeBegin, opt.m_u32RangeEnd); // 6. 检查回调函数 HIDO_Debug("[PathStorage] 6. Callback Function: 0x%08X\r\n", (HIDO_UINT32)PathStorage_HTTPCallback); HIDO_Debug("[PathStorage] ===== Starting HTTP Request =====\r\n"); // 启动HTTP下载 ret = HTTPClient_GetFile((HIDO_CHAR *)url, PathStorage_HTTPCallback, &opt, NULL); HIDO_Debug("[PathStorage] HTTPClient_GetFile returned: %d\r\n", ret); if (ret != 0) { HIDO_Debug("[PathStorage] ===== HTTP Request FAILED =====\r\n"); HIDO_Debug("[PathStorage] Final diagnostics:\r\n"); HIDO_Debug(" - Network Ready: %s\r\n", Internet_IsIPReady() ? "YES" : "NO"); HIDO_Debug(" - HTTPClient State: %d\r\n", HTTPClient_GetState()); HIDO_Debug(" - URL valid: %s\r\n", (url && strlen(url) > 0) ? "YES" : "NO"); HIDO_Debug(" - Options valid: YES (stack)\r\n"); HIDO_Debug("[PathStorage] Please check HTTPClient_GetFile implementation\r\n"); s_context.status = PATH_STORAGE_STATUS_FAIL_NETWORK; return -5; } HIDO_Debug("[PathStorage] ===== HTTP Request Started Successfully =====\r\n"); return 0; } /** * @brief HTTP下载回调函数 */ static HIDO_INT32 PathStorage_HTTPCallback(HIDO_UINT32 resp_code, ST_HTTPGetFileResponse *resp, HIDO_UINT8 *data, HIDO_UINT32 len, HIDO_VOID *arg) { HIDO_INT32 ret; // 检查响应 if (resp == NULL) { HIDO_Debug("[PathStorage] HTTP response is NULL\r\n"); s_context.status = PATH_STORAGE_STATUS_FAIL_NETWORK; return -1; } // 首次接收,记录文件总大小 if (s_context.total_size == 0) { s_context.total_size = resp->m_u32RangeTotal; HIDO_Debug("[PathStorage] Total size: %u bytes\r\n", s_context.total_size); // 检查大小是否合理(至少要有头部40字节) if (s_context.total_size < sizeof(MowerPathHeader_t)) { HIDO_Debug("[PathStorage] File too small: %u\r\n", s_context.total_size); s_context.status = PATH_STORAGE_STATUS_FAIL_FORMAT; return -1; } // 检查是否超过存储空间 if (s_context.total_size > PATH_STORAGE_MAX_SIZE - 4) { // 预留4字节存CRC32 HIDO_Debug("[PathStorage] File too large: %u\r\n", s_context.total_size); s_context.status = PATH_STORAGE_STATUS_FAIL_FLASH; return -1; } } // 数据为NULL表示下载结束 if (data == NULL || len == 0) { // 刷新对齐缓冲区 ret = PathStorage_FlushAlignBuffer(); if (ret != 0) { HIDO_Debug("[PathStorage] Flush align buffer failed\r\n"); s_context.status = PATH_STORAGE_STATUS_FAIL_FLASH; return -1; } // 将CRC32写入FLASH末尾 HIDO_UINT32 crc32_final = s_context.crc32; ret = MCUFlash_Write(PATH_STORAGE_BASE_ADDR + s_context.write_offset, (HIDO_UINT8 *)&crc32_final, sizeof(crc32_final)); if (ret != 0) { HIDO_Debug("[PathStorage] Write CRC32 failed: %d\r\n", ret); s_context.status = PATH_STORAGE_STATUS_FAIL_FLASH; return -1; } HIDO_Debug("[PathStorage] Download complete, size: %u, CRC32: 0x%08X\r\n", s_context.received_size, crc32_final); // 验证数据 ret = PathStorage_VerifyCRC32(); if (ret == 0) { s_context.status = PATH_STORAGE_STATUS_SUCCESS; s_context.progress = 100; HIDO_Debug("[PathStorage] Storage success!\r\n"); } else { s_context.status = PATH_STORAGE_STATUS_FAIL_CRC; HIDO_Debug("[PathStorage] CRC verify failed\r\n"); } return 0; } // 接收数据 s_context.received_size += len; // 更新进度 if (s_context.total_size > 0) { s_context.progress = (HIDO_UINT8)((s_context.received_size * 100) / s_context.total_size); } // 累积计算CRC32(使用软件CRC32,初始值0) s_context.crc32 = PathStorage_SoftwareCRC32(data, len, s_context.crc32); // 写入FLASH(处理32字节对齐) ret = PathStorage_WriteToFlash(data, len); if (ret != 0) { HIDO_Debug("[PathStorage] Write to flash failed: %d\r\n", ret); s_context.status = PATH_STORAGE_STATUS_FAIL_FLASH; return -1; } HIDO_Debug("[PathStorage] Progress: %u%% (%u/%u bytes)\r\n", s_context.progress, s_context.received_size, s_context.total_size); return 0; } /** * @brief 写入数据到FLASH(处理32字节对齐) */ static HIDO_INT32 PathStorage_WriteToFlash(HIDO_UINT8 *data, HIDO_UINT32 len) { HIDO_INT32 ret; HIDO_UINT32 write_len; HIDO_UINT32 offset = 0; // 先处理对齐缓冲区中的剩余数据 if (s_context.align_buffer_len > 0) { HIDO_UINT32 need = FLASH_WRITE_ALIGN - s_context.align_buffer_len; HIDO_UINT32 copy = (len < need) ? len : need; memcpy(s_context.align_buffer + s_context.align_buffer_len, data, copy); s_context.align_buffer_len += copy; offset += copy; // 如果凑够了32字节,写入FLASH if (s_context.align_buffer_len == FLASH_WRITE_ALIGN) { ret = MCUFlash_Write(PATH_STORAGE_BASE_ADDR + s_context.write_offset, s_context.align_buffer, FLASH_WRITE_ALIGN); if (ret != 0) { return ret; } s_context.write_offset += FLASH_WRITE_ALIGN; s_context.align_buffer_len = 0; } } // 写入32字节对齐的数据块 write_len = (len - offset) / FLASH_WRITE_ALIGN * FLASH_WRITE_ALIGN; if (write_len > 0) { ret = MCUFlash_Write(PATH_STORAGE_BASE_ADDR + s_context.write_offset, data + offset, write_len); if (ret != 0) { return ret; } s_context.write_offset += write_len; offset += write_len; } // 剩余不足32字节的数据放入对齐缓冲区 if (offset < len) { HIDO_UINT32 remain = len - offset; memcpy(s_context.align_buffer, data + offset, remain); s_context.align_buffer_len = remain; } return 0; } /** * @brief 刷新对齐缓冲区(补0xFF后写入) */ static HIDO_INT32 PathStorage_FlushAlignBuffer(void) { HIDO_INT32 ret; if (s_context.align_buffer_len == 0) { return 0; } // 补0xFF到32字节对齐 memset(s_context.align_buffer + s_context.align_buffer_len, 0xFF, FLASH_WRITE_ALIGN - s_context.align_buffer_len); // 写入FLASH ret = MCUFlash_Write(PATH_STORAGE_BASE_ADDR + s_context.write_offset, s_context.align_buffer, FLASH_WRITE_ALIGN); if (ret != 0) { return ret; } s_context.write_offset += FLASH_WRITE_ALIGN; s_context.align_buffer_len = 0; return 0; } /** * @brief 从FLASH读取路径文件头 */ HIDO_INT32 PathStorage_ReadHeader(MowerPathHeader_t *header) { HIDO_INT32 ret; if (header == NULL) { return -1; } // 从FLASH读取头部 ret = MCUFlash_Read(PATH_STORAGE_BASE_ADDR, (HIDO_UINT8 *)header, sizeof(MowerPathHeader_t)); if (ret != 0) { HIDO_Debug("[PathStorage] Read header failed: %d\r\n", ret); return ret; } // 验证头部 if (header->sof != PATH_FILE_SOF) { HIDO_Debug("[PathStorage] Invalid SOF: 0x%04X\r\n", header->sof); return -2; } if (header->type != PATH_FILE_TYPE) { HIDO_Debug("[PathStorage] Invalid type: 0x%02X\r\n", header->type); return -3; } if (header->version != PATH_FILE_VERSION) { HIDO_Debug("[PathStorage] Version mismatch: 0x%02X\r\n", header->version); // 版本不匹配只警告,不返回错误 } HIDO_Debug("[PathStorage] Header valid - PathID: 0x%08X, Origin: (%.8f, %.8f, %.2f), Boundary: %u, Path: %u\r\n", header->path_id, header->origin_lon, header->origin_lat, header->origin_alt, header->boundary_count, header->path_count); return 0; } /** * @brief 从FLASH读取指定的路径点数据 */ HIDO_INT32 PathStorage_ReadPoint(HIDO_BOOL is_boundary, HIDO_UINT32 index, PathPoint_t *point) { HIDO_INT32 ret; HIDO_UINT32 offset; MowerPathHeader_t header; if (point == NULL) { return -1; } // 读取头部获取点数信息 ret = PathStorage_ReadHeader(&header); if (ret != 0) { return ret; } // 检查索引范围 if (is_boundary) { if (index >= header.boundary_count) { HIDO_Debug("[PathStorage] Boundary index out of range: %u/%u\r\n", index, header.boundary_count); return -2; } offset = sizeof(MowerPathHeader_t) + index * sizeof(PathPoint_t); } else { if (index >= header.path_count) { HIDO_Debug("[PathStorage] Path index out of range: %u/%u\r\n", index, header.path_count); return -2; } offset = sizeof(MowerPathHeader_t) + header.boundary_count * sizeof(PathPoint_t) + index * sizeof(PathPoint_t); } // 从FLASH读取点数据 ret = MCUFlash_Read(PATH_STORAGE_BASE_ADDR + offset, (HIDO_UINT8 *)point, sizeof(PathPoint_t)); if (ret != 0) { HIDO_Debug("[PathStorage] Read point failed: %d\r\n", ret); return ret; } return 0; } /** * @brief 批量读取路径点数据 */ HIDO_INT32 PathStorage_ReadPoints(HIDO_BOOL is_boundary, HIDO_UINT32 start_index, HIDO_UINT32 count, PathPoint_t *points) { HIDO_INT32 ret; HIDO_UINT32 offset; HIDO_UINT32 max_count; HIDO_UINT32 read_count; MowerPathHeader_t header; if (points == NULL || count == 0) { return -1; } // 读取头部获取点数信息 ret = PathStorage_ReadHeader(&header); if (ret != 0) { return ret; } // 检查索引范围并调整读取数量 if (is_boundary) { max_count = header.boundary_count; offset = sizeof(MowerPathHeader_t) + start_index * sizeof(PathPoint_t); } else { max_count = header.path_count; offset = sizeof(MowerPathHeader_t) + header.boundary_count * sizeof(PathPoint_t) + start_index * sizeof(PathPoint_t); } if (start_index >= max_count) { HIDO_Debug("[PathStorage] Start index out of range: %u/%u\r\n", start_index, max_count); return -2; } read_count = (start_index + count > max_count) ? (max_count - start_index) : count; // 从FLASH批量读取 ret = MCUFlash_Read(PATH_STORAGE_BASE_ADDR + offset, (HIDO_UINT8 *)points, read_count * sizeof(PathPoint_t)); if (ret != 0) { HIDO_Debug("[PathStorage] Read points failed: %d\r\n", ret); return ret; } return read_count; } /** * @brief 获取当前存储状态 */ E_PathStorageStatus PathStorage_GetStatus(void) { return s_context.status; } /** * @brief 获取下载进度 */ HIDO_UINT8 PathStorage_GetProgress(void) { return s_context.progress; } /** * @brief 擦除FLASH中的路径数据 */ HIDO_INT32 PathStorage_Erase(void) { HIDO_INT32 ret; HIDO_Debug("[PathStorage] Erasing...\r\n"); ret = MCUFlash_Erase(PATH_STORAGE_BASE_ADDR, PATH_STORAGE_MAX_SIZE); if (ret != 0) { HIDO_Debug("[PathStorage] Erase failed: %d\r\n", ret); return ret; } // 重置上下文 memset(&s_context, 0, sizeof(s_context)); s_context.status = PATH_STORAGE_STATUS_IDLE; HIDO_Debug("[PathStorage] Erase success\r\n"); return 0; } /** * @brief 验证FLASH中路径文件的CRC32 */ HIDO_INT32 PathStorage_VerifyCRC32(void) { HIDO_INT32 ret; MowerPathHeader_t header; HIDO_UINT32 data_size; HIDO_UINT32 stored_crc32; HIDO_UINT32 calc_crc32; HIDO_UINT8 buffer[256]; HIDO_UINT32 offset = 0; // 读取头部 ret = PathStorage_ReadHeader(&header); if (ret != 0) { return ret; } // 计算数据大小(头部 + 所有点坐标) data_size = sizeof(MowerPathHeader_t) + (header.boundary_count + header.path_count) * sizeof(PathPoint_t); // 读取存储的CRC32(在数据末尾) ret = MCUFlash_Read(PATH_STORAGE_BASE_ADDR + data_size, (HIDO_UINT8 *)&stored_crc32, sizeof(stored_crc32)); if (ret != 0) { HIDO_Debug("[PathStorage] Read stored CRC32 failed: %d\r\n", ret); return ret; } // 分块读取并计算CRC32(使用软件CRC32,初始值0) calc_crc32 = 0; while (offset < data_size) { HIDO_UINT32 read_len = (data_size - offset > sizeof(buffer)) ? sizeof(buffer) : (data_size - offset); ret = MCUFlash_Read(PATH_STORAGE_BASE_ADDR + offset, buffer, read_len); if (ret != 0) { HIDO_Debug("[PathStorage] Read data for CRC failed: %d\r\n", ret); return ret; } // 累积计算软件CRC32 calc_crc32 = PathStorage_SoftwareCRC32(buffer, read_len, calc_crc32); offset += read_len; } // 比对CRC32 if (calc_crc32 != stored_crc32) { HIDO_Debug("[PathStorage] CRC32 mismatch - Stored: 0x%08X, Calc: 0x%08X\r\n", stored_crc32, calc_crc32); return -1; } HIDO_Debug("[PathStorage] CRC32 verify OK: 0x%08X\r\n", calc_crc32); return 0; }