server.c
handle_tcp_recv_data,:6280)4.2 特殊指令分发(handle_special_cmd,:2605)4.3 扩展参数表(handle_server_parameter_set,:849)4.4 远程升级状态机(load_current_update_data,:610)4.5 升级文件信息校验与下载(:2346、:3222、:3318、:3435、:3529、:3675)4.6 配置下发解析(deal_get_config_data,:4082)4.7 安规参数解析(deal_get_safety_config_data,:5219)4.8 无线电表数据解析(parse_meter_data,:6108)4.9 CAN 自定义帧与 CAN 日志采集(:7132、:7219、:7300)4.10 与 uCOS-II 任务/信号量/时钟的配合4.11 其他关键处理函数5. 对外接口5.1 本文件导出(供外部调用)5.2 本文件调用的外部函数/变量(按模块)6. 依赖关系7. 备注与风险点源文件路径:
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_flag | u8 | CAN 自定义帧发送完成标志(net/server.c:77) |
s_can_log_ctrl | CAN_LOG_CTRL_T | CAN 日志采集控制块(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_flag | extern 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_set | static | Extra 指令的二级参数表: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_field | static | 解析 "KEY":true/false,返回 1/0,键不存在返回 -1(:4042) | |
json_store_by_size | static | 按 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):
| 偏移 | 长度 | 含义 | |||
|---|---|---|---|---|---|
| 0 | 1 | 设备类型,发送侧写 EMS = 0x01(net/ems.h:68,见 net/ems.c:2913) | |||
| 1 | 1 | 命令方向,REQUEST_COMM=0x01 / RESPONSE_COMM=0x02(net/ems.h:88-89) | |||
| 2 | 1 | 命令字 ENCmdType,分发依据 | |||
| 3-6 | 4 | 正文长度,大端:`(head[3]<<24) | (head[4]<<16) | (head[5]<<8) | head[6](:6315);发送侧只填 [5][6],[3][4] 固定 0(net/ems.c:2917-2921`) |
| 7 | N | 正文,JSON 文本(buf + 7 被各处理函数直接当作 JSON 起点) | |||
| 7+N | 2 | 校验码,高字节在前:GetCrcCode_Modbus(pack, len-2)(Modbus CRC16)(:6405-6407) |
接收流程:
- 清零
g_au8NetRecvPack(5KB,:6290)。 - 按
type分两条完全对称的分支:type == W5500走recv(SOCK_TCP_CLIENT, ...)(:6298),type == WIRELESS走wireless_recv(...)(:6353)。两者都是"循环收头直到满 7 字节或超 1 秒"(W5500 正文再超 1 秒、无线正文超 5 秒),未收满直接return(丢帧,:6311、:6337)。 - 长度字段做上限保护:
l_u32DataLen > sizeof(g_au8NetRecvPack) - 9时钳到该值(:6316),防止写越界。 l_u32PackLen = 7 + dataLen + 2(:6340),并在(0, sizeof(g_au8NetRecvPack)]区间内才继续(:6403)。- CRC 比对;不匹配仅打印调试信息,不回复(
:6407、:7121-7127)。 - 记录端口
g_stNetSysInfo.u16_skip_port(:6409);若服务器为私有加密通道且非 APP 连接,对正文做decrypt()原地解密(:6410-6416,长度packLen-9)。 - 若正文含
"Status":"LoginError"→bIsLogin = FALSE(:6418-6425)。 - 无线上行未登录时不处理非 0xA4/0xA5 命令(
:6427-6433),保证无线电表数据在断云时仍可用。 switch(pack[2])按命令字分发(:6435)。
关键命令字与动作(:6435-7119):
| 命令字 | 名称 | 动作 |
|---|---|---|
| 0x00 | CMD_HEART_BEAT | 收到 Success 则刷新 u64TickHeartRcv、清零 u16HeartRstCnt |
| 0x01 / 0x02 | CMD_REGISTER / CMD_LOGIN | 注册无动作;登录成功置 bIsLogin=TRUE、打开 3 个配置请求标志、把 3 个请求 tick 对齐到 5s/10s/15s 边界、time_ticksenddata = get_rtc_tick()-240,并投递事件日志;失败清登录 |
| 0x03 / 0x14 / 0x60 | CMD_GET_CONFIG_INFO / ..._EXTEND1 / ..._EXTEND2 | deal_get_config_data(buf+7, 对应 type),随后清对应请求标志 |
| 0x04 / 0x15 / 0x61 | CMD_SEND_CONFIG_INFO / ..._EXTEND1 / ..._EXTEND2 | 置 APP 配置回传成功标志 |
| 0x42 / 0x4C | CMD_SET_CONFIG / ..._EXTEND1 | APP 直连配置:清登录、置 u8_app_connect_flag、deal_get_config_data(),置 u8_set_cfg |
| 0x43 / 0x4D / 0x44 / 0x45 / 0x4E | 各 GET_* / SEND_RUN_INFO* | 置 APP 连接标志与对应数据请求标志 |
| 0x08 | CMD_SPECIAL_INTERFACE | handle_special_cmd(g_au8NetRecvPack, l_u32PackLen) |
| 0x20 | CMD_CHECK_UPDATE | handle_check_update() |
| 0x21 | CMD_REQUEST_FILE_UPDATE | 先刷新心跳 tick,再 handle_fw_file_from_server() |
| 0x0C | CMD_RESUME_INTERFACE | 清断点续传缓冲与计数,状态置 RESUME_STS_Idle |
| 0x0F | CMD_UPDATE_ERROR_LOG | set_error_log_data_send_ok_true() |
| 0x09 | CMD_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 / 0x57 | CMD_SET_SAFETY_CONFIG_INFO / CMD_SET_SAFETY_CONFIG | deal_get_safety_config_data(),清设置标志并记录 tick;0x57 另置 APP 标志 |
| 0x4B / 0x58 / 0x47 | 安规查询 | 清发送标志 / 置 APP 查询标志 |
| 0xA4 | CMD_RXD_METER_DATA | parse_meter_data(...,"grid meter",GRID_METER);若 PV 检测标志为真再解析 "pv meter",否则清 PV 电表数据 |
| 0xA5 | CMD_RXD_WIFI_INFO | wireless_status_refresh(buf+7) |
0x56(INOVANCE_TYPE 条件编译) | CMD_SEND_FAULT_RECORD_DATA | 成功→set_fault_scord_status(),失败→fault_scord_init() |
| 0x18 | CMD_SEND_BURNIN_DATA | aging_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 供响应回填。
各分支动作要点:
"Update"(:2624):先清u8_forceupdt_flag,再按Parameter1分派到 8 种目标。是否强制升级由运行模式决定,例如 EMS/AFCI:(inverter_read_run_mode() != cUPS) || l_u8_upgrade_flag == TRUE(:2635、:2678);INV ARM 还需inv3_upgradeable_flag()(:2641);BMS 需bms_upgradeable_flag() && !bms_get_ready_enable()(:2658);Meter 用inverter_read_grid_freq() > 4500(即 50Hz 系统,:2665);EVC 用充电桩使能(:2672)。"INV"会连续调用两次:INV1 与 INV2(:2649-2653),且 INV1 返回非 0 时提前返回。"Restart"(:2684):置g_u8RestartFlag = TRUE。"SetConfig"(:2696):置g_u8SetConfigFlag = TRUE。"Resume"(:2708):按Y/M/D/H/F/S标记从Parameter1提取时间,Parameter2取长度;在 10 条g_resume_info[]里查重,不重复且u32_length == 0的空槽才写入;置g_u8_resp_resume_flag = TRUE。"RemoteControl"(:2781):ForcedCharge/ForcedWork/CloseMOS三种;ForcedWork会同时清强制充电与电池功率控制标志并bms_set_mos_enable(TRUE)。"SyncTime"(:2821):置g_u8SyncFlag并立即set_sys_rtc(buf+7)。"Dispatch"(:2835):handle_server_dispatch_command()+g_u8DispatchFlag。"Extra"(:2848):handle_server_extra_command()+g_u8ExtraFlag。"PowerFactor"(:2861)/"EVChargeEN"(:2874)/"DebugRunInfo"(:2891)/"UploadSafetyConfig"(:2903)/"SYSShutDownEN"(:2916)/"SetChargeEN"(:2929)/"SendEnergyDataEN"(:2943)/"ICDCUpdate"(:2956)/"G100Reboot"(:2971)/"SYSSelfTest"(:2984)/"SetBMSActivationTime"(:2997)/"SetSOCCalibrationg"(:3010)/"SmartDispatch"(:3024)/"ExpandICDCUpdate"(:3053)/"UploadInterval"(:3069)/"SetBatBrand"(:3080):分别调用对应处理函数并置响应标志。"GetBatLog"(:3038):整段被#if 0屏蔽,当前无效。"CanSend"(:3090):handle_server_can_send_command()。"UploadCanLog"(:3103):防重入——若s_can_log_ctrl.active != 0则回一条失败响应并返回;否则解析Parameter1的"ID1,ID2,...,IDN:条数"格式:先按:切出 ID 列表与条数,再按,逐个sscanf("%08lX")解析为 u32 ID;校验1 ≤ id_count ≤ MAX_CAN_LOG_ID_COUNT且1 ≤ count ≤ MAX_CAN_LOG_COUNT后start_can_log_capture()并回成功。
4.3 扩展参数表(handle_server_parameter_set,:849)
index 采用高字节分组 + 低字节索引编码,l_s32_value = atoi(Parameter2):
组(index>>8) | 参数点 | 说明 | |
|---|---|---|---|
0x00 | 2..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`** 通知落盘任务。 |
0x08 | 0..6 | 逆变器电网曲线、阴影扫描、孤岛检测使能、过欠压/过欠频降载、DC 两倍允充功率、意大利防逆流 | |
0x09 | 0..0x0C | 清零类:电能清零、电表清零、恢复出厂、EVC 电能清零、三相不平衡、IgFollow、隔离限值、断点存储复位、MID 电表清零、汇川故障录波使能 | |
0x0A | 0..2 | 电表相序调整标记(电网表/光伏表/三相表) | |
0x0B | 0..9 | 安规值:PF 值、固定无功、调节率、4 档 GFCI 阈值、N-PE 短路、GFCI 检测使能、GFCI 屏蔽 | |
0x0C | 0..3 | 离网切断/唤醒 SOC(限 5..90,且唤醒 SOC 至少比切断 SOC 大 5),及"范围内零"标记 | |
0x10 | 0,2,3 | 重启类:0=重启 INV、1=重启 BMS、2=复位 DSP EEPROM、4=清故障、5=重启 WIFI;2=N-N 短路;3=AFCI 自检清除 | |
0x11 | 0..11 | Sunspec/功能使能与汇川寄存器查询(senergys_query_inv_register) |
handle_server_extra_command(:1556)在调用它之前先拦截三个特殊 index:
65534(:1580):改写系统 SN并save_job_post(SYSTEM_BASE_PARA_PAGE)→ 延时 1 秒 →system_hard_reset()。OEM_BW 取 16 字节,其它 OEM 只取 15 字节(:1583-1590)。65535(:1596):改写 MAC,要求Parameter2长度恰为 12 个 HEX 字符,逐 2 字符strtoul成 6 字节;且l_u8_buf[0] % 2 == 0(单播地址)才接受,随后落盘 + 延时 1 秒 + 硬复位。65533(:1621):电池单包 SN 替换,"源SN,目标SN"且两者都需含"ALP"子串,写入g_t_bat_set_sn_info[]首个空闲槽(状态置BMS_SET_SN_IDLE)。
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。
CHECK_NO(:624):若check_updt_force_is_none()(8 个设备都无强制升级标志)→pstCurrUpt = NULL;否则进入CHECK_EMS。CHECK_EMS/BMS/INV1/INV3/METER/EVC/AFCI:统一调load_current_update_data_setCurrUpt(当前设备, 下一状态, &state)(:577)。CHECK_INV2(:667)单独特殊处理:必须INV1已置Get_Status_Is_Updated_From_Server()才挂接 INV2,否则清 INV2 标志——保证逆变器两台串行升级。
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 >= 总长:
- 未结束:
OSSemPost(g_pUpdateSem)+increase_current_offset_file_updt(lenPck); - 结束:按设备调用
calculate_ems_flash_md5()或calculate_dev_FW_Flash_MD5(type, sdram 基址, 下载区枚举),然后set_current_point_flag_force_updt(STOP_FORCE_UPDT)、set_current_point_updt_file_info(FALSE)、set_updt_status_current(UPDT_STS_IDLE)。
落盘分派(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 种解析模式:
- 前置校验(
:4107-4126):解析"SN"与本地 15 字节比对,不一致置l_u8SNErrorFlag;解析"ZipCode"判空。仅当type == CMD_SET_CONFIG或CMD_SET_CONFIG_EXTEND1(APP 直连)时允许 ZipCode 为空,否则直接return不处理任何配置。 - 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)。
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)宽度转换。
- 字符串切割块:
"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)。 - 带副作用/带范围检查的单字段:网络类(
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 个字段。
- 入口 SN 校验(
:5225-5229):"SN"存在且与本地前 15 字节不一致则整包丢弃(注意与配置函数不同:SN 字段缺失时不拦)。 - 数值转换有两类:
atoi()直接取整(用于使能位、枚举、次数),parse_fixed(buf, scale)(logic/math_tools.c:56)把小数串按scale放大为整数,scale=10表示保留 1 位小数、100表示 2 位小数、1表示只取整数部分。典型:OVP1_Value用parse_fixed(...,10)(:5233),OVP1_Time用atoi后再/10(:5242-5243,把"0.1s 单位"转成"秒"),UnFreqDerStart用scale=100(:6050)。 - 字段分组覆盖:过/欠压 3 段 + 10 分钟段 + 恢复值;过/欠频 3 段 + 恢复值;频率变化率;并网等待/重连/爬坡;ISO/漏电流(DCI)检测与阈值、GFCI 4 档阈值;孤岛/零线/LN 突变/AFCI/PV 电流/直流电压/波形检测使能;UPS 电压保护、N-PE 短路、备电频率;有功/无功下垂控制(3Tau、P-U 与 Q-U 曲线 4 点、Q 点对应 PF);频率下垂(过频/欠频各起点、百分比、Pn);低压穿越
FRTEnable;PELoss;VolDrop;Pref模式;AcOutBat(仅TOR_ERZEUGER生效,:6075)与PowerBatAc(parse_fixed(...,100),:6082);末尾"Preference"设过频优先模式(:6091)。
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 其他关键处理函数
set_sys_rtc(:743):Parameter1为 Unix 秒、Parameter3为时区偏移秒;LocalTime = secTime + TimeZoneSecond后localtime()转换,年份做tm_year - NOW_BASE_YEAR + TIME_BASE_YEAR基准换算(RTC 芯片 2000 年基准差异),最后Set_RTC()。handle_server_dispatch_command(:790):Parameter1=使能,Parameter2通过parse_dispatch_para()依次尝试解析 8/7/4 个功率点(任一成功即算收到),Parameter3=运行时长且下限强制 90 秒(:826-829);三个参数任一到齐即set_dispatch_parameter()并唤醒 logic 任务。handle_special_icdcupdate_cmd(:1993):MD5 输入顺序为SN + 地址 + 端口1 (+端口2) + 加密1 (+加密2),其中端口/加密在u8_expand_icdcupdate_flag == TRUE时按|切分为两路;MD5Final后sprintf("%02X")逐字节转 32 字符大写 HEX 与云端下发的Description比对,一致才更新IP_Address的跳过端口/加密/地址并net_init()。check_need_response_updt_devcie(:134):固定顺序INV1→INV2→INV3→BMS→Meter→EMS→EVC→AFCI,返回第一个命中项并立即清其响应标志,每次调用只取一个。handle_server_smart_dispatch_command(:1891):b_recv_flag初值 TRUE,Parameter1 或 Parameter2 任一缺失/解析不为 4 项则&= FALSE,最终失败时会主动关闭智能化控制并落盘(:1945-1946)。
5. 对外接口
5.1 本文件导出(供外部调用)
头文件(server.h)中声明的 37 个函数 + 第 3 节标注"全局"但未在头文件声明的函数(见 server.h.md 第 3 节注)。主要消费者:
net/ems.c:调用load_current_update_data()、check_need_response_updt_devcie()、get_updt_dev_type_curr()、is_goto_get_update_file_status()、is_goto_update_check_status()、get_current_point_file_mask()/file_len/offset、get_updt_status_current()/set_updt_status_current()、get_system_sn()、get_ems_hard_type()、get_ems_hard_version()、get_local_md5_ems_upt_file()、system_hard_reset()、handle_tcp_recv_data()(由midware/ethernt/ethernet.c:653/:899直接调用)。midware/ethernt/ethernet.c:handle_tcp_recv_data(W5500)(:653)、handle_tcp_recv_data(WIRELESS)(:899),并提供g_pUpdateSem(:996)。- 各
*_download.c/ 升级流程:通过server.h的访问器读取当前升级点信息,回写save_fw_to_spi_flash相关状态。
导出的全局变量: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.h | g_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 |
| 存储/Flash | save_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. 依赖关系
- 依赖(include):
time.h、board_init.h、socket.h、ethernet.h、ems.h、server.h、upd_download.h、ems_download.h、bms_download.h、inv_download.h、meter_download.h、data.h、json.h、md5.h、dry_node.h、key.h、bms.h、inverter.h、meter.h、logic.h、dispatch.h、pcf8563.h、flash.h、flash_data_store.h、lcd.h、sdram.h、usart3.h、crc.h、system_config.h、modbus.h、system_aux.h、fault_warn_log.h、simple_encryption.h、convert.h、evc.h、evc_ii.h、evc_download.h、evc_upgrade.h、ct.h、battery_comm.h、afci_download.h、afci_comm.h、inovance_comm_usart.h、sunspec_business.h、inttypes.h、math_tools.h(net/server.c:13-60)。它几乎横跨了midware/、logic/、data/、hmi/、common/全部应用层目录。
- 被依赖:
midware/ethernt/ethernet.c:接收任务调用handle_tcp_recv_data()(:653、:899)。net/ems.c:升级流程调用本文件大量访问器(load_current_update_data、get_current_point_*等),并使用g_pUpdateSem(:4686、:4709)配合handle_check_update/handle_fw_file_from_server的信号量。net/*_download.c(ems_download.c、bms_download.c、inv_download.c、meter_download.c、evc_download.c、afci_download.c、upd_download.c):依赖server.h的STUpdateTab与FILE_MASK_*、ENUpdateType,并在Save_Data_To_FW_Area_InSdram/save_data_to_fw_area_inFlash中消费g_u8_fw_sdram1/2。hmi/与logic/通过server.h中的访问器读取升级状态(本文件不主动回调它们)。
7. 备注与风险点
store_system_set_data()返回未初始化变量(net/server.c:3910-3915):函数体只有u8 res;与return res;,没有任何计算,返回值是栈垃圾。当前看grep未发现调用点,属死代码,但若被启用会直接产生不确定行为。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并触发配置落盘——可能导致非预期停机配置。handle_set_upload_interval()同类问题(:2512):l_u8_msg_id、l_u16_upload_interval未初始化即传入set_upload_interval()。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 直连下发)路径上高频执行,属最高优先级风险。handle_tcp_recv_data()的l_u32PackLen可能未初始化(:6286声明,仅在type == W5500或type == WIRELESS分支赋值):若传入其它type值,:6403的(l_u32PackLen > 0) && (...)判断读到栈垃圾,理论上可能进入 CRC 校验分支。当前调用点只传W5500/WIRELESS,但缺少default保护。handle_special_cmd()的长度下溢(:2615):json_check((char *)(buf + 7), len - 7 - 2)中len为u32,若报文长度 < 9(理论上被接收侧长度校验拦住),len - 9会下溢成极大值。- CRC 校验失败无任何响应或计数(
:7121-7127):调试关闭时静默丢包,现场排查只能靠抓包。同样,所有recv超时、l_u32PackLen越界都只return,无重试、无错误计数。 check_server_fw_is_updating_for_dev()的静态计数器s_u8_upgrade_cnt是全局共享的(:3224):不区分设备类型。若 EMS 与 BMS 交替失败,计数会相互累加,可能在累计 5 次后清掉"本来还能成功"的设备的升级表。check_server_fw_is_updating_for_dev()失败分支故意不清s_u8_upgrade_cnt(:3254-3256)只在未达上限时自增,达到上限才清零;结合上一条,属于设计如此但边界行为较隐蔽。- 掩码用
strstr子串匹配而非位掩码运算(:3227、:3449、:3705、:3744):见server.h.md风险点,"00001004"会同时命中INV1与METER,且判定顺序决定了实际归属,容易产生错误升级目标。 handle_fw_file_from_server()的test变量赋值后未被使用(:3677起多处test = n;后直接return;),属遗留代码;同时save_fw_to_sdram/save_fw_to_flash对Len > 1024静默不写,而调用点已在上游限制≤1024,是"双重保护但无错误上报"。get_current_point_file_mask()返回 static 缓冲(:276-293):static char fileMask[10]在多任务环境(接收任务 + 升级任务)中非重入;同名现象出现在get_current_point_version_force_updt()(:383)与get_system_sn()(:416)。当前调用点都在接收任务上下文,暂未暴露竞态,但属于脆弱设计。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,无长度校验。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 段。这是本次分析发现的最严重的未决问题:要么该模块头文件未纳入本快照,要么代码处于不可编译状态,务必向代码维护者确认。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尺寸时需同步核查。handle_server_power_factor_command()的无功值处理不对称(:1706-1739):flag=1/2 时只调用inverter_set_fix_reactpower_value(0),注释写的是"Set QU"/"Set cos phi(P)",实际未按注释设置对应参数,疑似功能未完成。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,恰好够用)。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 字符),也安全。整体无明显溢出,但全部依赖固定小索引范围。deal_get_safety_config_data()的 SN 校验方向与配置函数相反(:5225-5229):SN 字段缺失时不返回(放行),仅"存在且不匹配"才丢弃,与deal_get_config_data的严格策略不一致,存在被构造报文绕过的空间。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)等版本遗留代码,属清理项而非缺陷。)