backupbox.h
源文件路径:
midware/meter/backupbox.h所属模块:midware/meter(备电盒 / BackupBox 负载管理) 代码行数:50
1. 文件职责
本头文件定义备电盒(BackupBox)模块的两张表与唯一对外接口:BACKUP_BOX_CONFIG_T 保存用户可配的分相优先级与各相 SOC 门限,BACKUP_BOX_RUN_T 保存运行时各相开关状态、SOC 切出位图、上次下发命令位图、电网状态与过载计数。这两张表以「配置 + 运行」成对的形式被 midware/meter/meter.h 直接内嵌进 METER_TABLE_T(分别为 t_config_data.t_bb 与 t_bb_info,meter.h:397、meter.h:443),因此本头文件必须被 meter.h include(meter.h:17),成为电表模块的类型前置依赖。它只导出一个函数 bb_load_manage(),由 meter.c 的电表任务在检测到备电盒型号时调用。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 | ||
|---|---|---|---|---|
__EMS_BACKUPBOX_H__ | 宏 | 头文件包含卫士(backupbox.h:10) | ||
BB_DEBUG_INFO | 宏 | 0(backupbox.h:20)。置 1 时 backupbox.c 会在每个状态迁移点打印 printf 调试信息,含各相开关、soc_offline、soc_offline_last | ||
BACKUP_BOX_CONFIG_T | 结构体(backupbox.h:23) | 备电盒配置表:u8_enable(备电盒使能,0/1)、u8_prio_lx[3](L1/L2/L3 三相优先级,取值 2 或 3 表示该相可被过载切出)、u8_soc_lx[3](各相 SOC 门限,单位 %)、u32_sn(序列号)、u16_ver(备电盒固件版本)。未使用 #pragma pack,因此结构体长度依赖编译器默认对齐(u32_sn 会使尾部补齐) | ||
BACKUP_BOX_RUN_T | 结构体(backupbox.h:33) | 备电盒运行表:<br>u8_state_lx[3] — 各相负载开关实际状态<br>u8_soc_offline — 因 SOC 被切出的相位位图(bit0=L1、bit1=L2、bit2=L3)<br>u8_soc_offline_last — 上一次位图,用于边沿去抖<br>u8_cmd_last — 上一次下发命令的 3 bit 位图(`(L3<<2) | (L2<<1) | L1)<br>u8_grid_state — 电网状态;等于 2 时表示需要以 u8_state_lx 为准刷新软件位图<br>u8_overload_flag[3] — 各相过载标志<br>s8_overload_cnt[3] — 各相过载计数器(单相逻辑用 3 次确认)<br>s16_power_lx[3] — 各相负载功率缓存<br>u8_bb_running_state — 备电盒运行/升级状态标记(meter.c:5740` 在 OTA 时置 FALSE) |
注:
backupbox.h中没有任何位域、联合体或枚举定义;位图语义完全靠注释与backupbox.c中的移位表达式约定。u8_grid_state、u8_prio_lx中「2」「3」等取值也没有配套的枚举/宏,是裸魔法值。
3. 函数清单
本文件为纯声明型头文件,不含任何函数定义。
| 函数名 | 作用域 | 说明 |
|---|---|---|
bb_load_manage | 全局(声明于 backupbox.h:47) | 备电盒负载管理唯一入口,实现在 backupbox.c:721 |
说明:backupbox.c 中另外两个非 static 函数 single_phase_bb_load_manage(backupbox.c:398)与 triphase_bb_load_manage(backupbox.c:656)没有在本头文件声明,属于「有外部链接但无对外契约」的状态——它们只被 bb_load_manage() 在文件内调用,本应加 static。
4. 核心逻辑解析
4.1 配置表与运行表的职责切分
| 表 | 谁写 | 谁读 | 持久化 |
|---|---|---|---|
BACKUP_BOX_CONFIG_T(g_t_meter.t_config_data.t_bb) | HMI 菜单 / 服务器下发(经 data/ 配置管理) | backupbox.c 四个处理函数、single_phase_bb_load_manage、triphase_bb_load_manage | 是,随 METER_TABLE_T 的 config 区落 Flash |
BACKUP_BOX_RUN_T(g_t_meter.t_bb_info) | backupbox.c(u8_soc_offline/u8_soc_offline_last/u8_cmd_last/s8_overload_cnt/u8_overload_flag)、meter.c(u8_state_lx/u8_grid_state/u8_bb_running_state) | backupbox.c、HMI 显示 | 否,仅 RAM |
这种「配置 + 运行」成对布局是 EMS 项目里 meter.h、logic.h 等的通行做法:METER_TABLE_T 把可持久化字段集中在 t_config_data 里,运行态字段放在外面,data/ 的保存作业只搬运 t_config_data。
4.2 位图语义(唯一的跨模块数据契约)
u8_soc_offline、u8_soc_offline_last、u8_cmd_last 三个字段都是 3 bit 位图,语义一致:
bit0 → L1 (state_lx[0])
bit1 → L2 (state_lx[1])
bit2 → L3 (state_lx[2])
值 = (state_lx[2] << 2) | (state_lx[1] << 1) | state_lx[0]
u8_cmd_last = 0x0F(在 backupbox.c:417、backupbox.c:680 被用作「无有效命令」的哨兵值,因为 bit3 永远为 0,0x0F 不可能是合法位图)与 u8_soc_offline = 0x07(三相全部被切出)是两个隐含的约定值,头文件里没有解释。
4.3 与 uCOS-II 的并发约定
本头文件不涉及同步原语。两个表都由 midware/meter/meter.c 的 task_meter 任务读写,而 bb_load_manage() 也在同一任务上下文被调用(meter.c:5432,位于 meter_data_read() 之后的同一轮循环内)。因此:
- 无锁读写是安全的(单任务串行)。
- HMI 若在其他任务里读
g_t_meter.t_bb_info只能得到「上一轮」的一致快照,且 8 位字段的读写天然原子,不会撕裂。 - 风险在于
meter.c也可能在别处直接改u8_grid_state(来自备电盒应答解析),而backupbox.c只在函数入口读一次并缓存到局部state_lx,因此不会出现同一轮内前后不一致。
4.4 与型号判定的耦合
BACKUP_BOX_CONFIG_T / BACKUP_BOX_RUN_T 本身不携带型号,型号由 METER_TYPE_E(meter.h:148 附近)的 BACKUPBOX_5K / BACKUPBOX_10K / BACKUPBOX_23K 表达。本头文件不引用这些枚举值,但 backupbox.c 依赖它们来:
- 判断是否进入备电盒逻辑(
meter.c:5428与backupbox.c:53) - 决定是否有
change_state_bb_lx()的下发动作
因此新增备电盒型号时需要同时改 meter.h 的 METER_TYPE_E、meter.c 的三处型号判断、backupbox.c:53 的型号判断,本头文件不需要改动。
5. 对外接口
本文件导出
| 名称 | 类型 | 使用者 |
|---|---|---|
bb_load_manage() | 函数原型 | midware/meter/meter.c:5432(task_meter) |
BACKUP_BOX_CONFIG_T | 类型 | midware/meter/meter.h:397(t_config_data.t_bb)、backupbox.c |
BACKUP_BOX_RUN_T | 类型 | midware/meter/meter.h:443(t_bb_info)、backupbox.c、HMI 显示层 |
BB_DEBUG_INFO | 宏 | backupbox.c(编译期裁剪调试打印) |
本文件依赖的外部声明
chip.h(u8/s8/u16/u32 基础类型定义)。
6. 依赖关系
依赖
chip.h(backupbox.h:16)—— 提供u8/s8/u16/u32等整型别名。这是本头文件唯一的 include,依赖面极小。
被依赖
midware/meter/meter.h:17—— 直接#include "backupbox.h",以便在METER_CONFIG_DATA_T/METER_TABLE_T中内嵌两张表。因此所有 includemeter.h的文件都间接依赖本头文件,包括:midware/meter/backupbox.c、midware/meter/meter.cmidware/meter/ct.c、midware/meter/bb_update.c、midware/meter/meter_upgrade.c、midware/meter/diesel_generator.cmidware/interface/modbus/modbus.c、midware/inverter/*(通过meter.h取型号常量)data/system_config/system_config.c、net/ems.c、hmi/等
midware/meter/backupbox.c—— 唯一实现方,#include "backupbox.h"(backupbox.c:13)
7. 备注与风险点
- 两个外部链接函数未在头文件声明:
single_phase_bb_load_manage()(backupbox.c:398)与triphase_bb_load_manage()(backupbox.c:656)没有原型,也没有static。它们目前只被bb_load_manage()调用,但外部模块若想调用会缺少类型检查(C 语言隐式声明在 C99 下只是警告)。建议加static。 BACKUP_BOX_CONFIG_T未加#pragma pack(1)(backupbox.h:23-backupbox.h:31):该结构体属于METER_TABLE_T.t_config_data,是要落 Flash 并带 CRC 的结构。默认对齐下不同编译器/优化选项的填充字节数若发生变化,会导致 Flash 上旧数据的 CRC 校验失败。EMS 其它持久化结构体(如ct.h中的CT_CONFIG_TABLE_T)都用了pack(1),这里是不一致的做法,值得核实。- 魔法值没有常量:
u8_grid_state == 2(backupbox.c中 6 处)、u8_prio_lx[i] == 2 || == 3(backupbox.c:136、backupbox.c:288)、u8_cmd_last == 0x0F、u8_soc_offline == 0x07、SOC 死区+10、过载系数0.8、恢复延时1000*10全部是裸数字。头文件里没有任何对应的宏或枚举,阅读与修改成本高。 u8_bb_running_state语义模糊:字段名与注释(meter.c:5740的「BB升级状态认为停止运行」)不一致 —— 顾名思义是「运行状态」,实际当「是否正在升级」用。建议改名为u8_bb_upgrading。u8_state_lx[3]与u8_cmd_last可能长期不一致:u8_state_lx由电表应答解析写入,u8_cmd_last由备电盒逻辑写入,两者只有在u8_grid_state == 2时才会被强制对齐(backupbox.c:100-backupbox.c:108)。若备电盒固件从未上报u8_grid_state == 2,软件位图会一直独立演化。s16_power_lx[3]被meter.c写入但backupbox.c从不读取:写入点是midware/meter/meter.c:3419-meter.c:3421(把 GRID 三相有功存进来,s32 → s16截断),meter.c:3448-meter.c:3450还会取反。而backupbox.c的四种处理函数算功率用的是各自的函数栈变量(l_s32_load_power/l_s32_powerload[3]),完全不读这个字段。也就是说「运行表里的分相功率」与「负载管理实际用的分相功率」是两个数据流,且前者存在s16截断(超过 ±32767 W 会回绕)。u32_sn/u16_ver只由meter.c使用:backupbox.c完全没读这两个字段;实际使用者是midware/meter/meter.c:4493-meter.c:4511(与备电盒应答里的u32_bb_sn/u16_bb_ver比对并按变化写回)、meter.c:6161、meter.c:6191、meter.c:6343、meter.c:6359、meter.c:6394、meter.c:6405(拼装 SN 字符串与V%u.%02u版本号,用于上报/显示)。它们名为备电盒字段其实是「电表信息」复用,字段归属与使用者跨文件,改动时容易漏。u8_enable的语义混合:它同时出现在备电盒逻辑(backupbox.c:446、backupbox.c:701)和功能开关位(midware/auxiliary/system_aux.c里的u8_bb_load_manage/u8_bb_load_management是另一套功能位域)。两处开关分属不同机制,容易混淆谁才是真正的总开关。