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

dry_node.c

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

1. 文件职责

本文件实现 EMS 主控板两路干接点(继电器)输出的全部现场逻辑,是"储能状态 → 外部负载/热泵/柴油机/EPO 联动"的翻译层。它把电池 SOC、电网电表功率、逆变器运行模式(含 UPS)、当前星期与时间段、云端/Modbus 下发的配置参数合并判断,最终驱动 DRY_NODE1_ON/OFF、DRY_NODE2_ON/OFF 两个 GPIO。文件同时承载两代逻辑:旧版就地判定 Dry_Node1_Logic() / Dry_Node2_Logic()(SOC + 馈网功率 + 延迟/持续/暂停节流),以及新版 Dry_Node_Logic_ii()(13 种 DRY_NODE_II_MODE_E 模式的流水线式处理)。对外唯一入口是 Handle_Dry_Node_Logic(),由 midware/interface/interface.c:221 的 task_interface_parse 任务每周期调用一次(该任务 drm_mode_judge() 之后、query_parameter_save_tags() 之前),因此本文件没有自己的任务或信号量,是纯轮询式的被动执行体。除常规干接点外,它还顺手承担三件"挂靠"工作:柴油机干接点跟随、老化模式干接点跟随、外置 NPE(N-PE 短接继电器)控制与 EPO 干接点输入采集。

2. 关键数据结构与宏

名称类型说明
STDryNodeTab / STDryNodeInfo结构体(#pragma pack(1),定义于 dry_node.h:133)干接点持久化配置表,本文定义实例(dry_node.c:29)。对应 Flash DRY_NODE_PARA_PAGE(4078) 一个扇区镜像,含 u16_head、u16_crc_code、u8_reserve[28];字段分 1/2 通道各一份:u8ChannelN、u8ControlModeN、u8StartTimeNA/NB、u8EndTimeNA/NB、u8DateN、u8ChargeSOCN、u8ChargeModeN、u8UPSN、u32SwitchOnN、u32SwitchOffN、u8DelayN、u8DurationN、u8PauseN、u8StatusN、u32DelayTickN、u32DurationTickN、u32PauseTickN、u8RelayDoneN,另有新版的 u8FeedModeN、u16FeedValueN、u8SGEnableN、u16StartTimeMNA/MNB、u16EndTimeMNA/MNB、u8_modbus_dry1/2
DRY_NODE_II_TAB_T结构体(dry_node.h:93)新版 II 逻辑的运行期工作区。由配置字段(u8_control_mode、u8_ups_enable、u8_sg_ready_enable、u16_timeX_start/end_tick、u8_date、u8_set_soc_mode_value、u8_set_feed_mode、u32_set_feed_value、u8_delay/duration/pause_time)+ 运行状态(u8_pre/cur_dry_node_mode、u8_dry_node_mode_status、u8_dry_node_ctrl、u8_time_flag、u8_week_flag、三组 *_tick)+ 现场量(u16_sys_workmode、u8_bat_soc、s32_meter_actpower_t)构成。本文定义实例 g_t_dry_node_ii_info1/2(dry_node.c:30)
DRY_NODE_II_MODE_E枚举(dry_node.h:65)II 模式全集 13 项:0=DRY_NODE_NONE_MODE、1=DRY_NODE_UPS_MODE、2=DRY_NODE_SGREADY_MODE、3=TIME_MODE_SWITCH_ON、4=TIME_MODE_SWITCH_OFF、5/6=AUTO_MODE_SOC_ON_UPS_ON_AT_LEAST/AT_MOST、7/8=AUTO_MODE_SOC_ON_FEED_ON_AT_LEAST/AT_MOST、9/10=AUTO_MODE_SOC_ON_FEED_OFF_AT_LEAST/AT_MOST、11/12=AUTO_MODE_SOC_OFF_FEED_ON_AT_LEAST/AT_MOST。DRY_NODE_UPS_MODE、DRY_NODE_SGREADY_MODE 在本文件中从未被赋值,属预留值
DRY_NODE_MODE_STATUS_E枚举(dry_node.h:82)节流状态机状态:DRY_NODE_STATUS_NONE(0)、DRY_NODE_DELAY_STATUS(1)、DRY_NODE_DURATION_STATUS(2)、DRY_NODE_PAUSE_STATUS(3)、DRY_NODE_STATUS_MAX(4)
ENDryNodeDo枚举(dry_node.h:32)继电器动作请求:NODE_DONE_OPEN=0x01、NODE_DONE_CLOSE=0x02、NODE_DONE_NONE=0x03(可操作)。写入 u8RelayDoneN / u8_dry_node_ctrl
ENDryNodeStatus枚举(dry_node.h:39)继电器实际回读状态:NODE_OPEN=0x00、NODE_CLOSE=0x01(由 DI_x_DRY_CONTACT 得到)
ENDryNodeMode枚举(dry_node.h:45)控制模式:MODE_ON=1、MODE_OFF=2、MODE_AUTO=3
ENDryNodeCharge枚举(dry_node.h:52)SOC/馈网判据:AT_LEAST=1、AT_MOST=2、AT_DISABLE=3
ENDryNodeNum枚举(dry_node.h:59)通道号:DRY_NODE_ONE=1、DRY_NODE_TWO=2
DRY_NODE1_ON / DRY_NODE1_OFF宏(dry_node.h:20-21)等价 do0_output_on() / do0_output_off()(hardware/gpio/gpio.c)
DRY_NODE2_ON / DRY_NODE2_OFF宏(dry_node.h:26-27)等价 do1_output_on() / do1_output_off()
DI_1_DRY_CONTACT / DI_2_DRY_CONTACT宏(dry_node.h:23,29)继电器回读:do0_output_bit_get() / do1_output_bit_get()
DI_1_ACCESS_POINT / DI_2_ACCESS_POINT宏(dry_node.h:24,30)数字输入:di0_input_bit_get() / di1_input_bit_get()(EPO 信号源)
DEBUG_DRY_NODE宏(dry_node.h:18,值 0)打开后每次判定会输出数十条 printf 定位语句,量产必须为 0

3. 函数清单

函数名作用域说明
Get_Dry_Node_Status全局(无 static,dry_node.c:43)按通道号返回对应继电器触点回读值(DI_x_DRY_CONTACT)
Get_Week_By_Date全局(dry_node.c:54)用类 Zeller 公式把 SYSTEM_DATETIME 换算成 1~7 的星期序号(1=周一)
Judge_By_Time全局(dry_node.c:96)按小时判断当前时间是否落在 [start, end) 区间,支持跨天;起止相同视为无效
Dry_Node1_Logic全局(dry_node.c:144)旧版通道 1 就地逻辑:UPS 优先,其次 ON/OFF/AUTO 三模式,AUTO 下用 SOC+馈网功率+延迟/持续/暂停节流
Dry_Node2_Logic全局(dry_node.c:630)旧版通道 2 就地逻辑,与通道 1 结构完全同构(字段换 *2、宏换 DRY_NODE2_*)
update_dry_node_data全局(dry_node.c:1117)按 channel 把 STDryNodeInfo 配置 + 现场量(运行模式/SOC/电表功率)刷入 g_t_dry_node_ii_info1/2
check_dry_node_mode全局(dry_node.c:1185)仅按配置项(UPS 使能/SG-Ready 使能/SOC 模式/馈网模式)挑选 u8_cur_dry_node_mode,不判断条件是否成立
judge_by_week_time全局(dry_node.c:1289)按分钟判断两段时间是否命中,并判断当天是否在 u8_date 星期掩码内,写 u8_time_flag / u8_week_flag
deal_dry_node_mode全局(dry_node.c:1403)按 u8_cur_dry_node_mode 的 11 个有效分支求 u8_dry_node_ctrl(期望的继电器动作)
deal_dry_node_tick全局(dry_node.c:1536)延时/持续/暂停三态节流状态机,并对 OSTimeGet() 回绕做了补偿判断
driver_dry_node全局(dry_node.c:1664)把 u8_dry_node_ctrl 落到具体通道的 DRY_NODE1_ON/OFF 或 DRY_NODE2_ON/OFF
handle_outside_npe_function全局(dry_node.c:1693)外置 NPE 场景下用通道 1 干接点表示 UPS 态:市电三相机电压最大值 > 60 V 或退出 UPS 则断开,进入 UPS 则闭合
Dry_Node_Logic_ii全局(dry_node.c:1726)新版 II 逻辑的 6 段流水线:更新数据 → 选模式 → 判周/时间 → 求动作 → 节流 → 驱动
Handle_Dry_Node_Logic全局(dry_node.c:1767)对外唯一入口:先做柴油机/老化/外置 NPE 的恢复处理与 EPO 干接点采集,再按优先级选择 II 逻辑或旧版双通道逻辑

4. 核心逻辑解析

4.1 入口 Handle_Dry_Node_Logic() 的分支优先级(dry_node.c:1767)

函数用一组互斥的 if / else if 决定本周期"谁能驱动 DRY_NODE1",顺序即优先级:

  1. 状态跳变复位(dry_node.c:1782、1792、1801):用三个 static 变量分别镜像 t_config_data.Generator、DispatchInfo.u8ControlMode、t_config_data.u8_outside_npe_en 的上一次值。一旦发现由开变关,就地恢复默认:柴油机模式退出 → u8_generator_on = FALSE 且 DRY_NODE1_OFF;退出 AGING_MODE → DRY_NODE1_OFF;外置 NPE 关闭 → inverter_set_n_n_short(1)、inverter_set_n_pe_short_enable(TRUE)、DRY_NODE1_OFF;外置 NPE 打开 → inverter_set_n_n_short(2)、inverter_set_n_pe_short_enable(FALSE)。
  2. 上电 10 秒延时 + EPO 干接点采集(dry_node.c:1777、1817):首次调用时把 s_u32_epo_di_delay_start = OSTimeGet();之后仅在 OSTimeGet() - s_u32_epo_di_delay_start > 10*1000 时才调用 epo_set_signal_status(采样值)。采样源按机型分流:INVERTER_TYPE == ALPHAESS_TYPE 且子型号为 SUB_S5/SUB_B5/SUB_S3_6 时,强制把 Generator 置 FALSE 并取 DI_1_ACCESS_POINT,否则取 DI_2_ACCESS_POINT。这一分支只在 epo_read_config_enable() == TRUE 时生效。
  3. 外置 NPE(dry_node.c:1839):u8_outside_npe_en == TRUE → 只跑 handle_outside_npe_function(),本周期不再处理普通干接点。
  4. 柴油机(dry_node.c:1843):Generator == TRUE → DRY_NODE1 跟随 g_t_logic_info.u8_generator_on(由 logic/generator.c 设定)。G3 单相机(SUB_S5/SUB_B5/SUB_S3_6)在 EPO 使能时被显式跳过。
  5. 老化模式(dry_node.c:1865):DispatchInfo.u8ControlMode == AGING_MODE → u16_mppt_percentage == 0 闭合,否则断开。
  6. OEM_HHE 定制(dry_node.c:1876):g_t_system_base_info.u16_oem_flag == OEM_HHE 时,无 EMS 故障 + BMS 无故障 + DI_1 未接 → 断开,其余情况闭合(安全反逻辑:有故障就闭合)。
  7. 默认干接点业务(dry_node.c:1887):依旧先排除 G3 单相机 + EPO 使能的情况;随后按配置分流——
    • STDryNodeInfo.u8FeedMode1 != 0 || u8FeedMode2 != 0 → 走新版 Dry_Node_Logic_ii();
    • 否则 → 走旧版 Dry_Node1_Logic(); Dry_Node2_Logic();。

即:"馈网模式配置项是否为 0"是新旧两代逻辑的切换开关。

4.2 新版流水线 Dry_Node_Logic_ii()(dry_node.c:1726)

固定 6 步、每步对通道 1 和 2 各调一次,无内部状态、无分支:

update_dry_node_data(CH1/CH2)   // 采集
check_dry_node_mode(CH1/CH2)    // 选模式
judge_by_week_time(CH1/CH2)     // 周/时间
deal_dry_node_mode(CH1/CH2)     // 期望动作
deal_dry_node_tick(CH1/CH2)     // 节流
driver_dry_node(CH1/CH2)        // 输出

4.3 采集 update_dry_node_data()(dry_node.c:1117)

虽然是"按指针入参",实际函数体完全忽略 t_dry_node_info,直接写全局 g_t_dry_node_ii_info1 或 g_t_dry_node_ii_info2(switch(channel) 只有 DRY_NODE_ONE/DRY_NODE_TWO 两例,无 default)。每次调用都现场取三个量,因此 II 逻辑的时效性等于调用周期:

4.4 模式选择 check_dry_node_mode()(dry_node.c:1185)

u8_dry_node_enable == FALSE                     → DRY_NODE_NONE_MODE
control_mode == MODE_ON                          → TIME_MODE_SWITCH_ON
control_mode == MODE_OFF                         → TIME_MODE_SWITCH_OFF
control_mode == MODE_AUTO:
    ups_enable == TRUE && sys_workmode == cUPS:
        set_soc_mode == AT_LEAST → AUTO_MODE_SOC_ON_UPS_ON_AT_LEAST
        set_soc_mode == AT_MOST  → AUTO_MODE_SOC_ON_UPS_ON_AT_MOST
    else:
        sg_ready_enable == TRUE:
            SOC=AT_LEAST & FEED=AT_LEAST   → AUTO_MODE_SOC_ON_FEED_ON_AT_LEAST
            SOC=AT_LEAST & FEED=AT_DISABLE → AUTO_MODE_SOC_ON_FEED_OFF_AT_LEAST
            SOC=AT_DISABLE & FEED=AT_LEAST → AUTO_MODE_SOC_OFF_FEED_ON_AT_LEAST
        sg_ready_enable == FALSE:
            AT_LEAST/AT_LEAST → AUTO_MODE_SOC_ON_FEED_ON_AT_LEAST
            AT_MOST /AT_MOST  → AUTO_MODE_SOC_ON_FEED_ON_AT_MOST
            AT_LEAST/AT_DISABLE → AUTO_MODE_SOC_ON_FEED_OFF_AT_LEAST
            AT_MOST /AT_DISABLE → AUTO_MODE_SOC_ON_FEED_OFF_AT_MOST
            AT_DISABLE/AT_LEAST  → AUTO_MODE_SOC_OFF_FEED_ON_AT_LEAST
            AT_DISABLE/AT_MOST   → AUTO_MODE_SOC_OFF_FEED_ON_AT_MOST

注意:SG-Ready 使能时不支持 AT_MOST(三条分支全是 AT_LEAST/AT_DISABLE 组合),且所有未命中的组合(含 AT_DISABLE/AT_DISABLE)都不做赋值,u8_cur_dry_node_mode 会保留上一次的值。

4.5 星期与时间段判定

Get_Week_By_Date()(dry_node.c:54) 用 Zeller 同余的变形公式:

if (month == 1 || month == 2) { Year = Years - 1; Month = Month + 12; }
Week = (Century/4) - 2*Century + Year + Year/4 + 13*(Month+1)/5 + Day - 1;
Week %= 7;  if (Week == 0) Week = 7;   // 结果域 1..7,1=周一

Century 是常量 20(写死),因此只对 20xx 年成立。

Judge_By_Time(start, end)(dry_node.c:96) 只比 SYSTEM_DATETIME.bHours(小时粒度):start == end → FALSE;start < end → start <= h < end;start > end(跨天)→ h >= start || h < end。

judge_by_week_time()(dry_node.c:1289) 是 II 逻辑的对应实现,改用 SYSTEM_DATETIME.bHours*60 + bMinutes 的分钟粒度 l_system_tick,对两段时间各得出 l_u8_timeN_flag(0=段内有效、1=段外、2=段无效),再合并:任一段 flag==0 → u8_time_flag = 0;否则任一段 flag==1 → = 1;否则 → = 2。跨天段(start >= end)在本函数中被判为无效(越界即"时间段内有效"的反向写法,dry_node.c:1326–1336)——与 Judge_By_Time 的跨天语义相反。星期判定同旧版:u8_date & (1 << (week-1)),命中写 u8_week_flag = 0,否则 1(dry_node.c:1385)。

4.6 期望动作 deal_dry_node_mode()(dry_node.c:1403)

以 u8_cur_dry_node_mode 为 switch 键,输出 u8_dry_node_ctrl(NODE_DONE_CLOSE / NODE_DONE_OPEN);default 一律 OPEN。

模式闭合(CLOSE)条件
TIME_MODE_SWITCH_ONu8_time_flag == 0 && u8_week_flag == 0
TIME_MODE_SWITCH_OFF上述条件不成立(时间/星期反逻辑)
AUTO_MODE_SOC_ON_UPS_ON_AT_LEASTbat_soc >= set_soc_value
AUTO_MODE_SOC_ON_UPS_ON_AT_MOSTbat_soc <= set_soc_value
AUTO_MODE_SOC_ON_FEED_ON_AT_LEASTbat_soc >= set_soc_value 且 meter_actpower_t < 0 && abs(...) >= set_feed_value
AUTO_MODE_SOC_ON_FEED_ON_AT_MOSTbat_soc <= set_soc_value 且(power < 0 && abs(power) <= set_feed_value 或 power > 100)
AUTO_MODE_SOC_ON_FEED_OFF_AT_LEASTbat_soc >= set_soc_value
AUTO_MODE_SOC_ON_FEED_OFF_AT_MOSTbat_soc <= set_soc_value
AUTO_MODE_SOC_OFF_FEED_ON_AT_LEASTpower < 0 && abs(power) >= set_feed_value
AUTO_MODE_SOC_OFF_FEED_ON_AT_MOST(power < 0 && abs(power) <= set_feed_value)或 power > 100

power > 100 中的 100 是写死的魔法数(正向取电超过 100 W 即视为无馈网)。

4.7 节流状态机 deal_dry_node_tick()(dry_node.c:1536)

状态迁移(u8_dry_node_mode_status):

DELAY:   ctrl == CLOSE 且 (now - delay_tick) > delay_time*60*1000
             → DURATION_STATUS, duration_tick = now
         ctrl == CLOSE 且未超时 → 强制 ctrl = OPEN(延时未到,压住闭合)
         ctrl != CLOSE + → delay_tick = now(刷新延时)
DURATION:ctrl == OPEN 且超 duration_time → PAUSE_STATUS, pause_tick = now
         ctrl == OPEN 且未超时 → 强制 ctrl = CLOSE(持续期内禁止断开)
         ctrl != OPEN → pause_tick = now
PAUSE:   ctrl == CLOSE 且超 pause_time → DELAY_STATUS, delay_tick = now
         ctrl == CLOSE 且未超时 → 强制 ctrl = OPEN(暂停期内禁止闭合)
         ctrl != CLOSE → delay_tick = now
default: ctrl = NODE_DONE_OPEN

即语义为:"延时"保证动作前先等够 delay_time,"持续"保证闭合后至少保持 duration_time,"暂停"保证断开后至少静默 pause_time。三处超时判断都写了两个条件,第二个专门处理 OSTimeGet() 的 32 位回绕:(now > tick && now - tick > T) || (tick > now && (0xFFFFFFFF - tick + now) > T)(dry_node.c:1567、1597、1626 等)。0xFFFFFFFF - tick + now 少加了 1,回绕时判定偏保守 1 ms。

4.8 输出驱动 driver_dry_node()(dry_node.c:1664)

DRY_NODE_ONE → u8_dry_node_ctrl == NODE_DONE_CLOSE ? DRY_NODE1_ON : DRY_NODE1_OFF;DRY_NODE_TWO 同理走 DRY_NODE2_*。无 default 分支。注意此处用 else 兜底所有非 CLOSE 值:u8_dry_node_ctrl 若还是 NODE_DONE_NONE(未初始化过)也会落到 OFF,不会出现"三态无输出"。

4.9 旧版就地逻辑 Dry_Node1_Logic() / Dry_Node2_Logic()

两者结构完全同构(通道 1 见 dry_node.c:144–621,通道 2 见 dry_node.c:630–1107),下面以通道 1 描述,通道 2 只是字段后缀换成 *2、驱动宏换成 DRY_NODE2_*。

输入:u8BatSoc = bms_read_total_soc()/10、s16ActPower = meter_read_actpower(GRID_METER, PHASE_T)(本地截断为 s16)、l_u8Ch1TimeSectorA/B = Judge_By_Time(...)、l_u8Week = Get_Week_By_Date();回读触点写回 STDryNodeInfo.u8Status1。

主分支结构:

u8Channel1 != ENABLE:
    if (s_u8_enable_flag1 == TRUE) { u8RelayDone1 = NODE_DONE_OPEN; flag = FALSE; }  // 从使能变禁用,恢复默认
    else                            u8RelayDone1 = NODE_DONE_NONE;
    u32DelayTick1 = OSTimeGet();
u8Channel1 == ENABLE:
    s_u8_enable_flag1 = TRUE;
    if (inverter_read_run_mode() == cUPS):            // UPS 模式最高优先
        s_u8_ups_flag1 = TRUE;
        if (u8UPS1 == ENABLE)
            u8RelayDone1 = (BatSoc <= u8ChargeSOC1) ? NODE_DONE_CLOSE : NODE_DONE_OPEN;
        else
            u8RelayDone1 = NODE_DONE_NONE;
    else:
        MODE_ON:    u8Date1 & (1<<(week-1)) ? (A||B 命中 ? CLOSE : OPEN) : OPEN
        MODE_OFF:   u8Date1 & (1<<(week-1)) ? (A||B 命中 ? OPEN  : CLOSE) : CLOSE
        MODE_AUTO:  见下
        else:       u8RelayDone1 = NODE_DONE_NONE;
        若本周期从 UPS 跳出(s_u8_ups_flag1 == TRUE)→ u8RelayDone1 = NODE_DONE_OPEN; flag = FALSE;

AUTO 模式(dry_node.c:300–495)把"SOC 判据"与"馈网功率判据"组合成两族,且对常开/常闭两种接线做了反向映射:

动作执行段(dry_node.c:543–620):u8RelayDone1 == NODE_DONE_OPEN → DRY_NODE1_OFF,AUTO 模式下等过 u8Pause1*60*1000(自 u32PauseTick1)才把标记复位为 NODE_DONE_NONE 并刷新 u32DelayTick1;非 AUTO 模式立即复位。u8RelayDone1 == NODE_DONE_CLOSE → DRY_NODE1_ON,AUTO 模式下等过 u8Duration1*60*1000 复位,非 AUTO 立即复位。注意旧版逻辑在判定函数内部直接操作继电器 GPIO,不走 driver_dry_node()。

4.10 外置 NPE handle_outside_npe_function()(dry_node.c:1693)

取三相 inverter_read_grid_volt(PHASE_L1/L2/L3) 的最大值(两次三元比较)。断开条件:max(grid_volt) > 60(市电已恢复)或(当前不在 cUPS 且上一周期在 UPS)。闭合条件:inverter_read_run_mode() == cUPS。用 static u8 s_u8_ups_flag 记忆 UPS 跳变。60 V 是判断"市电是否存在"的经验阈值。

5. 对外接口

本文件导出

名称类型使用方
STDryNodeInfo全局变量(dry_node.c:29)data/system_config/system_config.c(Flash 读写与 CRC)、midware/interface/modbus/modbus.c(寄存器读写,约 5068–5201 读、7876–8009 写)、net/ems.c(上报/接收)、net/server.c(JSON 字段表 3969–4006、4834–4849)
g_t_dry_node_ii_info1 / g_t_dry_node_ii_info2全局变量(dry_node.c:30)仅本文件内部使用(II 逻辑工作区),无外部引用
Handle_Dry_Node_Logic函数(声明于 dry_node.h:209)midware/interface/interface.c:221(task_interface_parse 主循环)
Get_Dry_Node_Status、Get_Week_By_Date、Judge_By_Time、Dry_Node1_Logic、Dry_Node2_Logic、update_dry_node_data、check_dry_node_mode、judge_by_week_time、deal_dry_node_mode、deal_dry_node_tick、driver_dry_node、handle_outside_npe_function、Dry_Node_Logic_ii函数均为外部链接但未在头文件声明,实际只被本文件内 Handle_Dry_Node_Logic / Dry_Node_Logic_ii 调用;dry_node.h:208 声明的 dry_node_io_init() 在本文件没有实现(死声明)

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

来源名称
hardware/gpio/gpio.hdo0_output_on/off、do0_output_bit_get、di0_input_bit_get、do1_output_on/off、do1_output_bit_get、di1_input_bit_get
midware/bms/bms.h:1190bms_read_total_soc()(返回 0.1% 单位)
midware/meter/meter.h:471meter_read_actpower(site, phase)(s32,负值=馈网)
midware/inverter/inverter.hinverter_read_run_mode()(2011)、inverter_read_grid_volt()(1986)、inverter_set_n_n_short()(2100)、inverter_set_n_pe_short_enable()(2102)
logic/logic.hepo_read_config_enable()(691)、epo_set_signal_status()(694)、g_t_logic_info
logic/dispatch.hg_t_dispatch_t.t_stsystemcmdinfo.u8ControlMode、AGING_MODE(46)
hmi/key.h:229SYSTEM_DATETIME(RTC 年月日时分秒)
data/system_config/system_config.hg_t_system_base_info.u16_oem_flag(803)、OEM_HHE(57)
data/data.hg_t_ems_info.u_ems_fault.u32_all
midware/inverter/inverter.hg_t_inv_info.t_config_data.u8_inv_model_sub、g_t_inv_info.t_run_info.u16_mppt_percentage、INVERTER_TYPE、ALPHAESS_TYPE、SUB_S5、SUB_B5、SUB_S3_6、cUPS、PHASE_L1/L2/L3、PHASE_T
uCOS-IIOSTimeGet()(OS_TICKS_PER_SEC = 1000,即 1 tick = 1 ms)
标准库abs()、memset(间接)

6. 依赖关系

依赖

includes.h、ethernet.h、dry_node.h、bms.h、inverter.h、key.h、pcf8563.h、meter.h、logic.h、dispatch.h、system_config.h、data.h。其中 ethernet.h、pcf8563.h 在本文件中未见直接引用符号,属历史遗留 include。

被依赖

7. 备注与风险点

  1. update_dry_node_data() 的指针参数被完全忽略(dry_node.c:1117)、函数体直接写全局 g_t_dry_node_ii_info1/2。调用点传 &g_t_dry_node_ii_info1 看似正确,但一旦有人复制函数复用到第三个实例就会静默写错对象。建议改为 *t_dry_node_info = ... 或去掉参数。
  2. check_dry_node_mode() 存在"模式残留":所有 else { //不执行 } 路径不赋值,u8_cur_dry_node_mode 保留上一次的值。配置切到 AT_DISABLE/AT_DISABLE、或 SG-Ready 使能下选 AT_MOST 等未覆盖组合时,干接点会继续按旧模式动作,而不是回到 DRY_NODE_NONE_MODE。
  3. 新旧两代逻辑的跨天语义不一致:Judge_By_Time()(dry_node.c:96)把 start > end 当跨天并正确处理,而 judge_by_week_time()(dry_node.c:1326、1357)在 start >= end 时把区间判为"段内无效/段外",两套配置字段(u8StartTimeNA vs u16StartTimeMNA)语义不同。云平台下发跨天时段时行为会随新旧模式切换而变。
  4. 时间粒度不同:旧版按小时(bHours),新版按分钟(bHours*60 + bMinutes)。旧版配置 8→18 实际是 [8:00, 18:00),中间分钟无效——属于设计而非 bug,但配置文档需明确。
  5. s16 l_s16ActPower = meter_read_actpower(...) 截断(dry_node.c:151、637):meter_read_actpower 返回 s32,此处收窄到 s16(±32767 W)。大功率站点(>32.7 kW)会溢出变号。而新版 update_dry_node_data() 用 s32(dry_node.c:1144)没有这个问题——同一份数据两处类型不一致。
  6. 魔法数:l_s16ActPower >= 100(dry_node.c:363、411、849、897、1468、1513)、外置 NPE 的 60 V(dry_node.c:1705)、EPO 上电延时 10*1000 ms(dry_node.c:1817)、AT_DISABLE 作为"关闭 SOC 判据"复用(dry_node.c:310、796)。建议补宏。
  7. Get_Week_By_Date() 的 u8 运算与写死世纪:Century = 20 写死;l_u8Year = SYSTEM_DATETIME.bYears - 1 在 bYears == 0(2000 年 1/2 月)时按 u8 回绕成 255,算出星期错误(dry_node.c:64)。21 世纪其余年份中间值在 1~154 之间,赋值给 u8 不会溢出,但缺少注释说明这一巧合。
  8. Handle_Dry_Node_Logic() 的 EPO 分支有副作用:g_t_logic_info.t_config_data.Generator = FALSE 被写回配置结构(dry_node.c:1828),对 G3 单相机是"悄悄关掉柴油机配置",属于跨模块写配置,需确认云端不会因此报配置不一致。
  9. Dry_Node1_Logic/Dry_Node2_Logic 在逻辑判定内部直接驱动 GPIO(dry_node.c:549、584、1035、1070),与 driver_dry_node() 是两条并行驱动路径。虽然 Handle_Dry_Node_Logic() 的 if/else 保证同周期只走一条,但 Handle_Dry_Node_Logic() 开头的柴油机/老化/EPO 分支已经可能驱动过 DRY_NODE1,随后若配置回落到旧版逻辑分支,会在同一周期内二次改写。存在同一周期多次写同一 GPIO 的情况。
  10. s_u8_ups_flag1/2 与 s_u8_enable_flag1/2 是函数内 static,不是配置的一部分:这两组跳变标记在复位后靠上一周期值区分"由 UPS 掉出/由使能变禁用",掉电重启后不会恢复。
  11. DEBUG_DRY_NODE 若误开为 1,Dry_Node1_Logic/Dry_Node2_Logic 每个调用周期会执行 50+ 次 printf,task_interface_parse 是 500 ms 周期任务,会严重拖慢任务链(dry_node.h:18)。
  12. DRY_NODE_UPS_MODE、DRY_NODE_SGREADY_MODE、DRY_NODE_STATUS_NONE、DRY_NODE_STATUS_MAX 在本文件中从未被使用,属预留/遗留枚举值。
  13. includes.h 之外的 ethernet.h、pcf8563.h 未见使用,可清理。