afci_download.c
源文件路径:
net/afci_download.c所属模块:net(网络应用层 / 设备固件升级下载) 代码行数:113
1. 文件职责
本文件是 AFCI(电弧故障断路器)云端固件下载流程的标志与标识适配层,本身不搬运任何固件字节。它把 g_stEMSInfo.stUpt[UPDT_AFCI] 这一升级表项包装成 5 个对本模块友好的查询接口:强制更新标志(IsDownloadAFCI)、已从服务器更新完成标志(is_afci_firm_download_finished)、下载百分比(GetPercentDownloadAFCI)、AFCI 机型字符串(get_afci_type)、AFCI 硬件版本号(get_afci_hard_version)。
数据流位置:云端下发特殊命令 → net/server.c:2346 handle_special_cmd_for_dev() 置 stUpt[UPDT_AFCI].bIsUpdateForce 与文件掩码 → 本文件的 IsDownloadAFCI() / GetPercentDownloadAFCI() 被 HMI 主界面轮询 → net/ems.c:2945 request_update_file_info() 通过 get_afci_hard_version() / get_afci_type() 把机型与原版本上报服务器 → net/server.c:3675 handle_fw_file_from_server() 逐包写入 g_u8_fw_sdram1 → net/upd_download.c:391 calculate_dev_FW_Flash_MD5() 比对 MD5 成功后 Set_Status_Is_Updated_From_Server(UPDT_AFCI, TRUE) → is_afci_firm_download_finished() 被逆变器通信层读取,决定是否停发常规数据帧。
驱动方是「云端指令 + 逆变器通信任务」:前者触发下载,后者消费“升级完成”标志。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
STUpdateTab | 结构体 | 定义于 net/server.h:216,固件升级表项。本文件主要读它的 bIsUpdateForce(强制更新)、bIsUpdatedFromServer(已完成)、u32LenFile(文件总长)、u32OffsetFile(已下载偏移)、as8FileMask[8] |
ENUpdateType | 枚举 | 定义于 net/server.h:186;本文件用其中的 UPDT_AFCI(值 7,排在 UPDT_Meter/UPDT_EVC 之后)作为 stUpt[] 下标 |
ST_EMS_INFO(g_stEMSInfo) | 全局结构体实例 | 定义于 net/ems.h,本文件唯一的全局状态来源;stUpt[UPDT_MAX] 数组即升级表(net/ems.h:346) |
FILE_MASK_AFCI | 宏 | net/server.h 中为 "00002000",AFCI 的文件掩码;本文件未直接引用,由 server.c / ems.c 用于匹配 |
FILE_MASK_DEFAULT | 宏 | net/server.h:82,值 "00000000";本文件在文件掩码为空串时作为兜底返回 |
VERSION_AFCI_HARD | 宏 | midware/afci/afci_comm.h:18,值 "V1.0";本文件把它作为 AFCI 硬件版本上报云端 |
AFCI_TYPE_BYTE_WATT_S | 宏 | midware/afci/afci_comm.h:19,值 "BYTE_AFCI_S"(贝瓦单相 AFCI 机型串) |
AFCI_TYPE_BYTE_WATT_T | 宏 | midware/afci/afci_comm.h:20,值 "BYTE_AFCI_T"(贝瓦三相 AFCI 机型串) |
INVERTER_CATEGORY_E | 枚举 | 定义于 midware/inverter/inverter.h:403:INVERTER_TYPE_BYTE_WATT_S / INVERTER_TYPE_BYTE_WATT_T / INVERTER_TYPE_NON_BYTE_WATT;本文件用 get_inverter_type_category() 的返回值切换机型串 |
本文件不定义任何结构体、枚举、联合体、位域或宏常量,只有 include guard 由头文件提供。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
get_afci_Updt_File_Mask | 全局 | afci_download.c:41。返回 AFCI 升级文件掩码,空则回退 FILE_MASK_DEFAULT;但读的是 stUpt[UPDT_Meter] 下标 |
is_afci_firm_download_finished | 全局 | afci_download.c:59。返回 Get_Status_Is_Updated_From_Server(UPDT_AFCI),即 AFCI 固件已由服务器更新到位 |
IsDownloadAFCI | 全局 | afci_download.c:70。返回 get_flag_force_update(UPDT_AFCI),服务器下发的 AFCI 强制更新标志 |
GetPercentDownloadAFCI | 全局 | afci_download.c:81。返回 已下载偏移 * 100 / 文件总长 |
get_afci_type | 全局 | afci_download.c:93。按 get_inverter_type_category() 把逆变器机型映射为 AFCI 机型串,非贝瓦机型返回空串 "" |
get_afci_hard_version | 全局 | afci_download.c:110。返回常量 VERSION_AFCI_HARD("V1.0") |
共 6 个函数,全部为全局作用域,无 static 函数、无函数指针、无回调。文件内无任何循环/阻塞逻辑,均为 O(1) 查询。
4. 核心逻辑解析
4.1 三个纯转发接口与百分比公式
三个标志/百分比接口都是对 net/upd_download.c 中通用访问器的直接转发,没有本地缓存:
| 本文件函数 | 转发到 | 底层字段 |
|---|---|---|
IsDownloadAFCI() | get_flag_force_update(UPDT_AFCI)(upd_download.c:89) | stUpt[UPDT_AFCI].bIsUpdateForce |
is_afci_firm_download_finished() | Get_Status_Is_Updated_From_Server(UPDT_AFCI)(upd_download.c:375) | stUpt[UPDT_AFCI].bIsUpdatedFromServer |
GetPercentDownloadAFCI() | get_curr_offset_file_request(UPDT_AFCI)(upd_download.c:121)+ get_length_file_updating(UPDT_AFCI)(upd_download.c:327) | u32OffsetFile * 100 / u32LenFile |
百分比公式的关键取值来源:
u32LenFile(分母):由net/server.c:3318set_file_updating_info_for_dev()从云端 JSON 的Length字段解析,且必须先通过区间检查(sizeMin, sizeMax);AFCI 的区间是(1024*5, SDRAM_3M),即 5 KB ~ 3 MB(net/server.c:3477)。未通过检查时该字段保持 0。u32OffsetFile(分子):由net/server.c:3568increase_current_offset_file_updt()在每收妥一包固件数据后按包长递增(调用点在net/server.c:3831)。- 整个流程结束后(含 MD5 不一致分支)
u32OffsetFile会被清零(net/upd_download.c:429),因此百分比会自动回到 0。
由于分子分母都是 u32,而函数返回 u8,* 100 的计算在 32 位下完成后再截断为 8 位——正常流程里比值 ≤ 100,不触发截断。
4.2 AFCI 机型映射
get_afci_type()(afci_download.c:93)是一个 3 分支 switch:
get_inverter_type_category() 返回 | 输出的 AFCI 机型串 |
|---|---|
INVERTER_TYPE_BYTE_WATT_S | "BYTE_AFCI_S" |
INVERTER_TYPE_BYTE_WATT_T | "BYTE_AFCI_T" |
INVERTER_TYPE_NON_BYTE_WATT(含 default) | ""(空串) |
get_inverter_type_category() 定义于 midware/inverter/inverter.c:8003,返回值来自逆变器自身识别结果。返回空串是有意义的:非贝瓦机型不带 AFCI,云端比对机型串时不会命中。
4.3 状态迁移(外部状态机,本文件只提供读点)
本文件自身没有状态机,但它读写的两个布尔位构成一条外部迁移链:
[云端特殊命令到达]
server.c:2346 handle_special_cmd_for_dev(..., isForceUpd, fileMask)
├─ server.c:2370 set_response_force_upd_status(updType, TRUE) // 已应答
├─ server.c:2408 set_flag_force_update(UPDT_AFCI, TRUE) // bIsUpdateForce=1
└─ server.c:2418 set_updt_file_mask(UPDT_AFCI, FILE_MASK_AFCI)
↓ IsDownloadAFCI()==TRUE → HMI 进入“AFCI 下载中”界面
[文件信息上报/下发] ems.c:2945 → server.c:3318 u32LenFile 生效
↓ server.c:3675 逐包写 g_u8_fw_sdram1,u32OffsetFile 递增
↓ GetPercentDownloadAFCI() 在 HMI 上显示 0~100
[最后一包收妥] server.c:3811 calculate_dev_FW_Flash_MD5(UPDT_AFCI, g_u8_fw_sdram1, AFCI_FIRM_DOWNLOAD)
├─ MD5 相同 → upd_download.c:412 Set_Status_Is_Updated_From_Server(UPDT_AFCI, TRUE)
│ ↓ is_afci_firm_download_finished()==TRUE
│ ↓ alphaess_comm_usart.c:5444 只传输固件,停发常规帧
└─ MD5 不同 → upd_download.c:422 u16_upgrade_error.BIT.md5_error = 1
u8 返回值被直接当布尔用(if(...)),全工程一致,语义清晰。
4.4 通信协议帧格式与纠错
本文件不构造也不解析任何报文。与之相关的是它提供的两个字符串如何进入 EMS 云端协议:
get_afci_hard_version()用于net/ems.c:2989组织checkUpdate请求的Hard_Version字段(该请求最终由ems.c:3042fill_common_data(..., REQUEST_COMM, CMD_CHECK_UPDATE)加上 7 字节头与 2 字节校验)。get_afci_type()用于net/ems.c:3025的File_Type字段,以及net/ems.c:3105的固件内容请求;- 服务器下发的固件包在
net/server.c:3692起按「FileMask 8 字符 → FileOffset → FileType → FileLength → FileData(hex)」逐项校验;get_afci_type()参与net/server.c:3711的机型一致性判定,判定失败则整包丢弃(server.c:3727直接return),这是本文件间接参与的“重试/丢弃”机制。
超时与重试不在本文件,由 server.c 的包序校验(offset 必须与本地一致,server.c:3699)与云端超时重发实现。
4.5 与 uCOS-II 的配合
无任何 uCOS-II 调用:不创建任务、不申请信号量、不使用消息邮箱、不调用延时。所有函数都是对 g_stEMSInfo 的同步单次读写。
g_stEMSInfo 被 EMS 网络任务(net/server.c)写、被 HMI 任务(hmi/ui/ui_main.c:898)与逆变器通信任务(midware/inverter/alphaess/alphaess_comm_usart.c:5337)读,属于跨任务共享的普通全局变量,未加临界区。当前依赖 u32 读写的自然原子性和布尔位的单字节写入来避免撕裂。
5. 对外接口
本文件导出(供谁调用):
| 导出名 | 调用方 |
|---|---|
IsDownloadAFCI | hmi/ui/ui_main.c:898、net/ems_download.c:261(IsSystemDownload()) |
GetPercentDownloadAFCI | hmi/ui/ui_main.c:904 |
is_afci_firm_download_finished | midware/inverter/alphaess/alphaess_comm_usart.c:5337、:5444 |
get_afci_type | net/ems.c:3025、:3105;net/server.c:3314、:3711 |
get_afci_hard_version | net/ems.c:2989;net/server.c:3314 |
get_afci_Updt_File_Mask | 无任何调用点,且未在 afci_download.h 中声明 |
本文件不导出全局变量。
本文件调用的外部符号:
| 名称 | 来源 |
|---|---|
g_stEMSInfo | net/ems.h(全局 EMS 信息结构) |
Get_Status_Is_Updated_From_Server / get_flag_force_update / get_curr_offset_file_request / get_length_file_updating | net/upd_download.c(声明于 upd_download.h:26/28/37/40) |
get_inverter_type_category | midware/inverter/inverter.c:8003(声明于 inverter.h:2255) |
strlen | C 标准库(经 string.h 间接引入) |
FILE_MASK_DEFAULT | net/server.h:82 |
VERSION_AFCI_HARD / AFCI_TYPE_BYTE_WATT_* | midware/afci/afci_comm.h:18-20 |
6. 依赖关系
依赖(第 13-21 行 include):
upd_download.h、afci_download.h、ems.h、server.h、MD5.h、sdram.h、iwdg.h、afci_comm.h、inverter.h- 其中
MD5.h、sdram.h、iwdg.h在本文件内无任何直接引用(MD5 计算已下沉到upd_download.c的calculate_dev_FW_Flash_MD5),属于从meter_download.c沿袭下来的冗余 include,会额外引入 SDRAM/看门狗/摘要算法的编译依赖。
被依赖(include afci_download.h 的文件):
net/afci_download.c:14net/ems.c:57、net/ems_download.c:30、net/server.c:54hmi/ui/ui_main.c:32midware/inverter/alphaess/alphaess_comm_usart.c:31
7. 备注与风险点
get_afci_Updt_File_Mask()使用了错误的下标(疑似复制粘贴缺陷):afci_download.c:43-45操作的是g_stEMSInfo.stUpt[UPDT_Meter].as8FileMask,即读的是电表的升级掩码,而不是UPDT_AFCI。对照meter_download.c:40可见两者代码完全一致。所幸该函数全工程无调用点、也未在头文件声明,目前不影响运行,但一旦被启用就是隐藏 bug。as8FileMask[8]可能没有结束符,strlen存在越界读:set_updt_file_mask()(upd_download.c:253)用strncpy(dst, fileMask, 8)写入,而掩码串字面量恰好是 8 字符(如"00002000"),因此末尾'\0'会被截掉;afci_download.c:43与meter_download.c:42随后对它取strlen,会继续向后读到结构体的下一成员as8FWType[10]。当前靠该成员恰好为 0 才得到正确结果,属未定义行为,重构结构体布局即可能出错。- 百分比函数存在除零可能:
GetPercentDownloadAFCI()的除数是u32LenFile。bIsUpdateForce在server.c:2408被置位时,文件长度通常还没解析出来(u32LenFile仍为 0),而 HMI 是「先判IsDownloadAFCI()再算百分比」(ui_main.c:898、:904),存在同一轮内分母为 0 的窗口。ARM 平台上 32 位无符号除法除零不会触发硬件异常(Cortex-M4 的UDIV指令或库函数__aeabi_uidiv均返回 0),因此表现为百分比显示 0% 而非死机,但语义上并不正确,属于依赖“除零得 0”这一平台行为的隐式假设。 - 函数头注释张冠李戴:
afci_download.c:64-79的注释写的是IsDownloadMeter/GetPercentDownloadMeter,显然是复制meter_download.c后未改注释。 - 作者/文件名注释不一致:
.c文件头@author: Alpha EMS Team,.h文件头@author: Alpha g_stEMSInfo Team。后者与meter_download.h/evc_download.h一致,推测是历史上把标识符EMS全局替换为g_stEMSInfo时误改了注释。 - 硬版本号硬编码:
get_afci_hard_version()返回afci_comm.h的宏VERSION_AFCI_HARD = "V1.0",一旦硬件改版必须同步改宏,没有与实物 AFCI 上报版本做一致性校验的路径。 - 冗余 include:
MD5.h/sdram.h/iwdg.h在本文件未被使用(见 6. 依赖),可移除;当前保留会扩大编译依赖面。