Files

216 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LwIP FTP 升级功能说明(最终版)
## 1. 设计目标
-**不依赖 4G 模块 AT 指令** 的情况下,通过 **以太网 (LwIP socket)** 实现 FTP 固件下载。
- 复用已有的 `flash_upgrade.h` 升级区管理:
- `upgrade_erase_program_area()` 擦除升级区域;
- `upgrade_write_packet()``PACKET_SIZE`1024 字节)分包写入;
- `upgrade_success_update_header()` 更新升级头(`UPGRADE_FLAG_IDLE / UPGRADE_FLAG_NEED_UP`)。
- 与 4G FTP 升级整体思路保持一致,但实现形式为:
**非阻塞状态机 + 周期调用 `v_ftp_update_action()`**
## 2. 模块文件结构
位于 `app/plat_comm/lwip_module/`
- `lwip_module.[ch]`
- 对 LwIP socket 做简单封装:`lwip_resolve_host / lwip_set_nonblock / lwip_tcp_send / lwip_tcp_recv / lwip_tcp_close` 等。
- `lwip_ftp_upgrade.[ch]`
- **网线 FTP 程序远程升级的完整状态机实现**,仅保留这一对文件:
- 控制连接(21 端口);
- `USER/PASS` 登录;
- `TYPE I` 二进制模式;
- `CWD`(可选目录切换);
- `SIZE` 查询文件大小;
- `PASV` 被动模式解析数据通道;
- `RETR` 下载文件并按 1024 字节分包写 Flash
- `QUIT` 结束会话并写入 NEED_UP 升级头。
- 对外只暴露两个接口:
- `void lwip_ftp_upgrade_init_from_tftp(void);`
- `int lwip_ftp_upgrade_step(void);`
> 说明:旧版的 `lwip_ftp_fw_upgrade_api.[ch]`、`lwip_ftp_upgrade_flash_adapter.[ch]`、`lwip_ftp_fw_upgrade_sm.[ch]` 等文件已删除,不再使用。
## 3. 参数来源与触发方式
### 3.1 参数结构:`T_FTP_UPDATE_INFO`
`bs_public_impl.h` 中定义,关键字段如下:
- `u8_update_server_addr`FTP 服务器域名 / IP
- `u16_update_server_port`FTP 端口(通常 21);
- `u8_user / u8_pwd`:用户名、密码;
- `u8_path`:远程目录(可为空);
- `u8_file_name`:文件名;
- `u8_netType`:网络类型(`0=4G``1=网线`);
- `u32_file_size`:文件大小(由 FTP SIZE 结果回填);
- `u8_update_result`:升级结果(0=成功,1=失败)。
这些字段由 OCPP `UpdateFirmware` 命令解析函数 `parse_ftp_url()` 填充。
### 3.2 触发流程
1. OCPP 平台下发 `UpdateFirmware`URL 形如:
`ftp://user:pass@host:port/path/file.bin`
→ 解析到全局 `t_ftp_update_info`
2. 上层把对应平台的 `u8_updata_flag` 置位。
3. `plat_comm_task.c` 中的 `v_Plat_Comm()` 周期调用 `v_ftp_update_action()`
4. `v_ftp_update_action()` 内部根据:
- `s_bs_task_ctrl.ocpp_data.u8_log_file_upload``1=日志上传,0=程序升级`
- `t_ftp_update_info.u8_netType``0=4G`(走 4G 模块),`1=网线`(走 LwIP FTP)。
## 4. 程序升级流程(网线 / LwIP)
当满足:
- `s_bs_task_ctrl.ocpp_data.u8_log_file_upload == 0`(程序升级);
- `t_ftp_update_info.u8_netType != 0`(网线/LwIP);
`v_ftp_update_action()` 中流程为:
```c
if (t_ftp_update_info.u8_init_flag == 0)
{
if (t_ftp_update_info.u8_netType == 0) {
v_ftp_ctrl_init(); // 4G 模块初始化
} else {
lwip_ftp_upgrade_init_from_tftp(); // 网线 FTP 状态机初始化
}
t_ftp_update_info.u8_init_flag = 1;
}
if (t_ftp_update_info.u8_netType == 0) {
u8_ec200a_4g_ftp_update(); // 4G FTP 升级
} else {
int ret = lwip_ftp_upgrade_step(); // LwIP FTP 升级状态机单步
if (ret == 1) {
// 升级成功
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0;
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0;
t_ftp_update_info.u8_update_result = 0;
} else if (ret < 0) {
// 升级失败
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0;
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0;
t_ftp_update_info.u8_update_result = 1;
}
}
```
### 4.1 状态机步骤(`E_LWIP_FTP_STEP`
`lwip_ftp_upgrade.c` 内部定义的状态机大致流程:
1. `LWIP_FTP_STEP_CONNECT`
- 建立 FTP 控制连接(21 端口),并设置为非阻塞。
2. `LWIP_FTP_STEP_WAIT_WELCOME`
- 等待 `220` 欢迎语。
3. `LWIP_FTP_STEP_SEND_USER / WAIT_USER`
- 发送 `USER xxx`,等待响应;
- 若返回 `331`,继续发送 `PASS`;否则直接进入 `TYPE`
4. `LWIP_FTP_STEP_SEND_PASS / WAIT_PASS`
- 发送 `PASS xxx`,等待登录结果。
5. `LWIP_FTP_STEP_SEND_TYPE / WAIT_TYPE`
- 发送 `TYPE I`,切换为二进制模式。
6. `LWIP_FTP_STEP_SEND_CWD / WAIT_CWD`(可选)
- 如果 `u8_path` 非空,发送 `CWD <path>` 切目录。
7. `LWIP_FTP_STEP_SEND_SIZE / WAIT_SIZE`
- 发送 `SIZE <file>`,并从 `213` 响应中解析文件大小;
- 回填到 `s_ctx.file_size``t_ftp_update_info.u32_file_size`
8. `LWIP_FTP_STEP_PREPARE_FLASH`
- 调用 `upgrade_erase_program_area()` 擦除升级区;
- 构造 `upgrade_header_t`,写入 `UPGRADE_FLAG_IDLE / file_size / packet_count` 等信息。
9. `LWIP_FTP_STEP_SEND_PASV / WAIT_PASV`
- 发送 `PASV`,解析 `227` 响应得到数据通道 IP/端口。
10. `LWIP_FTP_STEP_CONNECT_DATA`
-`data_ip / data_port` 建立数据连接 socket。
11. `LWIP_FTP_STEP_SEND_RETR / WAIT_RETR`
- 发送 `RETR <file>`,等待 `150/125` 等开始传输响应。
12. `LWIP_FTP_STEP_RECV_DATA`
- 循环接收数据,按 1024 字节分包写入 Flash(见 4.2)。
13. `LWIP_FTP_STEP_SEND_QUIT`
- 发送 `QUIT`,关闭控制连接;
- 写入 `UPGRADE_FLAG_NEED_UP` 头部,标记升级就绪。
14. `LWIP_FTP_STEP_DONE / FAIL`
- `DONE`:整个流程成功,`lwip_ftp_upgrade_step()` 返回 `1`
- `FAIL`:任意步骤出错,返回 `<0`
### 4.2 分包写 Flash 与进度打印
- 内部使用一个 **动态申请的** 1024 字节缓存 `packet_buf`(不是全局大数组):
-`lwip_ftp_upgrade_init_from_tftp()` 中第一次调用 `pvPortMalloc(PACKET_SIZE)`(FreeRTOS 堆),后续重复使用;
- 失败会直接打印 `"pvPortMalloc packet_buf fail"` 并进入 `FAIL`
- 数据通道每次 `lwip_tcp_recv()` 得到的数据都会写入 `packet_buf`
- 每累计满 1024 字节调用一次 `upgrade_write_packet(packet_index, packet_buf, len)`
- 处理最后不足一包的数据时,也会单独调用一次。
- **接收进度打印**
- 每次收到数据后,都会根据 `downloaded / file_size` 计算百分比:
```c
Plat_Comm_LOG("lwip ftp: recv %lu/%lu (%u%%)\r\n", ...);
```
- 方便在串口日志中实时观察下载进度。
### 4.3 控制通道响应等待(最长 1 秒)
- 所有 FTP 控制命令的响应(`220 / 331 / 230 / 213 / 227 / 150 ...`)统一通过:
```c
static int ftp_read_reply_block(int sock, uint32_t timeout_ms,
int *out_code, char *out_msg, size_t out_msg_sz);
```
- 关键特性:
- **内部使用局部缓冲区**
- `char rx_buf[FTP_CTRL_RX_BUF_SIZE];`
- `char line[FTP_LINE_MAX];`
- 不再在全局结构体中定义大数组。
- 每次调用最长阻塞 `timeout_ms` 毫秒(本工程中统一传入 `1000`,即 1 秒):
- 在 1 秒内尽可能收满一条完整响应行;
- 超时、对端关闭或错误均返回非 0,状态机进入 `FAIL`。
- 满足需求:
- **“发送命令后可最长等待 1 秒接收数据,这样可以保证大部分交互在一个周期内完成”**。
## 5. 日志上传(4G / 预留 LwIP
- 当 `s_bs_task_ctrl.ocpp_data.u8_log_file_upload == 1` 时,`v_ftp_update_action()` 走“日志上传”分支:
- 当前仅在 `t_ftp_update_info.u8_netType == 0`4G)时调用 `u8_ec200a_4g_ftp_log_upload()`
- 网线场景下会打印提示,尚未提供 LwIP 日志上传实现。
后续如需支持 **LwIP FTP 日志上传**,建议在 `lwip_ftp_upgrade.[ch]` 基础上扩展 `STOR/APPE` 等命令。
## 6. 注意事项
- **非阻塞总框架 + 局部 1 秒等待**:
- 整体采用状态机 + 周期调用的方式集成在 `v_Plat_Comm` 中,不新建线程;
- 单条 FTP 命令的响应等待时间 **最多 1 秒**,保证大部分交互能在一次 `v_ftp_update_action()` 周期内完成。
- **内存使用**
- 不再在全局结构体中定义大数组:
- 控制通道解析使用的缓冲区全部为函数内局部变量;
- 数据分包缓冲 `packet_buf[PACKET_SIZE]` 采用 FreeRTOS `pvPortMalloc` 动态申请,仅 1KB。
- **文件大小依赖 SIZE**
- 必须保证 FTP 服务器支持 `SIZE` 命令,否则无法写入正确的 `file_size`,升级头将不可靠。
- **错误处理与重试**
- 任意步骤出错,`lwip_ftp_upgrade_step()` 返回 `<0`
- `v_ftp_update_action()` 会清除 `u8_updata_flag` 并设置 `u8_update_result = 1`
- 上层可根据日志与结果决定是否重新触发升级。
## 7. 与 4G FTP 的对比
- **4G FTP**
- 文件:`ec200a_4g_ftp.c`
- 通过 AT 命令驱动 4G 模块内置 FTP 客户端;
- 状态机在 `u8_ec200a_4g_ftp_update()` 内实现。
- **LwIP FTP(网线)**
- 文件:`lwip_module.c` + `lwip_ftp_upgrade.c`
- 使用 LwIP socket 直接实现 FTP 协议;
- 与 4G 共用 `flash_upgrade` 升级区与头部结构;
- 集成方式完全嵌入 `v_ftp_update_action()`,不影响 `v_Plat_Comm` 线程的主循环。