# 离线数据存储和补传功能说明

## 功能概述

本设备支持离线数据存储功能，当网络断开时，数据会自动存储到Flash文件系统中；当网络恢复后，数据会自动补传到MQTT服务器。

## 系统架构

```
┌─────────────────────────────────────────────────┐
│                   应用层                          │
├─────────────────────────────────────────────────┤
│  MODBUS数据     设备状态      MQTT客户端         │
│       │              │              │            │
│       ▼              ▼              ▼            │
│  ┌─────────────────────────────────────┐       │
│  │        离线数据管理模块               │       │
│  │    (OFFLINE_STORAGE 组件)            │       │
│  └─────────────────────────────────────┘       │
│       │                                    │    │
│       ▼                                    │    │
│  ┌─────────────────────────────────────┐       │
│  │     SPIFFS文件系统                 │       │
│  │    (FLASH_SPIFS 组件)               │       │
│  └─────────────────────────────────────┘       │
│       │                                    │    │
│       ▼                                    │    │
│  ┌─────────────────────────────────────┐       │
│  │       16MB Flash存储区               │       │
│  │          (8MB可用)                   │       │
│  └─────────────────────────────────────┘       │
└─────────────────────────────────────────────────┘
```

## 分区表配置

```csv
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x6000,
phy_init, data, phy,     0xf000,  0x1000,
factory,  app,  factory, 0x10000, 2MB,
storage,  data, spiffs,  0x210000,8MB,
```

- **storage分区**：8MB，用于离线数据存储
- **挂载点**：`/flash`
- **文件系统**：SPIFFS

## 存储容量估算

| 参数 | 数值 |
|------|------|
| Flash总容量 | 16MB |
| 存储分区大小 | 8MB |
| MODBUS数据量 | 200字节/条（1秒/次） |
| 每小时数据量 | 720KB |
| 可存储时长 | 约11小时 |

**注意**：SPIFFS最小文件大小为4KB，200字节的文件实际占用4KB空间，因此实际可存储的时长会更短。建议将多条数据合并到一个文件中存储以提高空间利用率。

## 文件组织结构

```
/flash/
├── data/
│   ├── modbus/              # MODBUS采集数据
│   │   ├── 1738452345.json  # 时间戳命名
│   │   ├── 1738452346.json
│   │   └── ...
│   └── status/              # 设备状态数据
│       ├── 1738452345.json
│       └── ...
└── index.json              # 索引文件
```

## 工作流程

### 正常模式（网络在线）

```
数据产生
  ↓
检测网络状态（在线）
  ↓
直接发布到MQTT
  ↓
不存储
```

### 离线模式（网络断开）

```
数据产生
  ↓
检测网络状态（离线）
  ↓
存储到Flash SPIFFS
  ↓
写入成功
  ↓
等待网络恢复
```

### 补传模式（网络恢复）

```
检测网络状态（在线）
  ↓
检查是否有离线数据
  ↓
读取最旧的数据
  ↓
发布到MQTT
  ↓
发布成功？
  ├─ 是 → 删除该数据 → 继续下一条
  └─ 否 → 等待重试
```

## API接口

### FLASH_SPIFS 组件

#### 初始化文件系统

```c
esp_err_t flash_spiffs_init(void);
```

#### 检查是否挂载

```c
bool flash_spiffs_is_mounted(void);
```

#### 获取存储使用情况

```c
uint32_t flash_spiffs_get_total_size(void);
uint32_t flash_spiffs_get_used_size(void);
```

#### 格式化文件系统

```c
esp_err_t flash_spiffs_format(void);
```

### OFFLINE_STORAGE 组件

#### 初始化存储模块

```c
esp_err_t offline_storage_init(void);
```

#### 存储离线数据

```c
esp_err_t offline_storage_store(const char *data, size_t length, offline_data_type_t data_type);
```

**参数**：
- `data`：数据内容（JSON字符串）
- `length`：数据长度
- `data_type`：数据类型
  - `OFFLINE_DATA_TYPE_MODBUS`：MODBUS采集数据
  - `OFFLINE_DATA_TYPE_DEVICE_STATUS`：设备状态数据

#### 读取最旧的数据

```c
esp_err_t offline_storage_read_oldest(char *buffer, size_t max_len, offline_data_type_t *out_data_type);
```

#### 删除最旧的数据

```c
esp_err_t offline_storage_delete_oldest(void);
```

#### 获取数据数量

```c
uint32_t offline_storage_get_count(void);
bool offline_storage_has_data(void);
```

#### 清空所有数据

```c
esp_err_t offline_storage_clear_all(void);
```

### MQTT_ESP 扩展接口

#### 存储离线数据（内部使用）

```c
esp_err_t mqtt_store_offline(const char *data, size_t length, offline_data_type_t data_type);
```

#### 启动/停止补传任务

```c
BaseType_t mqtt_start_offline_upload_task(void);
void mqtt_stop_offline_upload_task(void);
```

## 网络状态检测

系统通过MQTT连接状态判断网络是否在线：

| MQTT事件 | 网络状态 | 处理 |
|---------|---------|------|
| `MQTT_EVENT_CONNECTED` | 在线 | 启动补传任务 |
| `MQTT_EVENT_DISCONNECTED` | 离线 | 停止补传任务 |

## 存储策略

### 自动循环覆盖

- **最大文件数限制**：10,000个文件
- **触发条件**：当文件数超过限制时，自动删除最旧的数据
- **优先级**：按时间戳排序，删除最旧的数据

### 数据完整性保护

- **索引文件**：`/flash/index.json` 记录所有文件信息
- **原子写入**：使用临时文件+重命名机制
- **断电保护**：SPIFFS提供基本断电保护

### 存储空间管理

```
获取空间使用情况
  ↓
检查可用空间
  ↓
空间不足？
  ├─ 否 → 正常存储
  └─ 是 → 删除最旧数据 → 重试
```

## SPIFFS 特性说明

### 优点
- ESP-IDF原生支持，无需额外组件
- 代码成熟稳定
- 内存占用小（约2KB）
- 挂载速度快

### 缺点
- 最小文件大小为4KB，小文件浪费空间
- 无磨损均衡，Flash寿命相对较短
- 碎片严重，长期使用后性能下降
- 不支持目录（所有文件在根目录）

### 优化建议

1. **合并多条数据到单个文件**：减少文件数量，提高空间利用率
2. **定期格式化**：消除碎片，恢复性能
3. **限制文件数量**：避免碎片积累

## 日志输出

### 初始化日志

```
I (xxxx) main: Initializing SPIFFS file system...
I (xxxx) FLASH_SPIFS: 正在初始化SPIFFS文件系统...
I (xxxx) FLASH_SPIFS: SPIFFS文件系统挂载成功
I (xxxx) FLASH_SPIFS: 总空间: 8388608 字节
I (xxxx) FLASH_SPIFS: 已用空间: 4096 字节
I (xxxx) FLASH_SPIFS: 可用空间: 8384512 字节
I (xxxx) FLASH_SPIFS: 挂载点: /flash
I (xxxx) main: Initializing offline storage module...
I (xxxx) main: 离线存储模块初始化完成
```

### 存储日志

```
I (xxxx) mqtt_esp: Storing offline data (type=1, size=256)
I (xxxx) OFFLINE_STORAGE: 存储数据: /flash/data/modbus/1738452345.json, 大小: 256 字节
I (xxxx) OFFLINE_STORAGE: Storage usage: 4096 / 8388608 bytes (0.0%)
W (xxxx) OFFLINE_STORAGE: 注意：SPIFFS最小文件大小为4KB，小文件会占用更多空间
```

### 补传日志

```
I (xxxx) mqtt_esp: Network is online, starting offline data upload
I (xxxx) mqtt_esp: Found 10 offline data files, uploading...
I (xxxx) mqtt_esp: Publishing offline data (type=1, size=256)
I (xxxx) mqtt_esp: Offline data published successfully, msg_id=12345
```

## 故障处理

### Flash空间不足

**现象**：
```
E (xxxx) OFFLINE_STORAGE: 无法创建文件
```

**解决**：
1. 自动删除最旧的数据
2. 格式化存储分区

### 文件系统损坏

**现象**：
```
E (xxxx) FLASH_SPIFS: 挂载失败
```

**解决**：
1. 系统会尝试自动修复
2. 如修复失败，格式化文件系统
3. 检查Flash硬件

### 数据补传失败

**现象**：
```
E (xxxx) mqtt_esp: Failed to publish offline data
```

**解决**：
1. 检查网络连接
2. 检查MQTT服务器状态
3. 数据会保留在Flash中，下次重试

## 性能优化建议

### 1. 合并多条数据

将多条数据合并到一个文件中存储：

```c
// 示例：每100条数据存储为一个文件
// 空间利用率：100 * 200字节 / 4096字节 ≈ 4.9%
// 单文件存储：100 * 200字节 / 4096字节 = 100%
```

### 2. 降低存储频率

网络离线时降低存储频率：

```c
// 在线：1秒/次
// 离线：10秒/次
// 可存储时长：110小时
```

### 3. 选择性存储

只存储关键数据：

```c
// 只存储异常数据、状态变化
// 正常数据丢弃
```

### 4. 定期格式化

建议每隔1-3个月格式化一次文件系统，消除碎片：

```c
flash_spiffs_format();
```

## 监控和调试

### 查看存储使用情况

```c
size_t used = 0, total = 0;
offline_storage_get_usage(&used, &total);
ESP_LOGI(TAG, "Storage: %zu / %zu bytes", used, total);
ESP_LOGI(TAG, "Usage: %.1f%%", (used * 100.0) / total);
```

### 查看离线数据数量

```c
uint32_t count = offline_storage_get_count();
ESP_LOGI(TAG, "Offline data files: %u", count);
```

### 格式化存储

```c
flash_spiffs_format();
```

## 注意事项

1. **首次启动**：会自动格式化文件系统（如果需要）
2. **断电保护**：SPIFFS提供基本断电保护，但建议避免频繁断电
3. **Flash寿命**：SPIFFS无磨损均衡，建议定期格式化以延长寿命
4. **容量限制**：由于SPIFFS最小文件4KB，实际可存储时长会少于理论值
5. **补传速率**：每秒处理一条数据，避免阻塞新数据上传
6. **碎片问题**：长期使用后性能可能下降，建议定期格式化

## 后续扩展建议

1. **数据压缩**：集成zlib压缩，延长存储时长
2. **批量存储**：将多条数据合并到一个文件中
3. **优先级队列**：重要数据优先补传
4. **数据导出**：通过Web界面导出离线数据
5. **统计信息**：记录丢失/补传的数据统计
6. **智能采样**：根据存储空间动态调整采样率
7. **定期维护**：实现自动碎片整理和格式化

## 与LittleFS对比

| 特性 | SPIFFS | LittleFS |
|------|--------|----------|
| 最小文件大小 | 4KB | 0字节 |
| 内存占用 | ~2KB | ~4KB |
| 磨损均衡 | ❌ 无 | ✅ 自带 |
| 断电保护 | ⚠️ 一般 | ✅ 优秀 |
| 支持目录 | ❌ 不支持 | ✅ 支持 |
| 碎片问题 | ❌ 严重 | ✅ 轻微 |
| ESP-IDF支持 | ✅ 原生支持 | ❌ 需第三方组件 |

**选择建议**：
- 如果需要ESP-IDF原生支持且数据量不大 → SPIFFS
- 如果需要更好的性能和寿命 → LittleFS（需手动添加组件）
