EMS 知识库
首页 / knowledge/代码解析/net

server.c

源文件路径:net/server.c 所属模块:网络应用层(net) 代码行数:7304

1. 文件职责

net/server.c 是 EMS 与 AlphaESS 云端服务器之间的应用层协议实现主体,负责把 TCP/无线收到的整包报文拆帧、校验、按命令字分发,并把云端下发的"配置、安规参数、控制指令、固件"翻译成对本地全局结构体的读写与对底层驱动的调用。

它处在数据流的下行入口:net/ethernet.c 的接收任务调用 handle_tcp_recv_data()(net/server.c:6280)把一包数据交给本文件,本文件再依次分发给 handle_special_cmd()(控制指令)、handle_check_update()(升级检查)、handle_fw_file_from_server()(固件分片)、deal_get_config_data()(配置下发)、deal_get_safety_config_data()(安规参数下发)、parse_meter_data()(无线表数据)等处理函数。

上行方向本文件只负责置位各类响应标志(g_u8DispatchFlag、g_u8SetConfigFlag、g_u8SyncFlag 等,均定义在 net/ems.c:66-70),由 net/ems.c 的发送流程轮询后组包回传,实现"收-处理-置标志-发"的解耦。

此外本文件还承担远程固件升级的完整客户端流程:轮询选择升级目标(load_current_update_data)、校验云端下发的文件信息(set_file_updating_info/check_server_fw_is_updating)、按偏移拼装固件到 SDRAM/Flash(handle_fw_file_from_server)、结束时计算 MD5 并置状态为完成。

2. 关键数据结构与宏

2.1 本文件定义/持有的全局变量

名称类型说明
g_u8_fw_sdram1[SDRAM_3M]u8[]固件下载区 1,定位 SDRAM_FIRM1_START_ADDR(net/server.c:69),承载 EMS/INV1/INV3/BMS/Meter/EVC/AFCI
g_u8_fw_sdram2[SDRAM_3M]u8[]固件下载区 2,定位 SDRAM_FIRM2_START_ADDR(net/server.c:70),仅承载 INV2
g_au8NetRecvPack[SDRAM_5K]u8[]整包接收缓冲,定位 SDRAM_NET_RECV_PACK_BUFF_ADDR(net/server.c:71)
g_au8FWBuf[SDRAM_2K]u8[]通用 JSON 字段临时缓冲(json_get_value 输出目标),全文件高频复用
g_au8FWBin[SDRAM_1K]u8[]HEX→BIN 转换结果缓冲
s_s8_updgrademd5[33]static char[]本地 EMS 升级文件 MD5,初值 32 个 '0'(net/server.c:75),由特殊指令 Description 字段刷新,用于与云端 MD5 比对
g_u8_can_send_done_flagu8CAN 自定义帧发送完成标志(net/server.c:77)
s_can_log_ctrlCAN_LOG_CTRL_TCAN 日志采集控制块(net/server.c:80)
s_can_log_raw_buffers[MAX_CAN_LOG_ID_COUNT]u8 *[]每个 CAN ID 独立的原始数据缓存区指针(net/server.c:83)
g_u8_bat_log_flagextern u8电池日志上传标志(net/server.c:85 引入,对应分支已 #if 0)

2.2 宏常量

名称类型说明
PARAM2_FORMAT宏"Parameter2\":\"%" SCNu8 "-%" SCNu16 "-%" SCNu16 "-%" SCNu32 "\""(net/server.c:65),智能化控制指令 Parameter2 的 sscanf 格式串:充电截止 SOC-超额起时-超额止时-PV 最大功率
DEBUG / DEBUG_TCP_CLIENT_STATE / DEBUG_TCP_BMS_LOG外部宏调试开关,控制 printf 与 ShowData 是否编译
MAX_CNT_REQ_FILE_ERROR宏(net/ems.h:55)(5),请求文件信息/接收文件连续失败上限
APP_MIN_SIZE / APP_MAX_SIZE宏(net/ems_download.h:32-33)EMS 固件合法长度区间 100KB ~ (CHIP_FLASH_SIZE-32)KB
LEN_REQ_FILE宏(net/server.h:143)(1024),单包固件字节数上限
SENCOND_1S / SENCOND_5S / SENCOND_10S / SENCOND_15S宏uCOS-II tick 数常量,用于超时与周期判定
其余 FIELD_NAME_* / SPECIAL_CMD_* / FILE_MASK_* / VER_*宏全部来自 server.h,详见 server.h.md

2.3 结构体与枚举(本文件主要使用的)

名称类型说明
STUpdateTab结构体单设备升级上下文,存于 g_stEMSInfo.stUpt[UPDT_MAX];g_stEMSInfo.pstCurrUpt 指向"当前处理点"
STEMSTab结构体全局 EMS 状态(net/ems.h:347),本文件读写其 u8UpdtStatusCurr、u8UpdtDevTypeCurr、pstCurrUpt、stUpt[]、u8_forceupdt_flag、bIsLogin、u64TickHeartRcv、u8_set_safety_config_flag 等成员
json_simple_map_t结构体表驱动映射项 {key, target, size},本文件定义 g_json_simple_map[](net/server.c:3942),约 90 条
json_time_map_t结构体时间映射项 {key, p_hours, p_minutes},本文件定义 g_json_time_map[](net/server.c:4027),8 条
ENUpdateType枚举升级设备索引,直接作 stUpt[] 下标
ENCheckOrderState枚举升级轮询状态机状态
ENUpdtStatus枚举当前升级状态(NONE/IDLE/UPDATING/OVER)
RESUME_BUFFER_T结构体断点续传指令参数(年/月/日/时/分/秒/长度)
DISPATCH_CMD_T结构体远程调度参数(使能、8 段功率点、运行时长)
SMART_CONTROL_TABLE_T结构体智能化控制参数
BATTERY_SET_SN_TABLE_T结构体单包 SN 替换表(源 SN / 目标 SN / 状态)
BMS_ACTIVATION_TIME_TABLE_T结构体电池激活时间(年/月/日)
METER_RUN_TABLE_T结构体电表运行数据(三相电压/电流/功率/电能/频率/功率因数/SN/软硬件版本)
CAN_LOG_CTRL_T结构体CAN 日志采集控制块(该类型在本工程中未找到定义,见第 7 节)
CAN_LOG_RAW_ITEM_T结构体CAN 日志原始条目(同样未找到定义)
METER_SAMPLING / GRID_METER / PV_METER / PHASE_A/B/C/T枚举电表与相位索引

3. 函数清单

函数名作用域说明
set_updt_status_current全局写 g_stEMSInfo.u8UpdtStatusCurr(net/server.c:102)
get_updt_status_current全局读当前升级状态(:118)
check_need_response_updt_devcie全局按 INV1→INV2→INV3→BMS→Meter→EMS→EVC→AFCI 顺序取一个待回强制升级响应的设备并清标记(:134)
get_current_point_updt_file_info全局读 pstCurrUpt->bIsGotUpdtFileInfo,空指针返回 FALSE(:172)
set_current_point_updt_file_info全局写 pstCurrUpt->bIsGotUpdtFileInfo(:198)
get_current_point_flag_force_updt全局读 pstCurrUpt->bIsUpdateForce(:224)
is_goto_get_update_file_status全局"已取到文件信息 且 允许强制升级" → 可进入取固件阶段(:250)
get_current_point_file_mask全局返回当前升级点文件掩码字符串(static 缓冲,10 字节)(:276)
get_current_point_file_len_updt全局返回固件总长(:306)
get_current_point_offset_file_updt全局返回已下载偏移(:332)
get_curr_updt_point_is_null全局pstCurrUpt == NULL 判定(:358)
get_current_point_version_force_updt全局返回目标软件版本,长度被钳到 MAX_LEN_SOFT_VER-1(:381)
get_system_sn全局返回 16 字节系统 SN(:414)
get_ems_hard_type全局恒返回 FW_TYPE_EMS3_6(:435)
get_ems_hard_version全局转发 get_board_hw_ver()(:451)
get_local_md5_ems_upt_file全局返回 s_s8_updgrademd5(:467)
get_updt_dev_type_curr全局返回 g_stEMSInfo.u8UpdtDevTypeCurr(:483)
is_goto_update_check_status全局"未取到文件信息 且 允许强制升级" → 需先请求文件信息(:499)
check_updt_force_is_none全局8 个设备的 get_flag_force_update() 全为 0 才返回 TRUE(:529)
check_force_upgradable_flag全局除 EMS 外 7 个设备的 Get_Status_Is_Updated_From_Server() 全为 0 才返回 TRUE(:559)
load_current_update_data_setCurrUpt全局单步辅助:若该设备有强制升级标志且当前无并发升级则挂接 pstCurrUpt,否则推进状态(:577)
load_current_update_data全局升级轮询状态机主体,每 1 秒推进一步(:610)
set_sys_rtc全局解析 Parameter1(Unix 秒) + Parameter3(时区偏移秒) → 写 RTC(:743)
handle_server_dispatch_command全局解析远程调度(使能/8 点功率/最短 90s 运行时长),置事件标志并唤醒 logic 任务(:790)
handle_server_parameter_setstaticExtra 指令的二级参数表:index 高字节选组、低字节选点,共 6 组约 60 个参数点(:849)
handle_server_extra_command全局解析 Extra 指令,处理 index=65534(改 SN)、65535(改 MAC)、65533(电池 SN 替换),其余转 handle_server_parameter_set(:1556)
handle_server_power_factor_command全局解析 PowerFactor:flag 0=设 PF,1/2=清零无功,3=设固定无功(值除以 100)(:1666)
handle_server_evc_charge_enable_command全局解析 EVChargeEN:按桩号/id 设置单桩或 EVC-II 充电使能(:1754)
handle_server_debug_run_info_command全局解析 DebugRunInfo:上报使能、总时长、间隔(秒→毫秒,最小 10s)(:1798)
handle_server_soc_calibration_enable_command全局解析 SetSOCCalibrationg:置用户校准使能并触发一次立即校准(:1844)
set_smart_control_parameter全局把 SMART_CONTROL_TABLE_T 落到 g_t_logic_info.t_config_data.t_smart_control_para 并投递 Flash 保存任务(:1870)
handle_server_smart_dispatch_command全局解析 SmartDispatch:Parameter2 按 PARAM2_FORMAT 解 4 个值;任一参数缺失即关闭智能化控制(:1891)
handle_server_energy_data_enable_command全局解析 SendEnergyDataEN,变化时置电表配置保存标志(:1964)
handle_special_icdcupdate_cmd全局处理 ICDCUpdate/ExpandICDCUpdate:用 SN+地址+端口+加密位算 MD5 与云端校验,通过后落盘并 net_init()(:1993)
handle_server_sys_self_test_command全局解析 SYSSelfTest 使能(:2129)
handle_server_set_bms_activate_time_command全局解析 SetBMSActivationTime:按电池 SN 匹配后解析 YYYY-MM-DD 写入对应 BMS 激活时间(:2156)
handle_server_set_force_charge_command全局解析 SetChargeEN:写 Force_Charge_user 与充电截止 SOC(:2207)
handle_server_sys_shutdown_command全局解析 SYSShutDownEN:写停机使能与停机天数(0→7),变化时置逆变器配置保存标志(:2239)
handle_server_upload_safety_config全局解析 UploadSafetyConfig:1=查询(置发送标志),2=设置(置设置标志)(:2285)
system_hard_reset全局调用 set_ems_reset() 执行硬复位(:2319)
get_special_cmd_id_code全局把指令流水号存入 g_as8SpecialCmdId,长度 >16 直接丢弃(:2335)
handle_special_cmd_for_dev全局单设备升级指令解析:从 URL 中 uploads/app_update/ 后的 -版本号- 片段提取版本,设置强制升级标志/文件掩码/MD5/升级状态(:2346)
set_upload_interval全局目前仅支持 CMD_SEND_BMS_SELF_INFO(0xA3),写 u16_rpt_interval_0xA3 并落盘(:2485)
handle_set_upload_interval全局解析 UploadInterval 的 msg_id + 间隔,成功则置响应标志(:2512)
handle_set_bat_brand全局解析 SetBatBrand:电池 ID 需 <13,调用 send_battery_set_brand_cmd() 下发(:2551)
handle_special_cmd全局0x08 特殊指令总分发:JSON 校验后按 Command 字符串匹配约 28 条指令(:2605)
check_server_fw_is_updating_for_dev全局校验文件信息一致性(掩码/FWType/硬件版本/MD5),成功→UPDT_STS_UPDATING,失败累计 5 次→清零并置 web_error(:3222)
check_server_fw_is_updating全局按设备类型取"本地特征值"(掩码、厂商型号、硬件版本)后转调上一函数(:3275)
set_file_updating_info_for_dev全局解析并填充单设备 STUpdateTab(掩码/长度/MD5/软硬件版本/文件名/FWType/Platform),带合法性校验(:3318)
set_file_updating_info全局从 UpdateFileMask 判定设备类型并转调上一函数(:3435)
set_current_point_flag_force_updt全局写 pstCurrUpt->bIsUpdateForce(:3500)
handle_check_update全局0x20 响应处理:Success→填充文件信息并 OSSemPost(g_pUpdateSem);Error→累计 5 次后清空升级表并置 web_error(:3529)
increase_current_offset_file_updt全局pstCurrUpt->u32OffsetFile += len(:3568)
save_fw_to_sdram全局把 ≤1024 字节固件补齐 0xFF 后写入 SDRAM 下载区(:3583)
save_fw_to_flash全局同上,写入 Flash(仅 EMS 使用)(:3607)
save_fw_to_spi_flash全局按设备类型分派:EMS→Flash,INV2→g_u8_fw_sdram2,其余→g_u8_fw_sdram1(:3642)
handle_fw_file_from_server全局0x21 固件分片处理:逐项校验掩码→偏移→FWType→长度,HEX 转 BIN 落盘,末尾触发 MD5 计算与状态归位(:3675)
store_system_set_data全局空壳函数,声明 u8 res; 未初始化即 return res;(:3910)
parse_bool_fieldstatic解析 "KEY":true/false,返回 1/0,键不存在返回 -1(:4042)
json_store_by_sizestatic按 size(1/2/4) 把 u32 写入目标地址,避免表驱动写坏内存(:4059)
deal_get_config_data全局配置下发总解析(约 300 项),含 2 张 const 表 + 若干 sscanf 数组块(EV1/EVC1..6/TIMEChaDis1..28/TIMEfeedin1..6/PeShValue/CsipAus/DryTime1-2/InstallDate 等),末尾统一投递 15 个 Flash 保存任务(:4082)
deal_get_safety_config_data全局安规参数下发解析(97 项),先校验 SN,再逐项 parse_fixed/atoi 后调用 inverter_set_* 系列接口(:5219)
parse_meter_data全局解析无线电表一帧 35 字段 CSV,填充电网/光伏电表运行结构,含取反与 SN 变更上报(:6108)
handle_tcp_recv_data全局报文接收总入口:收 7 字节头→取长度→收正文+CRC→校验→按命令字分发(:6280)
handle_server_can_send_command全局解析 CanSend 的 Parameter1(`ID:DATA:TYPE...),逐帧入队并启动发送(:7132`)
start_can_log_capture全局初始化 CAN 日志采集控制块、按 ID 分配 SDRAM 缓存区并启动采集(:7219)
is_can_log_capturing全局返回 s_can_log_ctrl.active != 0(:7300)

4. 核心逻辑解析

4.1 报文帧格式与接收(handle_tcp_recv_data,:6280)

帧格式(7 字节头 + 正文 + 2 字节 CRC):

偏移长度含义
01设备类型,发送侧写 EMS = 0x01(net/ems.h:68,见 net/ems.c:2913)
11命令方向,REQUEST_COMM=0x01 / RESPONSE_COMM=0x02(net/ems.h:88-89)
21命令字 ENCmdType,分发依据
3-64正文长度,大端:`(head[3]<<24)(head[4]<<16)(head[5]<<8)head[6](:6315);发送侧只填 [5][6],[3][4] 固定 0(net/ems.c:2917-2921`)
7N正文,JSON 文本(buf + 7 被各处理函数直接当作 JSON 起点)
7+N2校验码,高字节在前:GetCrcCode_Modbus(pack, len-2)(Modbus CRC16)(:6405-6407)

接收流程:

  1. 清零 g_au8NetRecvPack(5KB,:6290)。
  2. 按 type 分两条完全对称的分支:type == W5500 走 recv(SOCK_TCP_CLIENT, ...)(:6298),type == WIRELESS 走 wireless_recv(...)(:6353)。两者都是"循环收头直到满 7 字节或超 1 秒"(W5500 正文再超 1 秒、无线正文超 5 秒),未收满直接 return(丢帧,:6311、:6337)。
  3. 长度字段做上限保护:l_u32DataLen > sizeof(g_au8NetRecvPack) - 9 时钳到该值(:6316),防止写越界。
  4. l_u32PackLen = 7 + dataLen + 2(:6340),并在 (0, sizeof(g_au8NetRecvPack)] 区间内才继续(:6403)。
  5. CRC 比对;不匹配仅打印调试信息,不回复(:6407、:7121-7127)。
  6. 记录端口 g_stNetSysInfo.u16_skip_port(:6409);若服务器为私有加密通道且非 APP 连接,对正文做 decrypt() 原地解密(:6410-6416,长度 packLen-9)。
  7. 若正文含 "Status":"LoginError" → bIsLogin = FALSE(:6418-6425)。
  8. 无线上行未登录时不处理非 0xA4/0xA5 命令(:6427-6433),保证无线电表数据在断云时仍可用。
  9. switch(pack[2]) 按命令字分发(:6435)。

关键命令字与动作(:6435-7119):

命令字名称动作
0x00CMD_HEART_BEAT收到 Success 则刷新 u64TickHeartRcv、清零 u16HeartRstCnt
0x01 / 0x02CMD_REGISTER / CMD_LOGIN注册无动作;登录成功置 bIsLogin=TRUE、打开 3 个配置请求标志、把 3 个请求 tick 对齐到 5s/10s/15s 边界、time_ticksenddata = get_rtc_tick()-240,并投递事件日志;失败清登录
0x03 / 0x14 / 0x60CMD_GET_CONFIG_INFO / ..._EXTEND1 / ..._EXTEND2deal_get_config_data(buf+7, 对应 type),随后清对应请求标志
0x04 / 0x15 / 0x61CMD_SEND_CONFIG_INFO / ..._EXTEND1 / ..._EXTEND2置 APP 配置回传成功标志
0x42 / 0x4CCMD_SET_CONFIG / ..._EXTEND1APP 直连配置:清登录、置 u8_app_connect_flag、deal_get_config_data(),置 u8_set_cfg
0x43 / 0x4D / 0x44 / 0x45 / 0x4E各 GET_* / SEND_RUN_INFO*置 APP 连接标志与对应数据请求标志
0x08CMD_SPECIAL_INTERFACEhandle_special_cmd(g_au8NetRecvPack, l_u32PackLen)
0x20CMD_CHECK_UPDATEhandle_check_update()
0x21CMD_REQUEST_FILE_UPDATE先刷新心跳 tick,再 handle_fw_file_from_server()
0x0CCMD_RESUME_INTERFACE清断点续传缓冲与计数,状态置 RESUME_STS_Idle
0x0FCMD_UPDATE_ERROR_LOGset_error_log_data_send_ok_true()
0x09CMD_INFO_INTERFACE从 get_info_status() 中取出最低置位的一位,clear_info_status(~(1<<bit)) 后按位做副作用(INV_UPGRADE_LOG→清升级状态;SERVER_SWITCH→net_init()),break 只处理一位
0x1D / 0x10 / 0x62 / 0x63数据上报响应刷新心跳 tick;0x62 另置 AFCI 日志发送状态
0x4A / 0x57CMD_SET_SAFETY_CONFIG_INFO / CMD_SET_SAFETY_CONFIGdeal_get_safety_config_data(),清设置标志并记录 tick;0x57 另置 APP 标志
0x4B / 0x58 / 0x47安规查询清发送标志 / 置 APP 查询标志
0xA4CMD_RXD_METER_DATAparse_meter_data(...,"grid meter",GRID_METER);若 PV 检测标志为真再解析 "pv meter",否则清 PV 电表数据
0xA5CMD_RXD_WIFI_INFOwireless_status_refresh(buf+7)
0x56(INOVANCE_TYPE 条件编译)CMD_SEND_FAULT_RECORD_DATA成功→set_fault_scord_status(),失败→fault_scord_init()
0x18CMD_SEND_BURNIN_DATAaging_mode_data_sync()
0xA3 / 0x36 / 0x11 / 0x1B / 0x12各数据上报 ACK置对应标志或仅打印
其它—default: 忽略

4.2 特殊指令分发(handle_special_cmd,:2605)

入口先校验:buf == NULL || !len 直接返回(:2612),然后 json_check(buf+7, len-7-2)(:2615)确保正文是合法 JSON,再从 buf+7 取 "Command" 字段(:2621)做字符串比较分发(约 28 条分支)。只有 SetConfig / UploadInterval / SetBatBrand / 智能化等少数分支不写 g_as8SpecialCmdId,其余分支都会先 get_special_cmd_id_code(buf+7,...) 记录 CmdIndex 供响应回填。

各分支动作要点:

4.3 扩展参数表(handle_server_parameter_set,:849)

index 采用高字节分组 + 低字节索引编码,l_s32_value = atoi(Parameter2):

组(index>>8)参数点说明
0x002..0x2A通用配置:安规类型(2)、逆变器类型(3,<INVERTER_MAX)、服务器临时切换(4,`"切换位\时长",时长 1..250)、OEM 标志(5,仅允许白名单内 7 个值,改后强制重新登录)、备份盒(6、0x0d)、远程升级禁用位(7,0=放开,1=单次放开)、并机参数(0x24-0x28)、电池加热 SOC/温升(0x29/0x2A)、运行模式(0x21,>2 归一为 0)等。**变化时统一置 g_t_config_save_flag.u8_set_xxx_config_flag`** 通知落盘任务。
0x080..6逆变器电网曲线、阴影扫描、孤岛检测使能、过欠压/过欠频降载、DC 两倍允充功率、意大利防逆流
0x090..0x0C清零类:电能清零、电表清零、恢复出厂、EVC 电能清零、三相不平衡、IgFollow、隔离限值、断点存储复位、MID 电表清零、汇川故障录波使能
0x0A0..2电表相序调整标记(电网表/光伏表/三相表)
0x0B0..9安规值:PF 值、固定无功、调节率、4 档 GFCI 阈值、N-PE 短路、GFCI 检测使能、GFCI 屏蔽
0x0C0..3离网切断/唤醒 SOC(限 5..90,且唤醒 SOC 至少比切断 SOC 大 5),及"范围内零"标记
0x100,2,3重启类:0=重启 INV、1=重启 BMS、2=复位 DSP EEPROM、4=清故障、5=重启 WIFI;2=N-N 短路;3=AFCI 自检清除
0x110..11Sunspec/功能使能与汇川寄存器查询(senergys_query_inv_register)

handle_server_extra_command(:1556)在调用它之前先拦截三个特殊 index:

4.4 远程升级状态机(load_current_update_data,:610)

由 net/ems.c 升级流程周期调用(1 秒一步:OSTimeGet() - s_u32TickLoad > SENCOND_1S,:619),并且当前状态 ≤ UPDT_STS_IDLE 才推进(:615,即 UPDATING/OVER 期间冻结)。另含 tick 回绕保护:s_u32TickLoad > OSTimeGet() 时重新对齐(:726)。

状态迁移链:CHECK_NO → CHECK_EMS → CHECK_BMS → CHECK_INV1 → CHECK_INV2 → CHECK_INV3 → CHECK_METER → CHECK_EVC → CHECK_AFCI → CHECK_NO。

load_current_update_data_setCurrUpt 的判定逻辑(:577):

if (get_flag_force_update(currType)) {
    if (check_force_upgradable_flag() == TRUE)      // 当前没有设备正在从服务端升级
        g_stEMSInfo.pstCurrUpt = &(g_stEMSInfo.stUpt[currType]);
    else {
        set_flag_force_update(currType, FALSE);      // 有并发升级,放弃该设备
        if (currType == UPDT_INV1 && Get_Status_Is_Updated_From_Server(UPDT_INV2))
            set_flag_force_update(UPDT_INV2, FALSE);
    }
} else
    *state = nextCheck;                              // 无任务,推进到下一设备

4.5 升级文件信息校验与下载(:2346、:3222、:3318、:3435、:3529、:3675)

指令解析(handle_special_cmd_for_dev,:2346): 从 Parameter2(INV2 例外地用 Parameter3)中 strstr 定位 mark(如 "EMS"、"uploads/app_update/"),随后找两个 '-' 之间的片段即为版本号,长度须 ≤ MAX_LEN_SOFT_VER-1;非 EVC 设备还要求 URL 长度 > "uploads/app_update/" 长度。满足后: set_status_update_file_info(type,FALSE) → set_flag_force_update(type, isForceUpd) → set_updt_file_mask() → set_ver_from_server() → set_updt_status_current(UPDT_STS_IDLE) → 清 s_s8_updgrademd5 并从 "Description" 取 32 位 MD5(INV2 除外)。Meter 分支还会按 URL 里是否含 "Meter" 区分 FILE_MASK_METER(并取 Parameter3 设升级对象)与 FILE_MASK_BB(固定电网表)。EVC 分支取 Parameter3 设 set_evc_upgrad_flag()。

文件信息校验(set_file_updating_info_for_dev,:3318 + check_server_fw_is_updating_for_dev,:3222): 逐个解析 UpdateFileMask / FileLen / FileMD5Code / SoftVer / HardVer / FileName / FWType(BMS 另解析 "Platform" → UPGRADE_GD32|UPGRADE_NXP),并用局部 test 记录首个失败码(1..7)。长度必须落在 (sizeMin, sizeMax) 开区间内(:3326),MD5 必须恰为 32 字符(:3338)。全部通过才 check_server_fw_is_updating()。

check_server_fw_is_updating_for_dev 的 4 项一致性判定(:3227-3230):

strstr(FileMask, 期望掩码)  &&  strstr(来源厂商串, 本地厂商型号)
&& (硬件版本串为 NULL 或 strstr(硬版本, 期望硬版本))
&& (type == UPDT_INV2 || memcmp(MD5, get_local_md5_ems_upt_file(), 32) == 0)

匹配成功 → 清零静态重试计数 s_u8_upgrade_cnt、set_status_update_file_info(type, TRUE)、EMS 另存 save_ems_server_update_info()、clear_curr_offset_file_request(type)、状态置 UPDT_STS_UPDATING(:3231-3237)。失败 → 计数达到 MAX_CNT_REQ_FILE_ERROR(5) 时清空对应 STUpdateTab(INV1/INV2 一起清)、状态置 UPDT_STS_NONE、g_t_upgrade_run_info.u16_upgrade_error.BIT.web_error = 1(:3243-3253)。

设备 → 本地特征值的映射在 check_server_fw_is_updating(:3275):EMS 用 get_ems_hard_type()/get_ems_hard_version(),逆变器三路用 inverter_read_inv_type()/inverter_read_hw_ver(),BMS 用 bms_read_battery_model()(硬版本传 NULL),Meter 用 get_Meter_Updt_File_Mask()/meter_get_meter_modle(get_meter_updt_object()),EVC 按 get_evc_enable_flag() 二选一型号接口,AFCI 用 get_afci_type()/get_afci_hard_version()。

检查响应(handle_check_update,:3529):strstr(buf+7,"Success") → 清错误计数、set_file_updating_info(buf,len)、OSSemPost(g_pUpdateSem) 唤醒等待下载的升级任务(配合 net/ems.c:4686 的 OSSemPend(g_pUpdateSem, 3000, &err))。"Error" → 静态计数 s_s32CntReqFileInfoErr 超 5 次则清空整张 stUpt[]、状态置 UPDT_STS_IDLE、置 web_error。

分片接收(handle_fw_file_from_server,:3675):五级门禁依次校验 ① UpdateFileMask 存在且与 get_current_point_file_mask() 前 8 字节 memcmp 相等; ② FileOffset == get_current_point_offset_file_updt(); ③ FWType 与本地厂商型号匹配(按掩码判定 7 种设备,:3705-3711); ④ FileLen ≤ LEN_REQ_FILE(1024); ⑤ FileData 存在。 全部通过后:hex_ascii_to_bin(FileData, 2*lenPck, g_au8FWBin) → save_fw_to_spi_flash()。随后判断 offset + lenPck >= 总长:

落盘分派(save_fw_to_spi_flash,:3642):UPDT_EMS → save_fw_to_flash()(写 Flash);UPDT_INV2 → save_fw_to_sdram(..., g_u8_fw_sdram2);其余 6 类 → save_fw_to_sdram(..., g_u8_fw_sdram1)。save_fw_to_sdram/save_fw_to_flash(:3583/:3607)只在 Len < 1024(补齐 0xFF 后按 1024 写)或 Len == 1024 时动作,其它长度静默丢弃。

4.6 配置下发解析(deal_get_config_data,:4082)

这是全文件最大的函数之一(:4082-5206,约 1120 行),针对约 300 个 JSON 字段。结构上有 5 种解析模式:

  1. 前置校验(:4107-4126):解析 "SN" 与本地 15 字节比对,不一致置 l_u8SNErrorFlag;解析 "ZipCode" 判空。仅当 type == CMD_SET_CONFIG 或 CMD_SET_CONFIG_EXTEND1(APP 直连)时允许 ZipCode 为空,否则直接 return 不处理任何配置。
  2. const 表驱动:
    • g_json_simple_map[](:3942-4025,约 90 项)——json_get_value + atoi + json_store_by_size() 按宽度写入,覆盖 SetMode、SetPhase、干节点 1/2 的 30 余个字段、GridPowerLimit、PapInvLimit、SOC 校准参数、各使能位等;
    • g_json_time_map[](:4027-4037,8 项)——输入为"当日分钟数",< 1440 时拆成 hours = v/60、minutes = v%60(:4233-4247)。
    • parse_bool_field()(:4042)处理 true/false 形态的布尔字段(Generator、BackUpBox、GridCharge、CtrDis)。
  3. sscanf 数组块(固定长度 CSV,起点用 strstr 定位):
    • "EV1" 10 项(:4296):单桩配置,含两组时段的分钟数→时分换算,末位校正(相数 0→3,最大电流 0→160);
    • "EVC%d"(i=1..MAX_EVC_II_NUM)20 项(:4344):EVC-II 四路配置,三组时段各做分钟→时分换算,并为相数/电流做缺省回填(:4385-4392);
    • "TIMEChaDis%d" 1..14 与 15..28 各 10 项(:4406、:4433):两套充放电时段表,分别落 t_config_extend1_data/t_config_extend2_data,并同步到运行副本 g_t_time_control_run;含 3 个缺省回填公式——放电功率为 0 取额定放电功率、放电截止 SOC 不得低于 UPS 保留 SOC、充电电网功率为 0 取额定充电功率;
    • "TIMEfeedin%d" 1..6 共 5 项(:4462):强制馈电时段;
    • "DryTime1"/"DryTime2" 各 4 项(:4831/:4843):干节点时段(分钟数);
    • "PeShValue" 11 项(:4992):峰谷电价与 4 组峰/谷时段;
    • "CsipAus" 15 项(:5135):南澳 CSIP 的 DER 控制参数(导出限值、斜坡、持续时间等),带显式 (u8)(s32)(u16)(u32) 宽度转换。
  4. 字符串切割块:"BMSActivationtime" 12 组 YYYY-MM-DD(:4908,按 , 递进切分,长度不为 10 时回填 1/1/1);"SOCCALTime" 单个日期(:4974);"WifiPWD"(:4884,需系统功能位允许,空串回填 AT_DEFAULT_PWD,变化时 net_init());"EVCGunLockEN" 按 bit 拆到 4 路枪锁(:4332);"FeedinK1..K4" 循环(:5081,值 ≤100 才写);"InstallDate"(:5103)。
  5. 带副作用/带范围检查的单字段:网络类(IPMethod/LocalIP/SubnetMask/Gateway → 置 u8_set_net_para_flag)、Modbus(ModbusAddress/ModbusBaudrate → 波特率变化立即 uart3_init(9600/115200/256000))、CT 类(CTRate/GridMeterCTE/PVMeterCTRate/PVMeterCTE/InstallMeterOption/Meter_CT_Select)、相序取反逐相位(GridMeterN_L1..L3、PVMeterN_L1..L3)、安规相关特殊字段(PapInvEN 仅澳规 AS4777 可设、OutsideNpeEN 仅 NC2022、GfciEN/PrepayMeterEN 仅 NRS097_2_1、AcOutBat 仅 TOR_ERZEUGER,后者在安规解析里)、以及大量直接调用驱动接口的字段(inverter_set_three_unbalance_mode、bms_set_self_lock_enable、meter_set_ct_sampling、epo_set_config_enable、modbus_sunspec_en_set+sunspec_modbus_data_init、meter_set_chnt_wireless_module_regional_freq_bands 等)。

收尾(:5179-5206):置电网表检测标志;按系统模式(AC/HYBRID)决定 PV 表检测标志;GCRatePercent == 0 时补 80;最后连续投递 15 个 Flash 保存任务(SYSTEM_BASE / LOGIC / METER / BATTERY / INVERTER / DRY_NODE / EVC_CONFIG / EVC_SETTING / EVC_II_SETTING1..4 / LOGIC_CONFIG2..4 / MODBUS_CONFIG),由 save_job_post() 通知断电续传存储任务落盘。

4.7 安规参数解析(deal_get_safety_config_data,:5219)

结构与上一函数类似但全部走 inverter_set_* 接口、不写 Flash(安规参数由逆变器侧持久化)。:5219-6100 共处理 97 个字段。

4.8 无线电表数据解析(parse_meter_data,:6108)

json_get_value(buf, "grid meter" | "pv meter", str) 取出一条 CSV 字符串,再用一条 35 字段的 sscanf 格式串(:6143)解析:

%d,            // 链路状态
%16[^,],%19[^,],%8[^,],%8[^,]   // 型号、SN、硬件版本、软件版本
%u,%u,%u,%u     // 刷新周期、总正向电能、总反向电能、Q(L)、Q(C)
%u,%u           // 总有功、总无功
%hu,%hu,%hu     // 三相电压
%hd,%hd,%hd     // 三相电流
%d,%d,%d        // 三相有功
%d,%d,%d        // 三相无功
%hu             // 频率
%hd,%hd,%hd,%hd // 总/三相功率因数
%u,%u,%u,%u,%u,%u // 三相正/反向电能

解析后若该表配置了整体取反(u8_grid_meter_negate/u8_pv_meter_negate,:6185),用局部宏 NEGATE(x) (x = -(x)) 对三相电流、有功、无功、功率因数取反,并交换正反向电能(:6196-6199)。

链路在线(meter_link_sta == 1)时:SN 变化或首次发现 → Set_Setup_Config_Status(FALSE) + meter_set_saveConfigFlag() 触发上报;刷新软/硬件版本与型号字符串;置 u8_found = TRUE、清 u8_comm_lost_err、记录 u32_get_data_last_time = OSTimeGet()。离线则调 wireless_meter_handle_lost()。函数开头固定置 u8_wireless_meter_flag = 1 与 meter_set_meter_or_ct(meter, METER_SAMPLING)。

4.9 CAN 自定义帧与 CAN 日志采集(:7132、:7219、:7300)

自定义 CAN 帧下发(handle_server_can_send_command,:7132):先 can_send_queue_clear();解析 Parameter1(形如 ID:DATA:TYPE|ID:DATA:TYPE|...),用 strtok_r 按 | 切帧,每帧 sscanf("%[^:]:%[^:]:%s") 得 ID(HEX 字符串)、DATA(必须 16 个 HEX 字符 = 8 字节)、TYPE;数据逐 2 字符 strtoul 转字节后 can_send_queue_add(can_id, data, type);全部解析完若队列非空则 can_send_queue_start()。相关接口声明在 midware/bms/bms.h:1240-1242。

CAN 日志采集(start_can_log_capture,:7219):参数三重校验(1 ≤ id_count ≤ MAX_CAN_LOG_ID_COUNT、1 ≤ target_count ≤ MAX_CAN_LOG_COUNT);首次调用按 SDRAM_CAN_LOG_BUF_ADDR 基址为每个 ID 划分 MAX_CAN_LOG_COUNT * sizeof(CAN_LOG_RAW_ITEM_T) 的缓冲区并存入 s_can_log_raw_buffers[];若已有任务(active != 0)强制终止并清空全部缓存区;随后重置控制块(active=1、total_count 取云端条数、start_tick=OSTimeGet()、current_pack_seq=1、各 ID 计数清零)、拷贝 cmd_id(长度须 <32)。is_can_log_capturing() 供 net/ems.c:7016 等采集与上报流程查询状态。

4.10 与 uCOS-II 任务/信号量/时钟的配合

机制位置作用
g_pUpdateSem(OSSemCreate(0),midware/ethernt/ethernet.c:996)net/server.c:3544、:3829收到文件信息成功 / 收到一个文件分片后 OSSemPost 唤醒升级任务;升级任务在 net/ems.c:4686 以 3000 tick 超时 OSSemPend
OSTimeGet():619、:6295 等所有超时与周期判定的唯一时基(tick)
OSTimeDlyResume(TASK_LOGIC_CONTROL_PRIO) + OSFlagPost(g_t_fast_dispatch_flag, POST_PEND_FAST_ENABLE, ...):838-839收到 Dispatch 指令后立刻打断 logic 任务的延时,使其立即按新调度参数运行
OSTimeDlyHMSM(0,0,1,0):1593、:1612改 SN/MAC 后等 1 秒让 Flash 写完再硬复位
save_job_post(page):1592、:1877、:2491、:5190-5205向断电续传存储任务投递落盘请求(非阻塞,队列化)
set_info_status(bit) / clear_info_status:6481、:6497、:2267 等置/清网络事件上报位,由上报流程异步发送
直接阻塞式 recv 循环:6296-6333接收任务内以 1s(无线 5s)超时自旋收包,期间会占用该任务,属已知设计

4.11 其他关键处理函数

5. 对外接口

5.1 本文件导出(供外部调用)

头文件(server.h)中声明的 37 个函数 + 第 3 节标注"全局"但未在头文件声明的函数(见 server.h.md 第 3 节注)。主要消费者:

导出的全局变量:g_au8NetRecvPack、g_au8FWBuf、g_au8FWBin、g_u8_fw_sdram1、g_u8_fw_sdram2(extern 声明在 server.h:256-259)、g_u8_can_send_done_flag、s_can_log_ctrl、s_can_log_raw_buffers(后三者无 extern 声明,靠隐式外部链接跨文件引用)。

5.2 本文件调用的外部函数/变量(按模块)

来源模块主要引用
net/ems.c、net/ems.hg_stEMSInfo、STEMSTab 成员、g_u8RestartFlag/g_u8SetConfigFlag/g_u8RemoteControlFlag/g_u8SyncFlag/g_u8DispatchFlag、g_u8_powerfactor_flag/g_u8_evchargeen_flag/g_u8_sys_shutdownen_flag/g_u8_set_charge_flag/g_u8_request_safetyconfig_flag/g_u8_g100_reboot_flag/g_u8_icdcupdatet_flag、g_as8SpecialCmdId、g_u8_resp_set_upload_interval、g_u8_resp_set_bat_brand、g_u8_send_safetyconfig_flag、g_resume_info[]、g_stNetSysInfo、g_t_app_info、set_info_status()、get_info_status()、clear_info_status()、Get_Status_Is_Updated_From_Server()、Set_Status_Is_Updated_From_Server()、set_flag_force_update()、get_flag_force_update()、set_response_force_upd_status()、get_response_force_upd_status()、set_updt_file_mask()、set_ver_from_server()、set_id_code_for_updt_cmd()、set_status_update_file_info()、clear_curr_offset_file_request()、save_ems_server_update_info()、save_job_post()、set_resume_cmd_id_code()、Set_Resume_Status_Current()、resume_buff_sdram、g_u32_resume_data_len、g_u8ReumeFailCnt、Set_Setup_Config_Status()、Set_Setup_Config_Extend1_Status()、Set_Setup_Config_Extend2_Status()、set_error_log_data_send_ok_true()、wireless_recv()、wireless_status_refresh()、wireless_meter_handle_lost()、can_send_queue_clear()/add()/start()、g_t_can_send_queue
逆变器中间件inverter_write_safety()、inverter_write_safesub()、inverter_read_safety()、inverter_read_safety_is_AS4777()、inverter_read_run_mode()、inverter_read_rated_ac_charge_power()、inverter_read_rated_ac_discharge_power()、inverter_read_grid_freq()、inverter_read_inv_type()、inverter_read_hw_ver()、inv3_upgradeable_flag()、inverter_set_*(safety/pf/reactpower/regulation/gfci/isolation/islanding/n_n_short/afci…约 60 个)、inverter_reset()、inverter_reset_dsp_eeprom()、inverter_clearing_fault()
BMS 中间件bms_read_battery_model()、bms_upgradeable_flag()、bms_get_ready_enable()、bms_set_ready_enable()、bms_set_mos_enable()、bms_set_self_lock_clear()、bms_set_self_lock_enable()、bms_reset()、send_battery_set_brand_cmd()、handle_bat_set_activation_time()、g_t_bat_info、g_t_bat_set_sn_info[]
电表中间件meter_reset()、meter_clear_run_data_flag()、meter_set_connect_status()、meter_set_meter_ct_rate/enable/negate()、meter_set_ct_sampling()、meter_set_meter_or_ct()、meter_set_saveConfigFlag()、meter_run_data_clear()、meter_get_meter_modle()、get_Meter_Updt_File_Mask()、get_meter_updt_object()/set_meter_updt_object()、mid_meter_clear()、set_meter_phase_sequnece_adjust_mark()、meter_set_chnt_wireless_module_regional_freq_bands()
EVC 模块get_evc_enable_flag()、get_evc_ii_enable_flag()、get_evc_type()、get_evc_ii_type()、set_evc_charge_flag()、set_evc_ii_charge_flag()、clear_evc_energy()、clear_evc_ii_energy()、set_evc_upgrad_flag()、g_t_evc_setting_info、g_t_evc_ii_single_config[]、g_t_evc_ii_run_info[]、g_t_evc_config_table、g_t_evc_debug_data
AFCI 模块get_afci_type()、get_afci_hard_version()、inverter_set_afci_self_check_clear()、set_afci_log_data_upload_send_status()
网络/协议net_init()、get_ethernet_server_port()、get_server_data_encryption_flag()、IP_Address、g_t_wireless_info、modbus_sunspec_en_set()、modbus_sunspec_en_addr_set()、sunspec_modbus_data_init()、uart3_init()、g_t_modbus_config
存储/Flashsave_data_to_fw_area_inFlash()、Save_Data_To_FW_Area_InSdram()、calculate_ems_flash_md5()、calculate_dev_FW_Flash_MD5()、resume_flash_reset()、factory_reset()、energy_reset()、epo_set_config_enable()
工具库json_get_value()、json_check()(common/json.h)、MD5Init/Update/Final()(common/md5.h)、hex_ascii_to_bin()(common/convert.h)、GetCrcCode_Modbus()(common/crc.h)、decrypt()(common/simple_encryption.h)、parse_fixed()(logic/math_tools.h)、parse_dispatch_para()(logic/dispatch.h)
系统/驱动OSTimeGet()、OSTimeDlyResume()、OSTimeDlyHMSM()、OSFlagPost()、OSSemPost()、recv()、Set_RTC()、get_rtc_tick()、get_board_hw_ver()、set_ems_reset()、ShowData()、aging_mode_data_sync()、set_fault_scord_status()、fault_scord_init()、g_t_logic_info、g_t_meter、g_t_inv_info、g_t_system_base_info、g_t_config_save_flag、g_t_time_control_run、g_t_debug_run_info、g_t_upgrade_run_info、g_t_parallel_total_machine_network、g_t_inovance_comm_info、STDryNodeInfo、LCD_RTDB、g_system_function_info、senergys_query_inv_register、g_server_grid_sampling_curve_t、ip_add_skip_change_clear()、set_system_function_enable()、parallel_set_*

6. 依赖关系

7. 备注与风险点

  1. store_system_set_data() 返回未初始化变量(net/server.c:3910-3915):函数体只有 u8 res; 与 return res;,没有任何计算,返回值是栈垃圾。当前看 grep 未发现调用点,属死代码,但若被启用会直接产生不确定行为。
  2. handle_server_sys_shutdown_command() 可能使用未初始化局部变量(:2239):u8_temp、u16_temp_days 仅在 json_get_value 成功时赋值,随后无条件参与 :2264-2265 的比较与写入。若云端漏发 Parameter1 或 Parameter2,会写入随机值到 u8_sys_shutdown_flag/u16_sys_shutdown_days 并触发配置落盘——可能导致非预期停机配置。
  3. handle_set_upload_interval() 同类问题(:2512):l_u8_msg_id、l_u16_upload_interval 未初始化即传入 set_upload_interval()。
  4. strstr() 返回值未判空即传给 sscanf(deal_get_config_data)::4178(EmsLanguage)、:4296("EV1")、:4347("EVC%d" 循环)、:4409/:4436("TIMEChaDis%d")、:4465("TIMEfeedin%d")、:4831/:4843(DryTime)、:4992(PeShValue)、:5103(InstallDate)、:5135(CsipAus)。云端配置报文中只要缺失其中任一字段,strstr 返回 NULL,sscanf(NULL, ...) 在多数 C 库下是直接访问 0 地址导致 HardFault。本函数又在 CMD_SET_CONFIG(APP 直连下发)路径上高频执行,属最高优先级风险。
  5. handle_tcp_recv_data() 的 l_u32PackLen 可能未初始化(:6286 声明,仅在 type == W5500 或 type == WIRELESS 分支赋值):若传入其它 type 值,:6403 的 (l_u32PackLen > 0) && (...) 判断读到栈垃圾,理论上可能进入 CRC 校验分支。当前调用点只传 W5500/WIRELESS,但缺少 default 保护。
  6. handle_special_cmd() 的长度下溢(:2615):json_check((char *)(buf + 7), len - 7 - 2) 中 len 为 u32,若报文长度 < 9(理论上被接收侧长度校验拦住),len - 9 会下溢成极大值。
  7. CRC 校验失败无任何响应或计数(:7121-7127):调试关闭时静默丢包,现场排查只能靠抓包。同样,所有 recv 超时、l_u32PackLen 越界都只 return,无重试、无错误计数。
  8. check_server_fw_is_updating_for_dev() 的静态计数器 s_u8_upgrade_cnt 是全局共享的(:3224):不区分设备类型。若 EMS 与 BMS 交替失败,计数会相互累加,可能在累计 5 次后清掉"本来还能成功"的设备的升级表。
  9. check_server_fw_is_updating_for_dev() 失败分支故意不清 s_u8_upgrade_cnt(:3254-3256)只在未达上限时自增,达到上限才清零;结合上一条,属于设计如此但边界行为较隐蔽。
  10. 掩码用 strstr 子串匹配而非位掩码运算(:3227、:3449、:3705、:3744):见 server.h.md 风险点,"00001004" 会同时命中 INV1 与 METER,且判定顺序决定了实际归属,容易产生错误升级目标。
  11. handle_fw_file_from_server() 的 test 变量赋值后未被使用(:3677 起多处 test = n; 后直接 return;),属遗留代码;同时 save_fw_to_sdram/save_fw_to_flash 对 Len > 1024 静默不写,而调用点已在上游限制 ≤1024,是"双重保护但无错误上报"。
  12. get_current_point_file_mask() 返回 static 缓冲(:276-293):static char fileMask[10] 在多任务环境(接收任务 + 升级任务)中非重入;同名现象出现在 get_current_point_version_force_updt()(:383)与 get_system_sn()(:416)。当前调用点都在接收任务上下文,暂未暴露竞态,但属于脆弱设计。
  13. handle_server_set_bms_activate_time_command() 用 memcmp(..., 32) 比较 s_single_sn_splice(:2169):该字段的实际可用长度取决于结构体定义,memcmp 按 32 字节比较会把结束符之后的填充字节一并纳入比较;时间字符串解析固定按 +8/+5/+0 偏移取子串(:2175-2180),隐含输入必须是 YYYY-MM-DD 且长度恰好 10,无长度校验。
  14. CAN_LOG_CTRL_T、CAN_LOG_RAW_ITEM_T、MAX_CAN_LOG_ID_COUNT、MAX_CAN_LOG_COUNT、SPECIAL_CMD_CAN_SEND、SPECIAL_CMD_UPLOAD_CAN_LOG_STR 在本工程中找不到任何定义(已对全仓库含被忽略文件执行 grep -rn,仅 net/server.c:80、net/server.c:7266、net/ems.c 多处为使用)。SDRAM_CAN_LOG_BUF_ADDR 则定义在 hardware/sdram/sdram.h:68,注释标注长度仅 32KB,而分配算法要求 MAX_CAN_LOG_ID_COUNT * MAX_CAN_LOG_COUNT * sizeof(CAN_LOG_RAW_ITEM_T)(:7243)——若这两个常量按"12 个 ID × 若干条"的量级,极可能超出 32KB 而踩到相邻 SDRAM 段。这是本次分析发现的最严重的未决问题:要么该模块头文件未纳入本快照,要么代码处于不可编译状态,务必向代码维护者确认。
  15. handle_server_can_send_command() 中 l_s8_param 拷贝长度(:7149):memcpy(l_s8_param, g_au8FWBuf, sizeof(l_s8_param) - 1) 固定拷 255 字节,而源 g_au8FWBuf 为 2KB、目标 256 字节,长度本身安全,但若云端在 Parameter1 中不含 \0,strtok_r 之后的 printf("%s") 读到的是 l_s8_param 中已截断的内容,行为可预期;此处无害但依赖 sizeof 一致性,改动 l_s8_param 尺寸时需同步核查。
  16. handle_server_power_factor_command() 的无功值处理不对称(:1706-1739):flag=1/2 时只调用 inverter_set_fix_reactpower_value(0),注释写的是"Set QU"/"Set cos phi(P)",实际未按注释设置对应参数,疑似功能未完成。
  17. handle_server_parameter_set() 中 l_u8_server_temp_switchover 缓冲仅 2 字节(:852):用于接收 strtok 出来的切换位字符串,snprintf(...,2,"%s",...) 会截断为 1 字符 + 结束符,超过 1 位数字的输入被静默截断;l_u8_server_temp_switchover_tim[5] 同类(最长 4 字符,但校验允许到 250,恰好够用)。
  18. deal_get_config_data() 的 for (i = 0; i < MAX_EVC_II_NUM; i++) 循环内 sprintf(l_u8_buf, "\"EVC%d\"", i+1)(:4344-4347):l_u8_buf 只有 64 字节,索引最大 4,安全;但同函数 :4408/:4435 的 sprintf 到 64 字节缓冲写入 "TIMEChaDis28"(12 字符),也安全。整体无明显溢出,但全部依赖固定小索引范围。
  19. deal_get_safety_config_data() 的 SN 校验方向与配置函数相反(:5225-5229):SN 字段缺失时不返回(放行),仅"存在且不匹配"才丢弃,与 deal_get_config_data 的严格策略不一致,存在被构造报文绕过的空间。
  20. handle_server_set_bms_activate_time_command() 在 i == 0 与 i > 0 两条分支里对同一逻辑重复实现(:2156-2193),且当 memcmp 命中但 Parameter2 缺失时直接 return,不置任何错误标志,云端无法感知失败。

(其余为调试打印、#if 0 屏蔽段(:3026、:3040-3050、:1475-1482、:1255)、被注释掉的 net_init()(:1103)等版本遗留代码,属清理项而非缺陷。)