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

sunspec_business.c

源文件路径:midware/interface/modbus/sunspec_business.c 所属模块:midware/interface/modbus(Sunspec Modbus 协议应用层) 代码行数:1603

1. 文件职责

本文件是 Sunspec Modbus 从站协议在 EMS 侧的"业务数据层",负责把 EMS 内部的实时运行数据(逆变器、BMS、电表、并机、能量统计)翻译成 Sunspec 标准模型的数据结构,并把上位机(第三方调度器/网关)通过 Modbus 写下来的 Sunspec 控制点翻译回 EMS 的调度接口。

它在数据流中处于"寄存器映射表 ↔ 业务结构体"的中间环节:sunspec_modbus.c 里 783 条 g_t_sunspec_mudbus[] 地址表把 Sunspec 寄存器地址绑定到本文件定义/刷新的各个数据结构字段上,本文件提供按地址二分查找的读写入口 sunspec_modbus_data_get/set,供 modbus.c 的从站响应流程调用。

驱动方式有两种:一是由 midware/interface/interface.c:369 的 Modbus-RTU 任务在收到合法 Sunspec 帧后(sunspec_refresh_flag_set() 打点)周期调用 sunspec_modbus_data_refresh() 刷新全部运行数据;二是由 logic/ac_control.c、logic/logic.c 在充放电控制与 MPPT 调度逻辑中反向读取本文件导出的铭牌/限值/使能接口。

此外本文件还承载了"机型铭牌表"这一静态配置:14 种 AlphaESS 三代机型(G3-S5/S6/S8/B5/S3.6/T4/T5/T6/T8/T10/T12/T15/T20)的额定功率、无功能力、电压电流范围、控制模式位图等参数都硬编码在这里。

2. 关键数据结构与宏

2.1 本文件定义/初始化的对象

名称类型说明
g_sunspec_comm结构体变量(SUNSPEC_COMM_T)Sunspec 通信看门狗状态,含 refresh_tick(最近一次合法帧的 OSTimeGet())与 refresh_flag。文件作用域,无 extern 声明,仅本文件内使用。
g_t_sunspec_inv_nameplate_array[]结构体数组(SUNSPEC_NAMEPLATE_T[SUNSPEC_INV_MAX],14 项)机型铭牌表,索引与 E_SUNSPEC_INV_TYPE 枚举一一对应(第 0 项 G3-S5 … 第 13 项 G3-T20 汇川)。
SUNSPEC_NAMEPLATE_T结构体(sunspec_modbus.h:182)铭牌数据结构:有功/视在/无功上限、充放电功率额定、AC 电压电流范围、功率因数、运行类别、supported_control_mode 位图、intentional_island,以及对应的 *_set 可写版本和 7 个 scale 系数。
SUNSPEC_COMM_T结构体(sunspec_modbus.h:430){u32 refresh_tick; u16 refresh_flag;}。
SUNSPEC_INV_MAX枚举末值(sunspec_modbus.h:98)= 14铭牌数组长度。

2.2 本文件主要引用的外部结构与宏

名称类型说明
SUNSPEC_MODBUS_T结构体(sunspec_modbus.h:101)地址表元素:data_p(指向业务字段的 u16*)、addr、default_value、init_type、big_endian、scale、rw。本文件的 data_get/set 完全依赖它。
g_t_sunspec_mudbus[]全局数组(SUNSPEC_MODBUS_T[783],定义于 sunspec_modbus.c:49)783 条 Sunspec 地址映射,必须按 addr 升序,本文件据此做二分查找。
E_R_W枚举(sunspec_modbus.h:16)RO=0x01 只读、WO=0x02 只写、RW=0x03 读写。data_get 用 (rw & RO)==RO 判读权限,data_set 用 (rw & WO)==WO 判写权限。
E_BIG_ENDIAN枚举(sunspec_modbus.h:22)BIG_ENDIAN / LIT_ENDIAN;LIT_ENDIAN 时做高低字节互换。
E_SCALE_FACTOR枚举(sunspec_modbus.h:27)SCALE_0_001=1 … SCALE_1=1000(代表 1 倍,不缩放)… SCALE_1000=1000000。
E_INIT_TYPE枚举(sunspec_modbus.h:78)INIT 允许上电写默认值,RUNING 保留。
E_SUNSPEC_INV_TYPE枚举(sunspec_modbus.h:83)铭牌数组索引:SUNSPEC_G3_S5=0 … SUNSPEC_G3_T20_INO=13。
E_SUNSPEC_WSET_ENA枚举(sunspec_modbus.h:37)有功调度相关地址常量 WSET_ENA_40300…WSET_ENA_40310,以及 MAX_CHG_40260/MAX_DISCHG_40261。
SUNSPEC_CONTROL_T结构体(sunspec_modbus.h:252)控制点集合 g_t_sunspec_ctlAC,本文件读写其中的 WsetEna/WsetMod/Wset/WsetPct/WsetEnaRvrt/WsetRvrtTms/WsetRvrtRem/mppt_percentage/mppt_over_count/RvrtTms_refresh_*。
SUNSPEC_RUN_DATA_T结构体(sunspec_modbus.h:131)运行数据 g_t_sunspec_run,本文件刷新其绝大部分字段。
SUNSPEC_DC_T / SUNSPEC_DC_GROUP_T结构体(sunspec_modbus.h:327/313)直流侧模型 714,group[4] 实际只用 group[0](电池)与 group[1](PV)。
SUNSPEC_AC_METER_T结构体(sunspec_modbus.h:382)交/直流电表模型 203,含 meter_Evt 位域联合体。
SUNSPEC_METER_EVT_U联合体(sunspec_modbus.h:342)电表事件位域,本文件只置 M_EVENT_Missing_Sensor。
INVERTER_SUB_E枚举(midware/inverter/inverter.h:305)机型子类型 SUB_S5/SUB_B5/SUB_S6/SUB_S8/SUB_S3_6/SUB_T4/T5/T6/T8/T10/T12/T15/T20,用于选择铭牌项。
INVERTER_RUN_MODE_E(cWaitMode 等)枚举逆变器运行模式,本文件映射为 Sunspec inv_sta(0=Off…7=Standby)。
POSITIVE_VALUE_OFFSET宏(logic/dispatch.h:16)= 32000调度功率的正偏置,避免负数在无符号通道里传输。
EVERGEN_FUNC_START宏(logic/dispatch.h:19)= 1set_dispatch_enable() 的入参。
SUNSPECT_ACTIVE_MODE枚举(logic/dispatch.h:54)调度模式:Sunspec 有功功率调度。
DISPATCH_MODE枚举(logic/logic.h:51)顺序控制模式:外部调度模式。
PHASE_A/B/C/T、PHASE_ALL、PV_CHANAL_ALL宏电表/逆变器读取的相别与通道选择。
ALPHAESS_TYPE、INV_SHUTDOWN宏逆变器厂商与停机状态判断。

3. 函数清单

函数名作用域说明
sunspec_modbus_data_get全局按 Sunspec 地址在 g_t_sunspec_mudbus[] 中二分查找,做权限校验、倍率换算、大小端调整后返回寄存器值。
sunspec_modbus_data_set全局按 Sunspec 地址二分查找后校验写权限、倍率换算、大小端调整并写回业务变量;对 WSET_ENA_40300~40310 额外置位回退计时刷新标志。
sunspec_modbus_data_init全局Sunspec 数据初始化:清零 base/run 结构、填 mn/md/opt/ver 标识、把 init_type==INIT 的地址表项写成默认值。
sunspec_active_power_dispatch_refreshstatic有功功率调度刷新:维护回退倒计时,把 Wset/WsetPct 换算成调度功率写入 g_t_dispatch_t,未使能时把 PV 放开到 100%。
sunspec_modbus_data_refresh全局Sunspec 运行数据总刷新:机型铭牌选择、逆变器状态、BMS/电表/PV/能量数据搬运到各 Sunspec 模型结构。
sunspec_modbus_dischg_get全局返回铭牌 max_dischg_set(电池最大放电功率设定值)。
sunspec_modbus_chg_get全局返回铭牌 max_charge_set(电池最大充电功率设定值)。
sunspec_modbus_Wset_ena_get全局返回 g_t_sunspec_ctlAC.WsetEna(有功调度使能)。
sunspec_modbus_mppt_percentage_get全局返回受 Sunspec 调度限制后的 MPPT 百分比。
sunspec_modbus_mppt_percentage_set全局设置 MPPT 百分比(上限 10000,即 100%);值 >0 时清零 mppt_over_count。
sunspec_modbus_mppt_over_count_add全局"PV 功率过大"计数自增,饱和到 10。
sunspec_modbus_mppt_over_count_get全局返回"PV 功率过大"计数。
sunspec_refresh_flag_set全局收到合法 Sunspec 帧时置位通信标志并记录 OSTimeGet()。
sunspec_refresh_flag_get全局读通信标志,超过 30000ms 未刷新则自动清零。
sunspec_modbus_max_charge_rat_get全局返回铭牌 max_charge_rat(电池额定充电功率)。
sunspec_modbus_max_discharge_rat_get全局返回铭牌 max_discharge_rat(电池额定放电功率)。

4. 核心逻辑解析

4.1 机型铭牌表(第 26~891 行)

g_t_sunspec_inv_nameplate_array[SUNSPEC_INV_MAX] 共 14 项,用指定初始化器逐字段赋值,顺序严格等于 E_SUNSPEC_INV_TYPE:

索引机型有功率 (W)视在 (VA)无功 (var)说明
0G3-S55000100004000单相 5kW
1G3-S66000115004800
2G3-S88000115006400
3G3-B55000100004000
4G3-S3.6368040002944
5G3-T4400040003200三相
6G3-T5500050004000
7G3-T6600060004800
8G3-T8800080006400
9G3-T1010000100008000
10G3-T12 VAVE———商用三相
11G3-T15 VAVE———AlphaESS 本体 T15
12G3-T15 汇川 (INO)———汇川版 T15
13G3-T20 汇川 (INO)200002000016000最大机型

除功率参数外,每项还统一带有:normal_operating = SUNSPEC_CAT_B、abnormal_operating = SUNSPEC_CAT_2、supported_control_mode = 0x1FFF(bit0~bit12 全支持)、intentional_island = 0x0003,以及 7 个缩放系数 actpower_scale=0 / factor_scale=-2 / apparentpower_scale=0 / reactpower_scale=0 / volt_scale=-1 / cur_scale=-1 / susceptance_scale=0。

注意 *_set 后缀字段是上位机可写的限值副本(对应地址 40252~40270,RW 权限),与只读的额定值(40228~40251,RO)分开存放。

4.2 地址二分查找读写(第 902~1031 行)

g_t_sunspec_mudbus[] 按 addr 升序排列,因此 sunspec_modbus_data_get/set 用标准二分查找把 O(n) 降到 O(log n)(SUNSPEC_STRU_NUM=783,约 10 次比较):

left = 0; right = stru_size - 1
while(left <= right):
    mid = left + (right - left)/2      // 写成这种形式防 (left+right) 溢出
    if(table[mid].addr == target) -> 命中
    elif(table[mid].addr <  target) left  = mid + 1
    else                            right = mid - 1

读路径 sunspec_modbus_data_get(902):

  1. 命中且 (rw & RO) == RO 才继续,否则返回 0;
  2. scale == SCALE_1(1000) 直接取 *(data_p);否则 l_f16_scale = scale / 1000,再 l_u16_ret = (u32)l_u16_ret * l_f16_scale;
  3. 若 big_endian == LIT_ENDIAN,做 ((v>>8)&0xFF)|((v<<8)&0xFF00) 字节互换;
  4. 未命中或无权读或未初始化时统一返回 0(函数开头 l_u16_ret = 0 的"没找到地址返回0")。

写路径 sunspec_modbus_data_set(957):结构与读对称,权限判据换成 (rw & WO) == WO;命中后先按倍率反算、再按大小端互换,最后 *(data_p) = input_data。

写后的特殊处理(1003~1030 行):源码注释明确写了"个别字段需要在重复写入时做额外处理……否则这函数容易成屎山"。实现方式是一个 switch(addr),把 WSET_ENA_40300 … WSET_ENA_40310(含 WsetEna、WsetMod、Wset 高低字、WsetRvrt 高低字、WsetPct、WsetEnaRvrt、WsetRvrtTms 高低字,见 sunspec_modbus.c:350~360)统一映射为

g_t_sunspec_ctlAC.RvrtTms_refresh_flag = 1;

即:任何有功调度相关寄存器的写入都会重置本文件的回退计时基准,实际倒计时运算延后到 sunspec_active_power_dispatch_refresh() 里做。

4.3 数据初始化 sunspec_modbus_data_init(1043 行)

memset(&g_t_sunspec_base, 0, sizeof)
memcpy(mn, "AlphaESS", sizeof("AlphaESS"))        // 厂商标识
memcpy(sunspec_star, "Suns", sizeof("Suns"))      // "Suns" 魔数
memcpy(ver, EMS_SOFT_VERSION, ...)                // 版本字符串(server.h:138 → VERSION_STRING)
sscanf(inverter_read_system_modle(), "%s", md)    // 机型字符串
memcpy(g_t_sunspec_base_GRID.opt, "Export+Import", ...)
memcpy(g_t_sunspec_base_PV.opt,   "Production", ...)
memset(&g_t_sunspec_run, 0, sizeof)
sunspec_modbus_mppt_percentage_set(10000)         // MPPT 默认 100%
for i in [0, SUNSPEC_STRU_NUM):
    if(table[i].init_type == INIT): *(table[i].data_p) = table[i].default_value

它被 4 个地方调用(都属于"使能 Sunspec / 上电 / 云端下发"路径):data/system_config/system_config.c:762(Modbus 配置落 EEPROM 之后)、midware/interface/interface.c:304(Modbus-RTU 任务入口,仅当 u8_sunspec_en == TRUE)、net/server.c:5172(云端下发 SunspecEN 打开时)、midware/interface/modbus/modbus.c:10217(本机 Modbus 写寄存器改变了 Sunspec 使能或从站地址时)。

4.4 有功功率调度刷新 sunspec_active_power_dispatch_refresh(static,1079 行)

这是本文件唯一的算法主体,分三段:

第 1 段:基准时间重置(1081~1095)

if (RvrtTms_refresh_flag == 1) {
    RvrtTms_refresh_flag = 0;
    if (WsetEna == 1 && WsetEnaRvrt == 1) {
        WsetRvrtRem          = WsetRvrtTms;      // 重置剩余秒数
        RvrtTms_refresh_tick = OSTimeGet();      // 记录基准
        g_t_dispatch_t.t_stsystemcmdinfo.u32CmdGetTick = OSTimeGet();
    } else {
        WsetRvrtRem = 0;
    }
}

第 2 段:回退倒计时(1097~1108)

if (WsetEna == 1 && WsetEnaRvrt == 1 && WsetRvrtRem > 0) {
    u16 second = (OSTimeGet() - RvrtTms_refresh_tick) / 1000;   // 已过秒数
    WsetRvrtRem = (WsetRvrtTms > second) ? (WsetRvrtTms - second) : 0;
}

源码注释说明:工程里没有秒级定时器,所以用 OSTimeGet() 的时间差做"软件倒计时",每次调用重算而不是递减。因此 WsetRvrtRem 的刷新精度取决于本函数被调用的频率(Sunspec 上位机约 3s 一帧)。

第 3 段:调度下发(1110~1148)

if (WsetEna == 1) {
    set_dispatch_enable(EVERGEN_FUNC_START);
    dispatch_set_mode(SUNSPECT_ACTIVE_MODE);
    logic_set_sequencecontrol_mode(DISPATCH_MODE);
    if (WsetMod == 1)  l_s32_ctrl_ac = Wset;                                       // 绝对值模式
    else               l_s32_ctrl_ac = WsetPct * max_actpower / 100;               // 百分比模式
    g_t_dispatch_t.t_stsystemcmdinfo.u16ActivePower = l_s32_ctrl_ac + 32000;        // POSITIVE_VALUE_OFFSET
    g_t_dispatch_t.t_stsystemcmdinfo.u32CmdGetTick  = OSTimeGet();
    g_t_dispatch_t.t_stsystemcmdinfo.u32_runtime    = 30;   // 注释:"Sunspec协议3S调用一次,拍脑袋定为30S"
} else {
    g_t_dispatch_t.t_stsystemcmdinfo.u32_runtime = 0;       // 撤销调度
    sunspec_modbus_mppt_percentage_set(10000);              // PV 开到最大
}

set_dispatch_enable 的关闭分支(EVERGEN_FUNC_STOP)被注释掉了(1144 行),改以 u32_runtime = 0 让指令自然过期。

被注释掉的 1120~1136 行说明:原设计在回退窗口内应改用 WsetRvrt / WsetPctRvrt 参与计算,当前实现已被注释,即"回退值"字段(40304/40305/40307)只被记录、不生效。

4.5 运行数据总刷新 sunspec_modbus_data_refresh(1173 行)

由 Modbus-RTU 任务在每次收到合法 Sunspec 帧后调用,函数内按 Sunspec 模型分组刷新:

(a) 机型与铭牌(1185~1259)

if (s_u8_inv_type != get_inverter_sub()
    && get_inverter_sub() ∈ {SUB_S5,SUB_B5,SUB_S6,SUB_S8,SUB_S3_6,
                             SUB_T4,T5,T6,T8,T10,T12,T15,T20})
{
    s_u8_inv_type = get_inverter_sub();
    switch(...) → l_u8_sunspec_inv_type = SUNSPEC_G3_xxx;
        // SUB_T15 特殊:get_inverter_type()==ALPHAESS_TYPE 取 T15_VAVE,否则 T15_INO
        // SUB_T12 → SUNSPEC_G3_T12_VAVE
    g_t_sunspec_inv_nameplate = g_t_sunspec_inv_nameplate_array[l_u8_sunspec_inv_type];
}

用 static s_u8_inv_type 做边沿检测,只在机型变化时拷贝一次铭牌(SUNSPEC_NAMEPLATE_T 约 100 字节)。

(b) 模型 1 通用数据(1260 行) g_t_sunspec_default.operating_sta = (inverter_get_shutdown_sta() == INV_SHUTDOWN) ? 1 : 0;

(c) 模型 701/702 逆变器状态与并网信息(1273~1352) 运行模式到 Sunspec inv_sta 的映射(源码内附有标准枚举注释):

EMS 运行模式Sunspec inv_sta
cWaitMode / cSelfTest1 (Sleeping)
cCheckMode2 (Starting)
cNormalMode3 (MPPT)
cByPassMode5 (Shutting Down)
cUpdateArm / cFaultMode6 (Fault)
cUPS7 (Standby)
default1

其余字段:inv_alarms = 0xFF(硬编码)、DER_mode = 0x00000003(bit0 电网跟随 + bit1 电网支撑,附标准位定义注释)、inv_reactive_energy_input/output = 0、ambient_temp/cabinet_temp = 0、throttle_source = 0x00000001(bit0 = 最大有功限制)、max_feedin_percent = g_t_logic_info.t_config_data.u8_max_feedin_percent、grid_connect = (g_t_meter.t_grid_info.u8_comm_lost_err == 0) ? 1 : 0、inv_phase_num = u8_inv_phase_num - 1(0 单相/1 裂相/2 三相)、inv_cur = inverter_read_grid_cur(PHASE_ALL)、pf = s8_run_pf_value / 10。

视在/无功功率按厂商分流:

ALPHAESS_TYPE  → g_t_alphaess_comm_info.t_alphaess_run_info.s32_inv_apparent_power / s32_inv_reactive_power
其它(汇川 T20)→ g_t_inv_info.t_run_info.s32_inv_apparentpower_t / s32_inv_reactpower_t

(d) 模型 713 储能电池(1362~1365)

energy_rating     = bms_read_max_system_storage_capacity()
energy_available  = bms_read_current_actual_remaining_available_electricity()
soh               = bms_read_single_soh(0) * 10
storage_sta       = (bms_read_fault_code() == 0) ? 0 : 1

(e) 向控制层回写标志位(1367 行) g_t_logic_info.t_config_data.u8_maxfeedin_offset_flag = g_t_sunspec_ctlAC.WsetEna; —— 把"是否由 Sunspec 接管有功"回写到逻辑层配置。

(f) 有功调度刷新:1369 行调用 sunspec_active_power_dispatch_refresh()。

(g) 模型 203 电表(1371~1381、1439~1449):分别把 GRID_METER 与 PV_METER 的 cur_total / apparent_power_a/b/c / apparent_power 搬进 g_t_sunspec_AC_meter 与 g_t_sunspec_PV_meter;用 static s_u8_meter_grid_find / s_u8_pv_grid_find 做电表发现状态的边沿检测,变化时把电表型号串拷进 g_t_sunspec_base_GRID.mn / g_t_sunspec_base_PV.mn;最后按 meter_read_connect_status() 置 M_EVENT_Missing_Sensor 位。

(h) 模型 714 直流(1384~1437)

DC_Ports_num 累加规则: inverter_get_pv_start_status() != 0 → +1;bms_get_ready_enable() != TRUE → +1
DC_total_power = inverter_read_pv_power(PV_CHANAL_ALL) + bms_read_total_volt()/100
DC_total_cur   = inverter_read_pv_cur(PV_CHANAL_ALL)   + bms_read_total_cur()/100
DCWhinj_total  = l_u32_pv_yeild + energy_read_bat_chgrge()/10
DCWhAbs_total  = energy_read_bat_dischg()/10
group[0] = 电池:PrtTyp=1(电池),id=0,id_str=s_type_splice,DCV/DCA = 总压/总流÷100,
           DCW = inverter_read_bat_power(),DCWhinj/Abs = 充/放电量÷10,tmp = s16_high_temper,
           DCSta = 有故障→3(ERROR);无故障→(工作状态==1);电池不在位→0(OFF)
group[1] = PV  :PrtTyp=0(PV),id=1,id_str="PV",DCV/DCW/DCA 取逆变器 PV 表,
           DCWhinj = energy_read_storage_pv_input()*10,DCWhAbs=0,tmp = 机温,
           DCSta = (parallel_read_system_pv_status()==1)

group[2]("Generic Bidirectional"混合系统端口)整段被注释掉(1452~1464 行),对应 u8_system_mode == HYBIRD_SYSTEM_MODE 的判断也不再参与 DC_Ports_num 计数。

4.6 通信看门狗与 MPPT 辅助接口(1473~1600 行)

5. 对外接口

5.1 本文件导出(声明于 sunspec_business.h)

符号类型调用方
sunspec_modbus_data_init函数data/system_config/system_config.c:762、midware/interface/interface.c:304、net/server.c:5172、midware/interface/modbus/modbus.c:10217
sunspec_modbus_data_refresh函数midware/interface/interface.c:370
sunspec_modbus_data_get函数midware/interface/modbus/modbus.c:8222(读寄存器响应)
sunspec_modbus_data_set函数midware/interface/modbus/modbus.c:8265, 8320(写单/多寄存器)
sunspec_modbus_dischg_get / sunspec_modbus_chg_get函数logic/ac_control.c:1869, 1912
sunspec_modbus_Wset_ena_get函数logic/logic.c:3003
sunspec_modbus_mppt_percentage_get函数logic/logic.c:3004
sunspec_modbus_mppt_percentage_set函数logic/ac_control.c:1883, 1887(本文件内部也调用)
sunspec_modbus_mppt_over_count_add / _get函数logic/ac_control.c:1876, 1882
sunspec_refresh_flag_set函数midware/interface/modbus/modbus.c:8576
sunspec_refresh_flag_get函数midware/interface/interface.c:369、logic/logic.c:3003
sunspec_modbus_max_charge_rat_get / sunspec_modbus_max_discharge_rat_get函数logic/ac_control.c:1862~1865

5.2 本文件定义的全局变量

g_sunspec_comm(SUNSPEC_COMM_T)与 g_t_sunspec_inv_nameplate_array[SUNSPEC_INV_MAX] —— 两者都没有在头文件中 extern 声明,实际是本文件私有的。

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

6. 依赖关系

依赖(本文件 include / 使用)

被依赖(谁用到它)

7. 备注与风险点

  1. 倍率换算存在整数除法缺陷(第 936、990 行):l_f16_scale = g_t_sunspec_mudbus[..].scale / 1000; 两侧都是整型,先做整数除法再赋给 float。当地址表使用 SCALE_0_1(=100) 时 100/1000 == 0,倍率直接变 0。实测 sunspec_modbus.c 中有 3 处使用 SCALE_0_1(165/166/167 行,地址 40115/40116/40117,逆变器 A 相电流与两路电网电压),这三处读出来恒为 0;写入同样恒为 0。SCALE_0_01、SCALE_0_001 一旦被使用也会同样失效。
  2. 机型未落在白名单时铭牌不赋值(第 1185~1198 行):外层 if 只在 get_inverter_sub() 属于 13 个机型之一时才进入并拷贝 g_t_sunspec_inv_nameplate。若上电早期 get_inverter_sub() 仍是 SUB_NULL,或未来新增机型(如 SUB_S5_II、SUB_M10、SUB_S10、SUB_B10,见 inverter.h:332~337)未同步加入清单,则铭牌保持全 0。由于 sunspec_modbus_chg_get() 会被 ac_control.c:1869 直接当作充电限值下限,铭牌为 0 会导致调节参数被压到 0,影响明显。switch 内的 default 分支(1252 行)在此结构下不可达。
  3. 回退(Rvrt)功能未生效(第 1120~1136 行):处理 WsetRvrt/WsetPctRvrt 的逻辑被整段注释,倒计时算完后并未真正回退到回退值。寄存器 40304/40305/40307 只写不用,上位机若依赖 Sunspec 标准回退语义会得到错误行为。
  4. l_u32_pv_yeild 是死变量(第 1177、1401 行):声明并初始化为 0 后再未赋值,DCWhinj_total 里 l_u32_pv_yeild + ... 这项恒为 0,PV 发电量实际只靠电池充电量凑数。
  5. 能量缩放不一致(第 1401 vs 1434 行):group[1].DCWhinj = energy_read_storage_pv_input() * 10,而同一函数内其他能量字段统一是 /10(电池充放、总注入)。乘除方向不同,单位是否对齐需要核对 PV 电量接口的原始单位。
  6. DC_total_power 量纲可疑(第 1398 行): inverter_read_pv_power() + bms_read_total_volt()/100,把一个功率和一个(电压/100)相加,物理意义不成立,像是漏了乘电流。
  7. 有符号到无符号 / 宽到窄截断(第 1371~1381 行):meter_read_current 返回 s16、meter_read_apparent 返回 s32,而目标字段 cur_total 是 u16、apparent_power* 是 s16。馈网工况下负电流会变成大正数,视在功率超过 32767 会截断。
  8. 铭牌表内的疑似笔误:
    • G3-B5 与 G3-S3.6 的 over_excited_pf / under_excited_pf 写成 8(第 235/236、296/297 行),其余机型与它们自身的 _set 版本都是 80,量级差 10 倍。
    • G3-S8 的 specified_over_excited_pf_set 与 specified_under_excited_pf_set 写成 1(第 184/185 行),非 set 版本为 80。
    • G3-T4/T5/T6/T8 的 max_actpower_set 分别写成 8000/8000/12000/12000(第 367、429、491、553 行)而 max_actpower 是 4000/5000/6000/8000,且 T5 出现 max_actpower_set(8000) > max_apparentpower_set(5000) 的不自洽组合。源码在这些行尾带 ---------- 标记,属于已知的临时状态,但仍是运行时可达的超限值。
  9. g_t_logic_info.t_config_data.u8_maxfeedin_offset_flag 被每帧回写(第 1367 行):该字段位于 t_config_data(配置数据)内,若该结构会被持久化到 EEPROM,则 Sunspec 每次写 WsetEna 都可能引发一次掉电存储,需确认其是否在保存清单内。
  10. magic number 集中:30000(通信超时 ms,1573 行)、30(调度运行时长 s,1140 行)、10000(MPPT 100%,1061/1147/1519/1522/1887)、0xFF(inv_alarms,1310)、0x00000003(DER_mode,1312)、0x00000001(throttle_source,1338)、32000(POSITIVE_VALUE_OFFSET,1138)、10(过计数饱和,1538)、/100(电压电流缩放,1398/1399/1408/1410)、*10、/10(能量缩放)。注释里对 30s 的说明是"拍脑袋定为30S"。
  11. sscanf(inverter_read_system_modle(), "%s", g_t_sunspec_base.md)(第 1055 行)没有宽度限制,若 md[32] 对应的机型串异常长会越界;memcpy(g_t_sunspec_base.ver, EMS_SOFT_VERSION, sizeof(EMS_SOFT_VERSION))(1053 行)也依赖版本串不超 ver[16](当前 "V0.11.676" 为 10 字节,安全,但加长后缀后需复核)。
  12. inv_phase_num = u8_inv_phase_num - 1(第 1341 行):u8 型减 1 后赋给 u16 字段,若配置值为 0 会得到 65535。
  13. group[2](混合系统双向端口)整段注释(1452~1464 行),DC_Ports_num 的 HYBIRD_SYSTEM_MODE 计数分支同样被注释(1391~1395 行),混合系统场景下 714 模型端口数可能偏低,属于版本遗留代码。
  14. WSET_ENA_40300 的 switch 只覆盖有功相关地址,源码注释也承认这种"命中后 switch 特判"的写法不可扩展("以后多了建议在结构体中加入函数指针处理"),是后续维护的负担点。