EMS 知识库
首页 / knowledge/代码解析/midware/interface/dry_node

dry_node.h

源文件路径:midware/interface/dry_node/dry_node.h 所属模块:midware/interface/dry_node(干接点接口) 代码行数:211

1. 文件职责

本文件是干接点(Dry Contact / 继电器接口)模块的类型与接口定义中心,为 EMS 定义两路干接点的全部配置结构、枚举与 IO 访问宏。它向外导出两块数据:一是持久化配置表 STDryNodeTab STDryNodeInfo(带 CRC 与 #pragma pack(1),直接对应 Flash 一个扇区的一份镜像,由 net/ems.c、net/server.c、modbus.c 读写),二是入口函数 dry_node_io_init() 与 Handle_Dry_Node_Logic()。模块的语义是"把储能的 SOC / 馈网功率 / 时间段 / UPS 状态翻译成两路继电器输出",用于负载投切、SG-Ready 热泵联动、柴油机启停、EPO 复归等外部联动。头文件本身不含逻辑,只有类型、宏和断言。

2. 关键数据结构与宏

名称类型(结构体/枚举/宏/联合体)说明
DEBUG_DRY_NODE宏调试开关,定义为 (0)(dry_node.h:18);置 1 时 dry_node.c 会输出大量 printf 分支编号
DRY_NODE1_ON / DRY_NODE1_OFF宏干接点 1 闭合/断开,展开为 do0_output_on() / do0_output_off()(dry_node.h:20-21)
DRY_NODE2_ON / DRY_NODE2_OFF宏干接点 2 闭合/断开,展开为 do1_output_on() / do1_output_off()(dry_node.h:26-27)
DI_1_DRY_CONTACT宏干接点 1 当前继电器状态回读,展开为 do0_output_bit_get()(dry_node.h:23)
DI_1_ACCESS_POINT宏DI0 输入点电平,展开为 di0_input_bit_get()(dry_node.h:24);用于 EPO 外部信号采集
DI_2_DRY_CONTACT / DI_2_ACCESS_POINT宏干接点 2 的继电器回读 / DI1 输入点电平(dry_node.h:29-30)
ENDryNodeDo枚举干接点动作标记:NODE_DONE_OPEN = 0x01、NODE_DONE_CLOSE = 0x02、NODE_DONE_NONE = 0x03(dry_node.h:32-37)
ENDryNodeStatus枚举继电器实际状态:NODE_OPEN = 0x00、NODE_CLOSE = 0x01(dry_node.h:39-43)
ENDryNodeMode枚举控制模式:MODE_ON = 0x01、MODE_OFF = 0x02、MODE_AUTO = 0x03(dry_node.h:45-50)
ENDryNodeCharge枚举SOC / 馈网比较模式:AT_LEAST = 0x01、AT_MOST = 0x02、AT_DISABLE = 0x03(dry_node.h:52-57)
ENDryNodeNum枚举通道编号:DRY_NODE_ONE = 0x01、DRY_NODE_TWO = 0x02(dry_node.h:59-63)
DRY_NODE_II_MODE_E枚举新版(II 版)逻辑的 13 个运行模式:DRY_NODE_NONE_MODE、DRY_NODE_UPS_MODE、DRY_NODE_SGREADY_MODE、TIME_MODE_SWITCH_ON、TIME_MODE_SWITCH_OFF、AUTO_MODE_SOC_ON_UPS_ON_AT_LEAST … AUTO_MODE_SOC_OFF_FEED_ON_AT_MOST(dry_node.h:65-80)。注意前三个枚举值在 dry_node.c 中从未被赋值使用
DRY_NODE_MODE_STATUS_E枚举新版逻辑的延时状态机:DRY_NODE_STATUS_NONE、DRY_NODE_DELAY_STATUS、DRY_NODE_DURATION_STATUS、DRY_NODE_PAUSE_STATUS、DRY_NODE_STATUS_MAX(dry_node.h:82-89)
DRY_NODE_II_TAB_T结构体新版逻辑的运行时工作表(dry_node.h:93-131,#pragma pack(1)):把 STDryNodeTab 的原始配置与本周期采集的 u16_sys_workmode、u8_bat_soc、s32_meter_actpower_t 以及延时/持续/暂停 tick 与状态打包,供 dry_node.c 的 II 版流水线使用
STDryNodeTab结构体干接点持久化配置表(dry_node.h:133-199,#pragma pack(1),带 u16_head 头与 u16_crc_code 尾),长度必须整除 Flash 扇区
STATIC_ASSERT(ASSERT_FLASH_SECTOR_SIZE % sizeof(STDryNodeTab) == 0)宏编译期断言,保证配置表能整份塞进 Flash 扇区(dry_node.h:201)
STDryNodeInfo全局变量(extern)干接点配置表的唯一实例,dry_node.h:205 声明,dry_node.c:29 定义
ASSERT_FLASH_SECTOR_SIZE宏Flash 扇区大小,定义于 midware/debug/debug.h:19(#define ASSERT_FLASH_SECTOR_SIZE FLASH_SECTOR_SIZE)

STDryNodeTab 字段分组(dry_node.h:135-198):

组字段含义
头u16_head数据头,用于校验 Flash 内容有效性
通道 1 配置u8Channel1、u8ControlMode1、u8StartTime1A/1B、u8EndTime1A/1B、u8Date1、u8ChargeSOC1、u8ChargeMode1、u8UPS1、u32SwitchOn1、u32SwitchOff1、u8Delay1、u8Duration1、u8Pause1使能、模式、两段时间、周掩码、SOC/馈网阈值与延时参数
通道 1 运行态u8Status1、u32DelayTick1、u32DurationTick1、u32PauseTick1、u8RelayDone1会在掉电前被保存,属于"半运行态"字段
Modbus 映射u8_modbus_dry1、u8_modbus_dry2Modbus 侧直接控制干接点用
通道 2与通道 1 同构的一整组 ...2 字段第二路干接点
SG-Ready 扩展u8FeedMode1、u16FeedValue1、u8SGEnable1、u16StartTimeM1A/1B、u16EndTimeM1A/1B(及 ...2)新版分钟级时间段 + 馈网功率阈值 + SG-Ready 使能
尾部u8_reserve[28]、u16_crc_code预留区与 CRC

3. 函数清单

本文件只含函数声明,无函数定义。

函数名作用域(static/全局)说明(一句话)
dry_node_io_init全局(声明)干接点 IO 初始化,声明于 dry_node.h:208,在 dry_node.c 中被 include 但未见实现
Handle_Dry_Node_Logic全局(声明)干接点总入口,声明于 dry_node.h:209,由 midware/interface/interface.c:221 每 500 ms 调用

4. 核心逻辑解析

4.1 两代配置表的共存

本头文件同时保留了两套"同一件事"的表示:

两套字段在同一个 STDryNodeTab 里并排存放,运行时由 dry_node.c:1899 的判据决定走哪一套:

if (STDryNodeInfo.u8FeedMode1 != 0 || STDryNodeInfo.u8FeedMode2 != 0)
    Dry_Node_Logic_ii();     // 新版:馈网/SG-Ready 模式
else {
    Dry_Node1_Logic();       // 老版:时间 / SOC / UPS 模式
    Dry_Node2_Logic();
}

即 u8FeedMode1/2 充当"新老版本仲裁开关":一旦云端或 Modbus 下发过馈网模式(非 0),就整机切到新版逻辑,老版两个函数不再执行。这是一个典型的"版本遗留"式双实现共存。

4.2 结构体对齐与 Flash 存储约束

STDryNodeTab 与 DRY_NODE_II_TAB_T 都用了 #pragma pack(1)(dry_node.h:91 开启,dry_node.h:202 关闭),保证结构在 RAM 中与 Flash/通讯线上的字节布局一致,可直接 memcpy 到 Flash 或直接按字节发给云端。代价是:

DRY_NODE_II_TAB_T 末尾的 u16_sys_workmode、u8_bat_soc、s32_meter_actpower_t 是每周期刷新的采集量却被放在 pack(1) 的"表"结构里,命名上带 _tab 后缀但实为运行态工作区,容易被误当成持久化配置。

4.3 宏与硬件的绑定关系

dry_node.h:20-30 的 8 个宏把逻辑层与 hardware/gpio/gpio.h 绑定:

宏展开底层实现
DRY_NODE1_ON / DRY_NODE1_OFFdo0_output_on() / do0_output_off()hardware/gpio/gpio.h:168-169
DI_1_DRY_CONTACTdo0_output_bit_get()hardware/gpio/gpio.h:170(读 DO0 的输出寄存器回读)
DI_1_ACCESS_POINTdi0_input_bit_get()hardware/gpio/gpio.h:171
DRY_NODE2_ON / DRY_NODE2_OFFdo1_output_on() / do1_output_off()hardware/gpio/gpio.h:173-174
DI_2_DRY_CONTACTdo1_output_bit_get()hardware/gpio/gpio.h:175
DI_2_ACCESS_POINTdi1_input_bit_get()hardware/gpio/gpio.h:176

命名上 DI_1_DRY_CONTACT 实际读的是 DO 的输出回读位(而非输入引脚),DI_1_ACCESS_POINT 才是真正的 DI 输入——名字里的 DI/DO 容易混淆。

4.4 声明与实现的不一致

5. 对外接口

本文件导出的函数与全局变量:

名称类型供谁调用
Handle_Dry_Node_Logic函数midware/interface/interface.c:221(仅此一处调用)
dry_node_io_init函数声明无调用者、无实现(死声明)
STDryNodeInfo全局变量data/system_config/system_config.c、midware/interface/dry_node/dry_node.c、midware/interface/modbus/modbus.c、net/ems.c、net/server.c(经 dry_node.h 引用的所有 10 个 include 者)
13 个枚举 / 4 个结构体 / 8 个 IO 宏类型与宏经 dry_node.h 引用的所有文件

本文件调用的外部函数/变量:

名称来源模块
do0_output_on/off、do0_output_bit_get、di0_input_bit_get、do1_output_on/off、do1_output_bit_get、di1_input_bit_gethardware/gpio/gpio.h(本头文件 dry_node.h:14 include)
ASSERT_FLASH_SECTOR_SIZEmidware/debug/debug.h:19(同文件还有 FLASH_SECTOR_SIZE)
STATIC_ASSERT工程通用断言宏,midware/debug/debug.h:16(dry_node.h:15 include)

6. 依赖关系

依赖(本文件 include)

dry_node.h:13-15:chip.h、gpio.h、debug.h。ASSERT_FLASH_SECTOR_SIZE 与 STATIC_ASSERT 都由 debug.h 提供,不需要 system_config.h。

被依赖(哪些文件会用到它)

全工程共 10 个文件 include dry_node.h:

文件用途
midware/interface/dry_node/dry_node.c:15本模块实现
midware/interface/interface.c:16调用 Handle_Dry_Node_Logic()
data/system_config/system_config.c:14STDryNodeInfo 的 Flash 读写与 CRC 校验
midware/interface/modbus/modbus.c:38Modbus 寄存器映射读写
net/ems.c:26云端上报/下发
net/server.c:27本地 Web 服务器按字段名读写(net/server.c:3969-3997 有完整字段表)
logic/generator.c:16柴油机联动
midware/bms/bms.c:26BMS 侧联动
midware/debug/board_test.c:30产测板级调试
system/board_init/board_init.c:42上电初始化

7. 备注与风险点

  1. dry_node_io_init() 是死声明:只声明、无实现、无调用(dry_node.h:208),应删除或补实现,否则调用者会拿到链接错误。
  2. DRY_NODE_II_MODE_E 有 3 个枚举值形同虚设(dry_node.h:67-69):DRY_NODE_UPS_MODE 与 DRY_NODE_SGREADY_MODE 在 dry_node.c 中从未被赋给 u8_cur_dry_node_mode,是纯设计残留;DRY_NODE_NONE_MODE 仅在 dry_node.c:1278(干接点未使能时)被赋值,但 deal_dry_node_mode()(dry_node.c:1406-1526)与 deal_dry_node_tick()(dry_node.c:1548-1654)都没有为它写 case,一律落到 default 分支强制 NODE_DONE_OPEN,因此它的行为与"未知模式"完全相同,None 语义没有独立实现。
  3. 命名不一致:STDryNodeTab(老式匈牙利/裸命名)与 DRY_NODE_II_TAB_T(下划线全大写)两种风格并存;DRY_NODE_STATUS_MAX(无 _E 后缀)与其它 ..._E 后缀枚举混杂;u8_modbus_dry1/2 是全表唯一使用小写下划线风格的字段。
  4. pack(1) 下的非对齐多字节字段:STDryNodeTab 在 pack(1) 下 u32SwitchOn1、u16FeedValue1 等字段的偏移完全由前序 u8 数量决定,任何字段增删都会移动后续所有字段,与 Flash 中已存数据、net/server.c 的字段表、Modbus 寄存器映射三处产生联动破坏。STATIC_ASSERT 只保护了"总尺寸整除扇区",不保护字段偏移,缺少版本号/字段偏移固化机制。
  5. u8Status1/2、u32DelayTick1/2、u32DurationTick1/2、u32PauseTick1/2、u8RelayDone1/2 是运行态却存在持久化表里:这些字段会被写入 Flash 并在下电恢复时读回,而 tick 值是 OSTimeGet() 的绝对 tick,重启后 tick 归零,恢复出来的 u32DelayTick1 会变成"未来时间",导致首次判断时 OSTimeGet() - u32DelayTick1 出现巨大的无符号差值回绕——dry_node.c:1573/1603/1632 有专门回绕分支处理,但依旧依赖正确性,属于高风险设计。
  6. u8_reserve[28] 手算预留:预留区长度 28 是为凑 Flash 扇区整除而人工计算的结果(dry_node.h:197),一旦增删字段必须重新手算,建议改为编译期按剩余空间推导。
  7. DEBUG_DRY_NODE 是"分支编号打印"式调试:dry_node.c 全文件共 52 × 2 处 printf("Dry_Node1_Logic...N"),一旦打开会让 500 ms 周期的任务严重超时,不宜在生产构建中启用。
  8. STDryNodeTab 的 u16_head / u16_crc_code 无版本/长度字段:Flash 格式升级时无法识别旧版布局,只能靠 CRC 失败后整表复位。