/*****************************************************************************
|
* @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 <string.h>
|
#include <stdio.h>
|
|
/*****************************************************************************
|
* 私有宏定义
|
*****************************************************************************/
|
#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;
|
}
|