ems.c
net/ems.h)2.3 本文件用的重要宏常量3. 函数清单4. 核心逻辑解析4.1 通信帧格式与封帧(fill_common_data,:333)4.2 发送通道:消息队列与以太网任务协作4.3 主状态机(ENStatus)与 ems_time_event() 的对应关系4.4 check_idle_process():派发优先级与周期表(:4521)4.5 上报报文的内容与打包要点4.6 登录与保活4.7 升级与断点续传流程4.8 调频/调度/FFR 数据采集(get_*_data())4.9 CAN 日志采集/上报两态机(需求 7)4.10 时间基准与定时语义4.11 关键全局变量的读写与触发条件5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
net/ems.c所属模块:net(网络应用层 / 云端 EMS 通信) 代码行数:7289
1. 文件职责
net/ems.c 是 EMS 主控与云端服务器(AlphaCloud/自建平台)之间 TCP+JSON 协议的上行组包与协议状态机主体:它把本地电网电表、逆变器、BMS、充电桩、油机、能量统计、配置与事件等数据,按约定的 JSON 字段格式逐个封装成帧,再通过 uCOS-II 消息队列交给以太网任务发出。
它同时承担"云端下发指令的应答与执行编排":重启、设/读配置、远程控制、对时、调度、功率因数、充电桩使能、一键停机、SOC 校准、电池激活时间、CAN 帧下发、安规配置读写、升级文件下载与断点续传等动作,都在本文件的状态机里被触发、组应答帧,并修改对应的全局标志位。
数据流的环节:设备中间件读数据(只读接口)→ 本文件组 JSON 帧 → g_au8NetSendPack/g_t_freq_dispatch_data.u8_send_buff → OSQPost(g_pEthernetQ) → midware/ethernt/ethernet.c 的以太网任务发送;下行帧的接收、解析与命令分发在 net/server.c,本文件只处理"我方要往外发"的一侧以及由此派生的本地副作用(例如置 g_u8RestartFlag 后的复位动作)。
驱动方式:所有逻辑运行在 user/main.c:255 创建的 task_net_app 任务中(TASK_NET_PRIO = 16、栈 512 word、循环周期 25 ms,见 UCOSII/uCOS-CONFIG/app_cfg.h:67、:94)。
2. 关键数据结构与宏
2.1 本文件定义/占用的全局变量
| 名称 | 类型(结构体/枚举/宏/联合体) | 说明 |
|---|---|---|
g_au8NetSendPack[5*1024] | 全局数组 | 主发送缓冲,__attribute__((at(SDRAM_EMS_NET_SEND_BUF_ADDR))) 定址 SDRAM,长度 5 KB(net/ems.c:65) |
g_u16DataSendLen | 全局变量 | 当前待发报文总长(含 CRC),由各 fill_* 设置,handle_message_queue() 取用(net/ems.c:95) |
g_stEMSInfo | 结构体 STEMSTab | 通信主控块(定义见 net/ems.h:283),保存登录/建链标志、全部定时 tick、重试计数、升级表(net/ems.c:108) |
g_t_app_info | 结构体 APP_TAB | 局域网 APP 通道控制块(net/ems.c:109) |
g_t_debug_run_info | 结构体 DEBUG_RUN_INFO_T | 调试数据上传使能/时长/间隔(net/ems.c:110) |
g_t_upgrade_run_info | 结构体 UPGRADE_INFO_T | 升级过程上报信息(net/ems.c:111) |
g_t_freq_dispatch_data | 结构体 FREQ_DISPATCH_BUFFER_T | 调频/调度数据缓存 + JSON 缓冲,定址 SDRAM_FREQ_DISPATCH_ADDR(net/ems.c:120) |
g_t_fault_scord_data | 结构体 FAULT_SCORD_T | 故障录波数据,仅在 #if (INVERTER_TYPE == INOVANCE_TYPE) 内定义,定址 SDRAM_FAULT_SCORD_START_ADDR(net/ems.c:121-123) |
g_as8SpecialCmdId[32] | 全局字符数组 | 最近一次云端特殊指令的 CmdIndex,由 net/server.c 填充,本文件回显(net/ems.c:118) |
g_s8_net_comm_type[30] | 全局字符数组 | 网络类型描述串(ALPHA_LINKER_WIFI/E_LINKER_4G/W5500 及其组合)(net/ems.c:119) |
u8_senergys_query_inv_register_read_flag_cnt | 全局变量 | 汇川/senergys 寄存器回读数据的重发次数计数(net/ems.c:117,最大 5 次后清 senergys_query_inv_register.u8_read_flag) |
s_u8DevType | static ENUpdateType | 当前需要应答"强制升级"的设备类型(net/ems.c:113) |
s_run_info_extend1_time | static struct RTC_DateTime | 扩展运行数据的采集时刻快照(net/ems.c:114) |
s_u8Flag1 | static volatile u8 | 状态机状态实际存储(类型为 ENStatus),net/ems.c:115 |
g_u8*Flag 系列 | 全局 u8 | 跨模块异步请求标志(重启、设配置、远控、对时、调度、参数、功率因数、充电桩使能、停机、充电、能量上传、自检、域名、G100 重启、扩展域名、上传周期、电芯品牌、安规等),定义见 net/ems.c:66-106,extern 声明见 net/ems.h:467-506 |
2.2 使用的主要类型(定义在 net/ems.h)
| 名称 | 类型 | 说明 |
|---|---|---|
ENCmdType | 枚举 | 全部协议命令字(0x00 心跳、0x02 登录、0x03/0x04 读写配置、0x08 特殊指令、0x09 信息、0x11/0x12/0x19/0x1B/0x1C/0x1A 调频与调度、0x20/0x21 升级、0x24 升级信息、0x42-0x4E/0x57/0x58 配置与安规、0x55 单体、0x56 录波、0x62 AFCI 日志、0x63 CAN 日志、0xA3 电池自检信息、0xA6 混搭信息 等) |
ENStatus | 枚举 | 状态机全部状态;本文件 changed_state_tcp_protocol() 只接受 < STATUS_MAX 的值 |
ENInfoType | 枚举(位掩码) | 即时消息类型位:SYSTEM_RESTART/SOCKET_STATUS/EVENT_LOG/GRID_STATUS/SELF_TEST_RESULT/EEPROM_FAULT/AFCI_SELFCHECK_STATUS 等 23 位 |
STEMSTab | 结构体 | 见 2.1 |
FREQ_DISPATCH_BUFFER_T | 结构体(#pragma pack(1),16 KB) | 200 点 × 5 组调频量 + u8_send_buff[11771] |
UPGRADE_INFO_T | 结构体 | 升级上报块,含位域联合 u16_upgrade_error(web/download/ups/md5/timeout/boot 错误位) |
FAULT_SCORD_T | 结构体(#pragma pack(1),40 KB) | 故障录波:s16_channel[19][320]、s32_channel[8][320](INOVANCE) |
APP_TAB / DEBUG_RUN_INFO_T | 结构体 | APP 通道与调试上传配置 |
2.3 本文件用的重要宏常量
| 名称 | 说明 |
|---|---|
EMS(0x01) / REQUEST_COMM(0x01) / RESPONSE_COMM(0x02) | 帧头设备域、方向域取值(net/ems.h:75、:88-89) |
SENCOND_100MS/SENCOND_2S/SENCOND_5S/SENCOND_10S/SENCOND_15S/SENCOND_60S | 本文件实际用到的毫秒节拍常量(值 = 1000*n) |
REQUEST_UPDATE_INTERVAL = SENCOND_100MS | 升级文件分片请求间隔,100 ms |
NET_DATA_SEND_TRY(50) | 同一报文在队列中的最大驻留次数,超限强制丢弃 |
MAX_READ_PER_CALL(60) / MAX_READ_TIME_MS(5) | 函数内局部 #define(net/ems.c:6859-6860):CAN 日志一次轮询最多读 60 帧、最多耗 5 ms |
CAN_LOG_CAPTURE_TIMEOUT_MS / CAN_LOG_PACK_COUNT_PER_PACK / MAX_CAN_LOG_COUNT / MAX_CAN_LOG_ID_COUNT | CAN 日志模块常量,本快照内无定义(见第 7 节) |
SPECIAL_CMD_*_STR | 特殊指令应答时回显的命令名(net/server.h:26 起),本文件在 handle_response_special_cmd() 中使用 |
FIELD_NAME_* | 升级校验/取文件的 JSON 字段名(net/server.h:68-77),如 UpdateFileMask、SoftVer、FileLen、FileMD5Code、FMType/FWType |
LEN_REQ_FILE(1024) | 单次请求升级文件的最大字节数(net/server.h:143) |
ERRCODE_FIFO_SIZE(10) | APPErrorCode 数组中读取错误码的个数(fill_system_run_info 应答分支,data/fault_warn_log/fault_warn_log.h:44) |
BATTERY_MAX_NUM(12) | 电池最大台数(midware/bms/bms.h:18),本文件在 ErrBmsList/WarBmsList/BatMaxCapacity/fill_bms_self_info() 等数组长度处使用 |
NOW_BASE_YEAR | 年份基数,AFCI 日志时间戳计算使用 |
3. 函数清单
共 77 个函数定义(已用 grep -nE "^[A-Za-z_].*\)[ ]*$" net/ems.c 全量核对,文件内无遗漏)。
| 函数名 | 作用域(static/全局) | 说明(一句话) |
|---|---|---|
socket_abnormal_handle (:140) | 全局 | 套接字异常:清登录/建链标志与登录、队列失败计数,状态机与 g_stNetDataSend 复位为空闲 |
ems_tick_init_wireless (:163) | 全局 | 只重设各上报 tick(无线升级/链路抖动时抑制"超时断链"判定) |
tcp_init_ems (:191) | 全局 | 初始化全部 tick、登录计数并复位状态机,任务启动时调用一次 |
get_setup_config_status (:236) | 全局 | 读取 g_stEMSInfo.u8SetDevConfig |
get_setup_config_Extend1_status (:252) | 全局 | 读取 u8SetDevConfigExtend1 |
get_setup_config_Extend2_status (:268) | 全局 | 读取 u8SetDevConfigExtend2 |
Set_Setup_Config_Status (:284) | 全局 | 置位配置已发送标志并同步 u64TickSendConfigData |
Set_Setup_Config_Extend1_Status (:304) | 全局 | 扩展配置 1 的同类标志设置 |
Set_Setup_Config_Extend2_Status (:324) | 全局 | 扩展配置 2 的同类标志设置 |
fill_common_data (:333) | static | 唯一的封帧函数:写设备域/方向/命令字/长度域并追加 Modbus CRC16 |
fill_login_data (:360) | 全局 | 组登录帧(SN + 公司名 + 密码;私密模式用 MD5 动态密码) |
fill_heart_beat_data (:415) | 全局 | 组心跳帧(无有效载荷) |
fill_system_run_info (:439) | 全局 | 组主运行数据帧 0x05/0x44;含电网、逆变器、电池、系统状态、能量统计、调试量、错误码 |
fill_system_grid_ct_sampling_info (:878) | 全局 | 组 CT 采样曲线帧 0x1A(25 包 × 2 通道 × 200 点) |
fill_system_run_info_extend1 (:946) | 全局 | 组扩展运行数据帧 0x16/0x4E;含充电桩、备电盒、电池批次/SOH/温度、MID 电表、欧盟 14a/RRCR/WLV、A/B 簇 |
fill_system_energy_data (:1270) | 全局 | 组能量数据帧 0x1D(PV/电网/电池/充电桩能量与电压频率) |
fill_system_second_data (:1358) | 全局 | 组秒级实时数据帧 0x10 |
fill_system_debug_run_data (:1510) | 全局 | 组调试运行数据帧 0x23(电表三相 + 调度/MD 信息) |
fill_send_gen_run_data (:1577) | 全局 | 组油机运行数据帧 0x36(油位、电压电流、功率、告警数组) |
fill_system_upgrade_data (:1638) | 全局 | 组升级进度帧 0x24(指令 ID、设备码、状态、原因、文件偏移) |
fill_send_config_data (:1676) | 全局 | 组主配置帧 0x04/0x43(容量、型号、版本、时控段、油机、备电盒、并机、Modbus、IP、无线模块) |
fill_send_config_data_extend1 (:1931) | 全局 | 组扩展配置 1 帧 0x15/0x4D(充电桩、28 组时控表、馈网时段、SOC 校准、EPO、设备功能位) |
fill_send_config_data_extend2 (:2181) | 全局 | 组扩展配置 2 帧 0x61(削峰填谷、CSIP/南澳、GFCI/AFCI、MID 电表、并机通信方式) |
fill_read_config_data (:2307) | 全局 | 组"读取配置"请求帧(载荷只有 SN),命令字由入参决定 |
fill_system_cell_run_data (:2335) | 全局 | 组单体数据帧 0x55(各电池单体温度/电压数组、总压总流、能量、工作状态) |
obtain_event_log_data (:2414) | 全局 | 非升级状态下读取事件日志内容(本文件内无调用点,见第 7 节) |
fill_send_info (:2429) | 全局 | 组即时消息帧 0x09(重启原因、断链、升级完成、EventLog、EventLog 各状态位) |
fill_send_afci_log (:2672) | 全局 | 组 AFCI 日志分包上传帧 0x62 |
set_info_status (:2722) | 全局 | 事件掩码按位或置位 |
clear_info_status (:2734) | 全局 | 事件掩码与运算清除(调用方需传取反掩码) |
get_info_status (:2746) | 全局 | 读取事件掩码 |
handle_response_special_cmd (:2758) | 全局 | 按状态选命令名称字符串并组 0x08 特殊指令统一成功应答 |
Handle_Response_Resume (:2824) | 全局 | 组断点续传应答帧(Resume + CmdIndex) |
handle_response_force_update (:2851) | 全局 | 组强制升级请求应答(占用中回 Error),并清对应设备的强制升级标志 |
fill_bat_log_ack (:2903) | 全局(#if 0 内,不编译) | 电池日志应答帧(旧实现,已被条件编译屏蔽) |
request_update_file_info (:2945) | 全局 | 组升级校验请求帧 0x20(掩码、软/硬件版本、MD5、型号) |
request_update_file_content (:3059) | 全局 | 组升级文件分片请求帧 0x21(offset + 不超过 1024 字节) |
respond_set_config_data (:3151) | 全局 | 组 0x42 设置配置成功应答 |
respond_set_config_data_extend1 (:3173) | 全局 | 组 0x4C 设置扩展配置 1 成功应答 |
fill_freq_dispatch_data_time (:3188) | 全局 | 把调频缓存的时间戳拆成 7 字节数组 |
fill_system_fcas_data (:3210) | 全局 | 组 FCAS 数据帧 0x11(10 组 × 10 点) |
fill_system_fast_fcas_data (:3259) | 全局 | 组快/极快 FCAS 帧 0x1B/0x19(5 包 / 20 包) |
fill_system_dispatch_data (:3423) | 全局 | 组调度数据帧 0x1C(包数由采集间隔推算) |
fill_system_ffr_data_100ms (:3589) | 全局 | 组 FFR 数据帧 0x12(100 点功率 + 频率) |
fill_burnin_data (:3653) | 全局 | 组老化测试结果帧 0x18 |
send_self_test_data (:3682) | 全局 | 组自检/安规测试数据帧 0x45/0x47(OVP/UVP/OFP/UFP 各阶参数与实际值) |
fill_set_safety_config_data (:3827) | 全局 | 组"设置安规配置"帧(载荷只有 SN) |
fill_send_safety_config_data (:3855) | 全局 | 组安规配置帧 0x4B/0x58(电压/频率保护、GFCI、孤岛、P-V/Q-V 曲线、FRT 等) |
fill_bms_self_info (:3997) | 全局 | 组电池详细信息帧 0xA3(欧盟电池法,约 20 个数组字段) |
fill_channel_data (:4190) | 全局(#if INOVANCE 内,当前不编译) | 把一个定长采样通道数组拼成 JSON 数组 |
fill_send_fault_record_data (:4220) | 全局(#if INOVANCE 内,当前不编译) | 组故障录波分包帧 0x56 |
fault_scord_init (:4315) | 全局(#if INOVANCE 内,当前不编译) | 录波分包总数置 16、序号清零 |
set_fault_scord_status (:4322) | 全局(#if INOVANCE 内,当前不编译) | 录波分包序号自增并置发送标志 |
fill_bat_log_data_send (:4348) | 全局(#if 0 内,不编译) | 电池日志上传帧(旧实现) |
fill_mix_bat_data (:4420) | 全局 | 组电池混搭信息帧 0xA6(型号/容量/DOD 串 + BatBrand 12 元素数组) |
ems_get_upload_interval (:4491) | 全局 | 取 0xA3 上传周期(缺省 600 s;为 0 时用随机数兜底,禁止返回 0) |
check_idle_process (:4521) | 全局 | 空闲态派发器:按优先级轮询所有条件,返回下一个要执行的状态 |
get_state_tcp_protocol (:5331) | 全局 | 读状态机当前状态 |
changed_state_tcp_protocol (:5343) | 全局 | 写状态机状态(≥STATUS_MAX 时回落到 STATUS_IDLE) |
handle_message_queue (:5362) | 全局 | 把报文投递到 g_pEthernetQ 消息队列,处理队满与投递失败 |
ems_time_event (:5390) | 全局 | 状态机主执行体,switch 处理全部 ENStatus 分支 |
app_connect_detect (:6280) | 全局 | APP 通道 60 s 无活动即断开;无线连接超 60 s 刷新上报 tick |
get_fcas_data (:6313) | 全局 | 按 1 s/200 ms/50 ms 采样 FCAS/Fast/VeryFast 到缓存数组 |
get_dispatch_data (:6485) | 全局 | 按云端下发的采集间隔采样调度缓存 |
get_ffr_data (:6568) | 全局 | 每 100 ms 采样一次 FFR 缓存 |
get_grid_ct_sampling_data (:6620) | 全局 | 把 CT 采样曲线就绪标志转换为发送标志 |
send_debug_run_info_data (:6642) | 全局 | 调试数据上传的使能/超时管理(到期自动关闭并清配置结构) |
send_upgrade_info_data (:6675) | 全局 | 升级过程状态机(填 DEV 码/指令 ID/文件偏移,推进 UPGRADE_*) |
reply_upload_can_log (:6780) | 全局 | 回复云端 UploadCanLog 指令(由 net/server.c 调用) |
can_log_format_timestamp (:6810) | static | 把 Unix 时间戳格式化成 YYMMDDHHMMSS |
can_log_byte_to_hex (:6832) | static | 把 8 字节 CAN 数据转成 16 位大写 HEX 字符串(不足补 00) |
check_can_log_upload_task (:6849) | static | CAN 日志采集/上报两态轮询(25 ms 节拍,单次最多 60 帧、5 ms) |
is_can_log_upload_ready (:7029) | 全局 | 查询是否还有未上报的 CAN 日志分片 |
can_log_calc_total_pack (:7054) | 全局 | 按 ID 独立分包计算总包数 |
fill_send_can_log_data (:7072) | 全局 | 打包当前 CAN 日志分片为 JSON 帧 0x63 |
test_can_log_upload_custom (:7204) | 全局 | CAN 日志采集的自定义测试入口(只执行一次) |
task_net_app (:7250) | 全局 | 网络应用任务主体(uCOS-II 任务,25 ms 循环) |
4. 核心逻辑解析
4.1 通信帧格式与封帧(fill_common_data,:333)
所有上行报文由 fill_common_data() 统一封帧,布局如下(偏移从 0 起):
| 偏移 | 长度 | 内容 | 说明 |
|---|---|---|---|
| 0 | 1 | 设备域 = EMS(0x01) | 固定 |
| 1 | 1 | 方向域 = REQUEST_COMM(0x01) / RESPONSE_COMM(0x02) | 上报用 REQUEST,对方主动指令的应答用 RESPONSE |
| 2 | 1 | 命令字(ENCmdType) | 由调用方的 fun/type 参数传入 |
| 3-4 | 2 | 0x00 0x00 | 保留 |
| 5-6 | 2 | 数据域长度(大端)= 报文总长 − 7 | sendBuff[5] = (l_u16Len-7)>>8,[6] = (l_u16Len-7)&0xFF(:340-341) |
| 7 … | N | JSON 明文文本 | 各 fill_* 从 l_u16Len = 7 起用 snprintf 追加 |
| N+1, N+2 | 2 | Modbus CRC16(大端) | GetCrcCode_Modbus(sendBuff, l_u16Len),覆盖范围为偏移 0 起 l_u16Len 字节(common/crc.c) |
约定:l_u16Len 变量表示"JSON 已用到的末尾偏移(含 7 字节帧头)";封帧后发送长度 g_u16DataSendLen = l_u16Len + 2。各 fill_* 的典型写法是每段 snprintf 后带一个尾逗号,最后一段前用 l_u16Len -= 1 退掉多余逗号再补 "}"。缓冲容量参数统一取 sizeof(g_au8NetSendPack) - 2(5118),调频类的 fill_* 则改用 sizeof(g_t_freq_dispatch_data.u8_send_buff) - 2 并把 JSON 写进 u8_send_buff。
超时与重试机制不在本文件用超时/重发计数器实现,而是靠两条机制:一是应用层的 if(OSTimeGet() - tick >= 周期) 周期节拍(见 4.10);二是队列侧的 NET_DATA_SEND_TRY(50) 上限——同一报文在 g_stNetDataSend.u8Type 未被以太网任务清零时最多保留 50 个任务周期,之后由 check_idle_process() 强制丢弃并发下一包。
4.2 发送通道:消息队列与以太网任务协作
handle_message_queue(type, info, len)(:5362)把 type/指针/长度 写入全局 g_stNetDataSend(midware/ethernt/ethernet.c:34 定义,STEthernetData 含 u8lock 字段),然后 OSQPost(g_pEthernetQ, &g_stNetDataSend):
- 投递成功 →
g_stEMSInfo.u16OSQDealFailCnt = 0; - 队满(
OS_ERR_Q_FULL)→OSQFlush(g_pEthernetQ)清队列(丢历史包,保最新包); - 其它错误 →
u16OSQDealFailCnt++(该计数在midware/ethernt/ethernet.c与我们:49的OSQ_DEAL_FAIL门限配合触发事件日志)。
发送完毕由以太网任务把 g_stNetDataSend.u8Type = STATUS_NONE、u8lock = FALSE(ethernet.c:574/615/788/859)。因此 ems_time_event() 开头有 if(g_stNetDataSend.u8lock == TRUE) return;(:5395)——发送期间整个状态机停摆一个周期,避免组包缓冲被并发改写。
例外:CAN 日志上报分支(:6228-6255)不走队列,直接调用 send(SOCK_TCP_CLIENT, ...) 发原始 socket,并自行按 10 s 间隔节流。
4.3 主状态机(ENStatus)与 ems_time_event() 的对应关系
ems_time_event()(:5390)是一个巨型 switch(get_state_tcp_protocol());绝大多数分支的执行模式固定为:组包 → handle_message_queue() → changed_state_tcp_protocol(STATUS_IDLE)。状态之间的迁移由 check_idle_process() 在 STATUS_IDLE 时决定(一次性触发;回到 IDLE 后下一周期再由派发器选下一个动作,因此 25 ms 周期内最多发一包)。核心映射如下:
| 状态 | 组包/动作 | 命令字 | ||
|---|---|---|---|---|
STATUS_LOGIN | fill_login_data()(并 u8LogInCnt++) | 0x02 | ||
STATUS_HEART_BEART | fill_heart_beat_data() | 0x00 | ||
STATUS_SEND_DATA_TO_SERVER | fill_system_run_info(CMD_SEND_DAT_SYS_RUN) | 0x05 | ||
STATUS_SEND_DATA_TO_SERVER_EXTEND1 | fill_system_run_info_extend1(CMD_SEND_DAT_SYS_RUN_EXTEND1) | 0x16 | ||
STATUS_SEND_SECOND_DATA | fill_system_second_data() | 0x10 | ||
STATUS_SEND_ENERGY_DATA | fill_system_energy_data() | 0x1D | ||
STATUS_SEND_BMS_MIN_DATA | fill_system_cell_run_data() | 0x55 | ||
STATUS_SEND_BMS_SELF_INFO | fill_bms_self_info() | 0xA3 | ||
STATUS_SEND_MIXBAT_INFO | fill_mix_bat_data() | 0xA6 | ||
STATUS_SEND_GENSET_DATA | fill_send_gen_run_data() | 0x36 | ||
STATUS_SEND_BURNIN_DATA | fill_burnin_data() | 0x18 | ||
STATUS_SEND_DEBUG_RUN_INFO | fill_system_debug_run_data() | 0x23 | ||
STATUS_SEND_UPGRADE_INFO | fill_system_upgrade_data() | 0x24 | ||
STATUS_SEND_FCAS_DATA / STATUS_SEND_FAST_FCAS_DATA / STATUS_SEND_VERY_FAST_FCAS_DATA / STATUS_SEND_FFR_DATA / STATUE_SEND_DISPATCH_DATA | fill_system_fcas_data() / `fill_system_fast_fcas_data(0x1B | 0x19) / fill_system_ffr_data_100ms() / fill_system_dispatch_data()` | 0x11 / 0x1B / 0x19 / 0x12 / 0x1C | |
STATUS_SEND_GRID_CT_SAMPLING_DATE | fill_system_grid_ct_sampling_info() | 0x1A | ||
STATUS_SEND_CONFIG / _EXTEND1 / _EXTEND2 | fill_send_config_data(CMD_SEND_CONFIG_INFO) 等 | 0x04 / 0x15 / 0x61 | ||
STATUS_READ_CONFIG / _EXTEND1 / _EXTEND2 | `fill_read_config_data(0x03 | 0x14 | 0x60)` | 0x03 / 0x14 / 0x60 |
STATUS_SET_CONFIG / STATUS_SET_CONFIG_EXTEND1 | respond_set_config_data() / respond_set_config_data_extend1() | 0x42 / 0x4C | ||
STATUS_GET_CONFIG / STATUS_GET_CONFIG_EXTEND1 | fill_send_config_data(CMD_GET_CONFIG) / fill_send_config_data_extend1(CMD_GET_CONFIG_EXTEND1) | 0x43 / 0x4D | ||
STATUS_SEND_RUN_INFO / _EXTEND1 | fill_system_run_info(CMD_SEND_RUN_INFO) / fill_system_run_info_extend1(CMD_SEND_RUN_INFO_EXTEND1) | 0x44 / 0x4E | ||
STATUS_SEND_SAFETY_INFO / STATUS_SEND_SELF_TEST_INFO | `send_self_test_data(0x47 | 0x45)` | 0x47 / 0x45 | |
STATUS_SEND_SAFETY_CONFIG_INFO / STATUS_SEND_SAFETY_CONFIG | `fill_send_safety_config_data(0x4B | 0x58)` | 0x4B / 0x58 | |
STATUS_SET_SAFETY_CONFIG_INFO / STATUS_SET_SAFETY_CONFIG | `fill_set_safety_config_data(0x4A | 0x57)` | 0x4A / 0x57 | |
STATUS_UPDATE_CHECK / STATUS_GET_UPDATE_FILE | request_update_file_info() / request_update_file_content() | 0x20 / 0x21 | ||
STATUS_SEND_INFO | fill_send_info() | 0x09 | ||
STATUS_SEND_ERROR_LOG | 直接取 g_t_error_log_tab.u8_error_log_buff(长度 > 7 才发) | —— | ||
STATUS_SEND_AFCI_LOG_DATA | fill_send_afci_log() | 0x62 | ||
STATUS_SEND_RESUME_TO_SERVER | 直接发 resume_buff_sdram(长度 g_u32_resume_data_len),随后置 RESUME_STS_Over | —— | ||
十余个 STATUS_RESPSONED_*(重启/设配置/远控/对时/调度/参数/功率因数/充电桩使能/域名/G100 重启/停机/充电/能量/自检/激活时间/SOC 校准/扩展域名/上传周期/电芯品牌/CAN 下发) | handle_response_special_cmd(状态)(部分先清对应 g_u8*Flag) | 0x08 | ||
STATUS_RESPSONED_FORCE_UPDATE | handle_response_force_update() | 0x08 | ||
STATUS_RESPSONED_RESUME | Handle_Response_Resume()(先清 g_u8_resp_resume_flag) | 0x08 | ||
STATUS_SEND_CAN_LOG_DATA | fill_send_can_log_data() + 直接 send() | 0x63 | ||
STATUS_SEND_FAULT_RECORD | fill_send_fault_record_data()(#if INOVANCE,当前不编译) | 0x56 | ||
STATUS_SET_BAT_LOG_CMD / STATUS_SEND_BAT_LOG_DATA | 位于 #if 0(:6157-6180),不编译 | —— | ||
default | 直接回落 STATUS_IDLE | —— |
两个分支带"硬复位"副作用:STATUS_RESPSONED_RESTART(:5575)与 STATUS_RESPSONED_G100_REBOOT(:5691)在发完应答后 OSTimeDlyHMSM(0,0,5,0) 延时 5 s 再 system_hard_reset()。STATUS_RESPSONED_SET_CONFIG(:5589)除了应答,还会一次性把 g_u8RequestConfigFlag、g_u8RequestConfigFlagExtend11/12、g_u8appConfigFlag* 全部置真并刷新三个请求 tick,形成"配置被改 → 立即回读配置"的闭环。
4.4 check_idle_process():派发优先级与周期表(:4521)
这是本文件最核心的调度逻辑,全部写成一个 if-else 阶梯,先命中先返回。三层结构:
- 队列忙等(
:4529):g_stNetDataSend.u8Type != STATUS_NONE时只做计数,累计 50 次(NET_DATA_SEND_TRY)后把u8Type强制清为STATUS_NONE并返回 IDLE(相当于发送超时丢包)。 - 建链但未登录(
:4547):bIsLogin==FALSE && bIsBuiltSocket==TRUE && APP 未连接→ 每 10 s(SENCOND_10S)返回一次STATUS_LOGIN。 - 已登录(
:4562):- 先处理"配置回读":
(g_u8RequestConfigFlag && g_u8appConfigFlag)每 5 s 发一次STATUS_READ_CONFIG;扩展 1 每 10 s;扩展 2 每 15 s。 - 然后进入大门闸
if((g_u8RequestConfigFlag == FALSE) || (g_u8appConfigFlag == FALSE))(:4601)——只要还处在"等待回读配置"状态,下面的全部业务上报都被跳过。门内的检查顺序(即优先级)为:
- 先处理"配置回读":
| 顺序 | 条件 | 触发周期 | 目标状态 |
|---|---|---|---|
| 1 | 未在下载(IsSystemDownload()==FALSE)且 get_setup_config_status()==FALSE | 10 s | STATUS_SEND_CONFIG(0x04) |
| 2 | 扩展 1 同条件 | 10 s | STATUS_SEND_CONFIG_EXTEND1 |
| 3 | 扩展 2 同条件 | 10 s | STATUS_SEND_CONFIG_EXTEND2 |
| 4 | check_need_response_updt_devcie(&s_u8DevType) | 事件 | STATUS_RESPSONED_FORCE_UPDATE |
| 5 | get_flag_modbus_force_update()==FALSE 时:is_goto_update_check_status() | 5 s | STATUS_UPDATE_CHECK(0x20) |
| 6 | is_goto_get_update_file_status()(需先取得 g_pUpdateSem,OSSemPend 3 s 超时) | 100 ms | STATUS_GET_UPDATE_FILE(0x21) |
| 7 | g_u8RestartFlag / g_u8SetConfigFlag / g_u8RemoteControlFlag / g_u8SyncFlag / g_u8DispatchFlag / g_u8ExtraFlag / g_u8_powerfactor_flag / g_u8_evchargeen_flag / g_u8_request_safetyconfig_flag / g_u8_sys_shutdownen_flag / g_u8_set_charge_flag / g_u8_send_energy_data_flag / g_u8_sys_self_test_flag / g_u8_icdcupdatet_flag / g_u8_g100_reboot_flag(需 G100 处于 STATE1_NORMAL) / g_u8_set_bms_activate_time_flag / g_u8_soc_calibration_flag / g_u8_expand_icdcupdatet_flag / g_u8_resp_set_upload_interval / g_u8_resp_set_bat_brand | 事件(标志) | 对应的 STATUS_RESPSONED_* / STATUS_SEND_* |
| 8 | g_u8_resp_resume_flag / Get_Resume_Status_Current()==RESUME_STS_Sending / ==RESUME_STS_Over | 事件 / 5 s | STATUS_RESPSONED_RESUME / STATUS_SEND_RESUME_TO_SERVER |
| 9 | g_u8_bat_log_flag(g_u8_bat_log_send_flag 为空分支,无动作) | 事件 | STATUS_SET_BAT_LOG_CMD(对应 case 在 #if 0 内) |
| 10 | g_u8_can_send_done_flag | 事件 | STATUS_RESPSONED_CAN_SEND |
| 11 | is_can_log_upload_ready() | 事件 | STATUS_SEND_CAN_LOG_DATA |
| 12 | get_info_status() 命中 22 种事件位之一 | 10 s | STATUS_SEND_INFO |
| 13 | AFCI 日志发送标志 + 回读成功 + 非升级中 + 可强制升级 + 无续传 | 事件 | STATUS_SEND_AFCI_LOG_DATA |
| 14 | 运行数据:老化模式 60 s,其它模式 300 s | 60/300 s | STATUS_SEND_DATA_TO_SERVER |
| 15 | 扩展运行数据(同周期,time_ticksenddataextend1) | 60/300 s | STATUS_SEND_DATA_TO_SERVER_EXTEND1 |
| 16 | 油机数据(需 genset_enable_flag) | 300 s | STATUS_SEND_GENSET_DATA |
| 17 | 心跳 | 60 s(SENCOND_60S) | STATUS_HEART_BEART |
| 18 | 调度/FCAS/FastFCAS/VeryFastFCAS/FFR/CT 采样(均需 get_setup_config_status() 为真) | 标志 | STATUE_SEND_DISPATCH_DATA 等 |
| 19 | 老化数据(aging_mode_send_data_flag()) | 10 s | STATUS_SEND_BURNIN_DATA |
| 20 | 升级信息(u8_send_data_flag 或 g_u8_send_upgrade_info_flag) | 3 s | STATUS_SEND_UPGRADE_INFO |
| 21 | 调试数据(u64TickSendDebugRunData) | u32_debug_run_info_time_interval | STATUS_SEND_DEBUG_RUN_INFO |
| 22 | 能量数据(get_energy_data_enable()) | 60 s | STATUS_SEND_ENERGY_DATA |
| 23 | 秒级数据(u64TickSecData) | 10 s | STATUS_SEND_SECOND_DATA |
| 24 | get_inverter_safty_test_info_send_server_flag() | 事件 | STATUS_SEND_SAFETY_INFO |
| 25 | 错误日志(g_t_error_log_tab.u8_error_log_send_flag) | 10 s | STATUS_SEND_ERROR_LOG |
| 26 | 安规配置上传 / 设置(u8_set_safety_config_flag) | 10 s | STATUS_SEND_SAFETY_CONFIG_INFO / STATUS_SET_SAFETY_CONFIG_INFO |
| 27 | 云 BMS 单体数据 | 60 s | STATUS_SEND_BMS_MIN_DATA |
| 28 | 电池详细信息(欧盟电池法) | 硬编码 10 min(:5202) | STATUS_SEND_BMS_SELF_INFO |
| 29 | bms_self_info_clear_flag(电池信息被清空) | 事件 | STATUS_SEND_BMS_SELF_INFO |
| 30 | 故障录波(#if INOVANCE,当前不编译) | 2 s | STATUS_SEND_FAULT_RECORD |
| 31 | 混搭信息 | 硬编码 600000 ms(:5242) | STATUS_SEND_MIXBAT_INFO |
随后是 APP/局域网通道(:5255):g_t_app_info.u8_app_connect_flag == TRUE 时,按 u8_set_cfg/u8_get_cfg/u8_send_run_info/u8_self_test_data/u8_set_cfg_extend1/u8_get_cfg_extend1/u8_send_run_info_extend1/u8_set_safety/u8_get_safety 九个一次性标志派发 STATUS_SET_CONFIG/STATUS_GET_CONFIG/STATUS_SEND_RUN_INFO/STATUS_SEND_SELF_TEST_INFO/…,并逐个把标志清零。
4.5 上报报文的内容与打包要点
- 主运行数据
fill_system_run_info()(:439):字段体量最大的一帧,按电网电表(meter_read_*)→ PV/DC 电表 → 逆变器(PV/并网/备份/电池)→ BMS(总压总流 SOC、8 路支路电流/SOC、限功率、单体极值)→ 系统状态(Fault/Protection/Normal三态由ems_read_fault_code()、inverter_read_fault_code[_extend/extend2]()、bms_read_fault_code()、bms_read_protection_code()决定,:664-669)→ 能量统计(PV/逆变器/电网/电池/油机/充电桩)→ 调试量(PcsPara1..12)顺序拼接。fun参数决定方向:等于CMD_SEND_DAT_SYS_RUN时是主动上报(REQUEST_COMM,附加 Eirp/CSQ、调度信息、硬编码的 UVP/OVP/OFP 保护定值和PcsPara*);其它(如CMD_SEND_RUN_INFO、CMD_GET_CONFIG)走RESPONSE_COMM且以"Status":"Success"开头,并以APPErrorCode数组(get_show_errcode(i),ERRCODE_FIFO_SIZE个)结尾(:847-853)。 - BMS 错误码在
ErrBmsList/WarBmsList中按g_t_bat_info.t_run_info.u8_num(上限BATTERY_MAX_NUM)逐电池输出{"id":n,"code":v},末尾用"检查最后一个字符是否为逗号再回退"的方式去尾(:690-693、:711-714)。 - 扩展运行数据
fill_system_run_info_extend1()(:946):充电桩数据在get_evc_enable_flag()为真时输出单路EV1*,否则按MAX_EVC_II_NUM循环输出EVn*(要求对应g_t_evc_ii_single_config[i].u8_enable);其余为备电盒UBB*、GenConnectStatus/OperationalModeStatus(由inverter_read_run_mode()与dercontrol/opmodgenlimw推导)、12 个电池的BatCycle/SOH/BatTempLow|High/BatRunHrs、欧盟 14a(EnWGCTRL/RRCRCTRL/RRCRRATE,取u8_rrcr_en→u8_rrcr_feedin_percent,或EZA_ENABLE→u8_eza_feedin_percent)、WlvCtrl/WlvRate;MID 电表段仅在g_t_meter.t_config_data.u8_mid_meter_function_enable == TRUE时输出;BatMaxCapacity用"先按实际电池数输出、再补 0 至BATTERY_MAX_NUM"的写法保证定长(:1192-1207);A/B 簇信息仅在bms_is_a_cluster_valid()/bms_is_b_cluster_valid()为真时输出 6 个{"id","status"}。 - 配置帧:
fill_send_config_data()(:1676)可分两块——公共服务/APP 都有的字段(容量、型号、版本、时控段 1/2 的起止分钟数、GridCharge/CtrDis布尔、备电盒优先级与 SOC 限值、安规类型、IP/子网/网关、Modbus 地址波特率、并机、AFCI 等);仅当fun == CMD_SEND_CONFIG_INFO时追加干接点 1/2 时段、网络类型字符串(ALPHA_LINKER_WIFI/E_LINKER_WIFI/E_LINKER_4G/NONE,若 W5500 同时在线则strcat追加" & W5500")与 4G ICCID(:1877-1903)。fill_send_config_data_extend1()(:1931)同理分CMD_SET_CONFIG_DEV_EXTEND1块:充电桩配置与两台EVCn28 元组、SystemFunction1/2位域、并网功率限制、干接点馈电模式、TIMEChaDis1..28(14 组来自t_config_extend1_data、14 组来自t_config_extend2_data)、6 组电池强制馈网时段、电池激活时间(年份 <2000 时输出空位)、SOC 校准、EPO、EnWGEnable。fill_send_config_data_extend2()(:2181)输出削峰填谷 11 元组、CSIP 15 元组、GFCI/AFCI/MID 电表使能与版本、并机通信方式等。 - 升级相关:
request_update_file_info()(:2945)按get_current_point_file_mask()选择设备分支(FILE_MASK_EMS/INV1/INV2/INV3/BMS/电表/EVC/AFCI)填HardVer、FileMD5Code、FWType(型号字符串)与UpdtReversed;request_update_file_content()(:3059)每次请求min(剩余, LEN_REQ_FILE=1024)字节,并把当前升级表的as8MD5Code一并带上(:3112)。两者在指针为空或长度已用尽时返回FALSE,ems_time_event()用返回值决定是否入队。 - 调频/调度帧:
fill_system_fcas_data()固定 10 组(每组 10 点,逐点PInv/Fac/Pmeter/Ppv/PmeterDC/PvarMeter/Pbat/FMFlag);fill_system_fast_fcas_data(cmd)按cmd取 5 包(0x1B)或 20 包(0x19),输出 8 个数组;fill_system_dispatch_data()的包数cnt = 10000 / u32_dispatch_data_interval / 10(:3433)——与get_dispatch_data()的填数组上限s_u8_index >= (10*1000)/interval(:6540)配对,且interval下限被钳到 50(:6509-6512);fill_system_ffr_data_100ms()固定 10 组、频率按两位小数输出。
4.6 登录与保活
tcp_init_ems()(:191)在任务启动时把所有 tick 对齐到当前OSTimeGet(),把状态机与g_stNetDataSend复位为空闲,并清u8LogInCnt。fill_login_data()(:360)构造{"UserName":SN,"CompanyName":...,"Password":...}:公司名/密码优先级为g_system_custom_info.u8_login_flag自定义值 > OEM 为OEM_ZONERGYESS时写死zonergyess/Alpha0923> 默认AlphaESS/Alpha0912;当get_server_data_encryption_flag() == NET_PRIVATE_ENCRYPTED时改用动态密码MD5("ALPHA"+SN)取摘要第 12~15 字节格式化成 8 位大写 HEX。- 保活链路:登录 → 心跳(
:5038,周期 60 s)→ 运行数据(60 s/300 s)。链路异常由midware/ethernt/ethernet.c依据u8LogInCnt >= LOGIN_NO_ACK_NUM、u16HeartRstCnt >= HEART_RST_FAIL、u8LogInNoAckRstCnt、u16OSQDealFailCnt等计数处理(门限宏来自net/ems.h),并调用本文件的socket_abnormal_handle()(:140)复位通信状态。
4.7 升级与断点续传流程
升级(net/upd_download.c 提供状态查询,本文件负责驱动):
check_need_response_updt_devcie(&s_u8DevType)为真 →STATUS_RESPSONED_FORCE_UPDATE→handle_response_force_update()(:2851):若get_updt_status_current() > UPDT_STS_IDLE或IsSystemUpgrade()为真,应答"Status":"Error",否则Success;应答里带CmdIndex(取g_stEMSInfo.stUpt[s_u8DevType].au8IDCMDUpdate,s_u8DevType >= UPDT_MAX时用空串),随后set_response_force_upd_status(s_u8DevType, FALSE)清标志。load_current_update_data()载入升级表(g_stEMSInfo.pstCurrUpt/stUpt[],也可由midware/interface/modbus/modbus.c:8779+经 Modbus 直接指定升级目标)。STATUS_UPDATE_CHECK(5 s 周期)→ 0x20 上报本机掩码与版本信息,云端回帧决定是否需要下载。STATUS_GET_UPDATE_FILE(100 ms 周期,REQUEST_UPDATE_INTERVAL)→ 0x21 分片请求。进入前OSSemPend(g_pUpdateSem, 3000, &err):拿到信号量后还要再判一次is_goto_get_update_file_status();OSSemPend失败累计 >10 次则置bIsLogin = FALSE(强制重新登录以续订文件)并OSSemPost归还信号量(:4686-4716)。每次请求前调用ems_tick_init_wireless(),避免无线升级期间被判超时或升级后数据包错乱。- 升级过程中的进度上报由
send_upgrade_info_data()(:6673,每 25 ms 调一次)驱动:按UPDT_EMS/INV1/INV3(ARM)/BMS/Meter(含 BACKUPBOX 分支)/EVC/AFCI顺序匹配,填u8_dev_code、u8_id_cmd_update、u32_upgrade_file_offset(优先取各设备自身记录的偏移);状态迁移为UPGRADE_NONE →(收到升级指令) DOWNLOAD_START →(不可升级⇒下载结束) DOWNLOAD_FINISH →(仍有升级⇒3 s 周期) UPGRADE_START →(结束且可升级) UPGRADE_FINISH,结束时按u16_upgrade_error位域选择原因字符串(success/WEB_ERROR/DOWNLOAD_ERROR/SYSTEM_UPS/MD5_ERROR/TIMEOUT/BOOT_ERROR);UPGRADE_FINISH且发送标志已清后整体清零g_t_upgrade_run_info。
断点续传(数据由 data/resume_data/flash/flash_data_store.c 维护):
g_u8_resp_resume_flag为真 → 清标志并回 0x08 应答(Handle_Response_Resume(),含get_resume_cmd_id_code())。Get_Resume_Status_Current() == RESUME_STS_Sending→ 直接把resume_buff_sdram(长度g_u32_resume_data_len)作为完整报文入队,然后置RESUME_STS_Over。== RESUME_STS_Over时每 5 s 重试一次:g_u8ReumeFailCnt > 10则清零resume_buff_sdram、g_u32_resume_data_len = 0、状态回RESUME_STS_Idle;否则重新置RESUME_STS_Sending再发一遍(:4881-4903)。- 续传期间常规运行数据被抑制:发送 0x05/0x16 的条件里都带
!is_goto_get_update_file_status() && Get_Resume_Status_Current()==RESUME_STS_NONE && get_resume_index_length()==FALSE。
4.8 调频/调度/FFR 数据采集(get_*_data())
这三个函数每 25 ms 被调用一次,把实时量写入 g_t_freq_dispatch_data 的并行数组,写满一个周期后置对应的 g_u8_send_*_flag,由状态机负责发送:
| 函数 | 采样周期 | 数组上限 | 采集量 |
|---|---|---|---|
get_fcas_data()(:6313)常规 FCAS | 1 s(get_rtc_tick() 秒差值) | 10 | s16_power、u16_freq(get_dispatch_running_freq())、s32_meter_power、s32_pv_power、s32_pv_meter_power、s32_reactpower、s16_batpower、u8_dispatch_flag |
| 同上,快 FCAS | 200 ms(OSTimeGet() 差值) | 50 | 同上,频率取 get_fast_dispatch_running_freq() |
| 同上,极快 FCAS | 50 ms | 200 | 同上 |
get_dispatch_data()(:6485) | g_t_freq_dispatch_data.u32_dispatch_data_interval(下限 50 ms) | 10000/interval | 同上,标志取 dispatch_regulation_flag();调度模式变化时索引归零重采 |
get_ffr_data()(:6568) | 100 ms | 100 | 只采 s16_power 与 u16_freq |
三者在"本轮采样完成(发送标志已置)"期间直接 return,避免覆盖未发出的数据;PV 功率按系统模式(DC_SYSTEM_MODE / AC / HYBIRD)计算:并网+混合模式取 inverter_read_pv_power(PV_CHANAL_ALL) + max(meter_read_actpower(PV_METER,PHASE_T), 0)。索引归零时把 g_t_freq_dispatch_data.t_datetime = SYSTEM_DATETIME 作为该批数据的时标。get_grid_ct_sampling_data()(:6620)只做标志搬运;send_debug_run_info_data()(:6642)用静态 send_data_enable_flag 保证使能后持续上传一段时间(u32_debug_run_info_time),到期清 g_u8_send_debug_run_info_flag 并 memset 整个 g_t_debug_run_info。
4.9 CAN 日志采集/上报两态机(需求 7)
控制块 s_can_log_ctrl 与缓存指针数组 s_can_log_raw_buffers[] 定义在 net/server.c:80/83,本文件负责采集与打包:
- 采集态(
check_can_log_upload_task(),:6849,25 ms 轮询,active==1):先查超时current_tick - start_tick > CAN_LOG_CAPTURE_TIMEOUT_MS或累计数达到total_count,满足即转上报态并把包序号、ID 索引、偏移全部归零;否则从标准帧队列get_can0_standard_pack()与扩展帧队列get_can0_exten_pack()(hardware/can/can0.c)取帧,一次最多MAX_READ_PER_CALL=60 帧、最多耗MAX_READ_TIME_MS=5 ms;帧 ID 与目标 ID 匹配时才停止取帧循环(不匹配的帧被丢弃并计数read_count);匹配到则写入s_can_log_raw_buffers[matched_idx]的第current_count[matched_idx]条(id/dlc/data/timestamp,时间戳由SYSTEM_DATETIME经mktime()生成)。 - 上报态(
active==2):is_can_log_upload_ready()(:7029)按"每个 ID 独立分包、每包CAN_LOG_PACK_COUNT_PER_PACK条"计算总包数total_pack = Σ ceil(count_i / 每包条数),只要current_pack_seq <= total_pack即返回真;ems_time_event()的STATUS_SEND_CAN_LOG_DATA分支(:6228)带 10 s 节流(静态s_last_send_tick),调fill_send_can_log_data()(:7072)把当前包定位到"第几个 ID 的第几包"(:7110-7121),逐条输出{"t":"YYMMDDHHMMSS","d":"16位HEX"},帧头带CmdIndex/TargetId/TotalCount/PackSeq/TotalPack;若采集超时且条数不足,尾部标记"Description":"Partial",否则"OK"。发送成功后current_pack_seq++;发送失败则调socket_abnormal_handle()并推进节流时间。上报完成后(current_pack_seq > total_pack)把active清 0 并清空各 ID 计数。 test_can_log_upload_custom(ids[], id_count, target_count)(:7204)是自测入口,参数越界即返回,成功后调start_can_log_capture(...,"CUSTOM",6)(实现在net/server.c:7219),并用静态s_test_done保证只跑一次。
4.10 时间基准与定时语义
本文件混用两套时间基准,改动超时逻辑时必须区分:
OSTimeGet():uCOS-II 系统 tick,OS_TICKS_PER_SEC = 1000(UCOSII/uCOS-CONFIG/os_cfg.h:51),1 tick = 1 ms。用于心跳、登录、配置、升级、事件上报、断点续传、老化、升级信息等所有SENCOND_*比较。get_rtc_tick():返回 RTC 换算出的 Unix 秒(midware/auxiliary/system_aux.c:373由mktime()生成),用于运行数据(60 s/300 s,:4986、:5004)、油机数据(300 s)、常规 FCAS(1 s)。- 反向跳变保护:几乎所有比较后面都跟一条
else if(tick > OSTimeGet()) { tick = OSTimeGet(); }(如:4556、:4571、:4613等),用于把因复位/RTC 校时而落到"未来"的 tick 拉回当前值。这类分支只修正一次,不是通用回绕处理。
task_net_app(p_arg)(:7250)任务体:先 tcp_init_ems() 与 set_info_status(SYSTEM_RESTART)(重启事件立即上报),随后 for(;;) 内固定顺序调用 ems_time_event() → app_connect_detect() → get_dispatch_data() → get_fcas_data() → get_ffr_data() → get_grid_ct_sampling_data() → send_debug_run_info_data() → send_upgrade_info_data() → can_send_task()(需求 3,实现在 midware/bms/bms.c:4320)→ check_can_log_upload_task(),然后刷新 g_stEMSInfo.u32EMSTaskTick = OSTimeGet() 并 OSTimeDlyHMSM(0,0,0,25) 睡 25 ms。u32EMSTaskTick 是任务存活的心跳:midware/ethernt/ethernet.c:1453 用它做"超过 60 s 未刷新 ⇒ printf("task_net_app is time out!")(ethernet.c:1456)"的监控,并在异常后把该值拉回当前时间。
4.11 关键全局变量的读写与触发条件
| 变量 | 写入方 | 读取/触发点 | |
|---|---|---|---|
g_u8RestartFlag 等 20 余个跨模块标志 | net/server.c(解析云端指令后置位)、data/、logic/、midware/ 各模块 | check_idle_process() 轮询;对应 case 中清零后再回 0x08 应答 | |
g_stEMSInfo.u8SetDevConfig/Extend1/Extend2 | Set_Setup_Config_Status() 等(被 data/system_config/system_config.c、hmi/key.c、各厂商逆变器通信模块调用,表示"本地配置已改,需重发") | 发配置的节流条件(get_setup_config_status()==FALSE 才发)与调度/FCAS/FFR 的上报使能条件 | |
| `g_stEMSInfo.time_ticksenddata[extend1 | gen]` | tcp_init_ems()、ems_tick_init_wireless()、发送后刷新 | 运行数据/油机数据的秒级节流 |
g_stEMSInfo.u64Tick* / u32Tick* | 各上报分支发送后刷新(例外:u64TickHeartRcv 由接收侧 midware/ethernt/ethernet.c:1383/1419 刷新) | 全部周期判定;同时被 midware/ethernt/ethernet.c 用于超时断链判断(u8LogInCnt >= LOGIN_NO_ACK_NUM、u64TickHeartRcv > SENCOND_180S / SENCOND_30M、u16OSQDealFailCnt >= 50、u32EMSTaskTick > SENCOND_60S) | |
g_stEMSInfo.u32InfoType | set_info_status()(data/data.c、logic/*、midware/bms/bms.c、midware/ethernt/ethernet.c、net/*_download.c 等调用) | check_idle_process() 第 12 优先级;fill_send_info() 按位生成消息体;net/server.c 按位清除 | |
g_stEMSInfo.pstCurrUpt / stUpt[] | net/upd_download.c 的 load_current_update_data()、midware/interface/modbus/modbus.c:8779+ | 升级校验/取文件与升级信息上报 | |
g_t_freq_dispatch_data | get_fcas_data()/get_dispatch_data()/get_ffr_data() 写数据与 t_datetime,各 fill_system_*_data() 写 u8_send_buff | 调频/调度/FFR 上报;索引 s_u8_index 为各函数内 static | |
g_t_debug_run_info | net/server.c 下发使能/时长;send_debug_run_info_data() 到期清零 | 调试数据上报节流 | |
g_t_upgrade_run_info | send_upgrade_info_data()、net/server.c、各 *_upgrade.c 写错误位 | 0x24 上报与状态迁移 | |
g_au8NetSendPack / g_u16DataSendLen | 全部 fill_* | handle_message_queue()(指针直接入队,发送期间靠 u8lock 保护) | |
g_t_fault_scord_data | midware/inverter/inovance/inovance_comm_usart.c、fault_scord_init()/set_fault_scord_status() | 0x56 录波上报(当前不编译) |
5. 对外接口
本文件导出的函数(在 net/ems.h 中有声明、供外部调用)
| 导出函数 | 外部调用方 |
|---|---|
task_net_app | user/main.c:255(OSTaskCreateExt) |
socket_abnormal_handle | midware/ethernt/ethernet.c |
set_info_status | data/data.c、logic/logic.c、logic/self_test.c、midware/afci/afci_comm.c、midware/bms/bms.c、midware/ethernt/ethernet.c、midware/inverter/inverter.c、midware/inverter/alphaess/alphaess_comm_usart.c、net/ems_download.c、net/server.c、net/upd_download.c |
get_info_status / clear_info_status | logic/logic.c、net/server.c |
Set_Setup_Config_Status | data/data.c、data/system_config/system_config.c、hmi/key.c、midware/bms/bms.c、midware/ethernt/wireless/wireless_driver.c、各厂商逆变器通信模块(alphaess/ginlong/goodwe/inovance/kelong/set)、midware/meter/meter.c |
Set_Setup_Config_Extend1_Status | data/data.c、data/system_config/system_config.c、hmi/key.c、logic/logic.c、midware/auxiliary/system_aux.c、midware/bms/alpha/battery_comm.c、midware/bms/bms.c、midware/evc/evc.c、midware/evc/evc_ii.c |
Set_Setup_Config_Extend2_Status | data/system_config/system_config.c、logic/csip_aus.c、logic/logic.c、midware/interface/modbus/modbus.c、midware/inverter/alphaess/alphaess_comm_usart.c、midware/meter/meter.c |
fault_scord_init / set_fault_scord_status | midware/inverter/inovance/inovance_comm_usart.c、net/server.c(注意:定义在 #if INOVANCE 内) |
reply_upload_can_log | net/server.c(收到 UploadCanLog 指令时回复) |
fill_send_can_log_data / is_can_log_upload_ready | 声明在 net/ems.h,实际由本文件内部使用 |
tcp_init_ems | 声明在 net/ems.h,实际由本文件 task_net_app() 调用 |
其它未在头文件声明的全局函数(get_setup_config_*、fill_*、handle_response_*、request_update_*、respond_*、get_*_data、ems_time_event、app_connect_detect、send_*_data、can_log_*、ems_get_upload_interval、obtain_event_log_data)经全仓检索只在本文件内被调用,属"事实上的文件内接口",但未加 static(详见第 7 节)。
本文件导出的全局变量:g_stEMSInfo、g_u8RestartFlag 等全部跨模块标志(net/ems.h:467-506 声明;g_stEMSInfo 被 data/fault_warn_log、data/resume_data/flash、hmi/led/app_led.c、midware/interface/modbus、midware/ethernt/ethernet.c、net/server.c 等广泛使用)。
本文件调用的外部函数/变量(按来源模块)
- 时间与系统:
system_aux.h→get_rtc_tick()、SYSTEM_DATETIME、system_hard_reset()、get_system_sn()、get_ems_hard_type/version()、get_ems_boot_ver()、get_server_data_encryption_flag()、IsSystemUpgrade()、IsSystemDownload()、g_system_custom_info、g_t_system_base_info、g_system_function_info、g_u8_rtc_debug_data、pstRest/g_t_restart_time、pstRest - 电表:
meter_read_voltage/current/frequent/actpower/reactpower/apparent/einput/eoutput、meter_read_meter_sn/software_version/hard_version、meter_get_meter_modle、meter_get_meter_or_ct、meter_get_meter_ct_rate/ct_enable、meter_read_bb_sn/bb_software_version、get_average_power_of_gridmeter()、g_t_meter - 逆变器:
inverter_read_*(PV/并网/备份/电池/温度/功率因数/运行模式/告警/故障/版本/SN/额定功率/能量)、inverter_get_phase_number()、inverter_get_parallel_mode()、inverter_read_afci()、inverter_read_n_n_short()、inverter_get_backup_freq_basic()、get_inverter_type()、g_t_inv_info(含t_safty_info/t_safty_tset_info/t_run_info/t_config_data/t_afci_info/t_debug_log) - BMS:
bms_read_total_volt/cur/soc/power、bms_read_single_*(cur/soc/soh/cycle/temper/runhrs/max_capacity)、bms_read_status_flag/work_status/relay_status/shut_down/num、bms_read_max_charge_power/max_dischg_power、bms_read_fault_code[_by_id]、bms_is_fault_code_valid、bms_read_protection_code[_extend2_code]、bms_read_warning_code_by_id、bms_is_warning_code_valid、bms_read_battery_model/cell_model、bms_get_store_ver/store_sn/platform/ready_enable/self_lock_enable、bms_read_hard_version、bms_read_a_cluster_*/bms_read_b_cluster_*/bms_is_a|b_cluster_valid、bms_read_max_system_storage_capacity、bms_read_current_actual_remaining_available_electricity、g_t_bat_info - 充电桩:
get_evc_enable_flag/power_total/energy_total/chargemodel/hard_version/type、get_evc_ii_*(power_total/energy_total/chargemodel/gun_lock_flag/type)、g_t_evc_run_info、g_t_evc_setting_info、g_t_evc_config_table、g_t_evc_debug_data、g_t_evc_ii_run_info、g_t_evc_ii_single_config - 能量统计(
data/system_aux):energy_read_storage_pv_input、energy_read_pv_inverter_output、energy_read_storage_inv_input/output、energy_read_storage_bat_charge/bat_dischg、energy_read_grid_consumption/feedin/chgrge、energy_read_bat_dischgrge/bat_dischg_feedin_grid、energy_read_gc_charge/gc_output、get_realtime_bat_chg/dischg/chg_from_grid/dischg_feedin_grid、get_energy_data_enable() - 逻辑/调度:
logic_read_*(sequencecontrol_mode、ac_total_set_*、inv_rated_*、bat_*_power_limit)、g_t_logic_info、get_dispatch_set_power()、get_dispatch_mode()、get_dispatch_running_freq()、get_fast_dispatch_running_freq()、get_dispatch_running_mode()、dispatch_api_data_interval()、frequency_regulation_flag()、dispatch_regulation_flag()、get_fcas_running_flag()、get_fast_fcas_running_flag()、get_very_fast_fcas_running_flag()、get_ffr_running_flag()、aging_mode_send_data_flag()、g_t_dispatch_t(含t_stsystemcmdinfo、t_agingmode_info) - 并机/PV 充电器:
parallel_read_*(ac/dc 总功率设定、充满优先、SOC 阈值)、parallel_read_full_power_priority_enable、parallel_read_bat_soc_threshold、pvcharger_read_bat_*、g_t_host_machine_running_info[] - 升级与下载(
net/upd_download.h、net/server.h、各*_download.h/*_upgrade.h):get_updt_status_current()、check_need_response_updt_devcie()、get_flag_force_update()、set_response_force_upd_status()、get_flag_modbus_force_update()、set_save_firm_in_app_flash_flag()、load_current_update_data()、is_goto_update_check_status()、is_goto_get_update_file_status()、get_curr_updt_point_is_null()、get_current_point_*(file_mask/version/len/offset/flag/version_force_updt)、get_local_md5_ems_upt_file()、get_Meter_Updt_File_Mask()、get_meter_updt_object()、Get_Status_Is_Updated_From_Server()、check_force_upgradable_flag()、check_updt_force_is_none()、inverter_read_upgrade_file_offset[_arm]()、Get_Idx_Updt_BMS_Loacl()、get_upgrade_file_offset_meter/evc/afci、GetUpgradeFileOffsetBB()、get_updt_dev_type_curr() - 断点续传:
Get_Resume_Status_Current()、Set_Resume_Status_Current()、get_resume_index_length()、get_resume_cmd_id_code()、resume_buff_sdram、g_u32_resume_data_len - 其他:
common/crc.h的GetCrcCode_Modbus()、common/md5.h的MD5Init/Update/Final、logic/math_tools.h的fmt_fixed()、hmi/key.h/lcd.h的LCD_RTDB.u8_language、get_show_errcode()、data/fault_warn_log的read_event_log_data()、data/net_event_log的g_t_error_log_tab、dry_node.h的STDryNodeInfo、generator.h/diesel_generator.h的g_t_diesel_gen、afci_*的g_t_afci_log_tab、evc*的g_t_evc_*、flash_data_store/sdram.h的定址宏、hardware/can/can0.h的get_can0_standard_pack/get_can0_exten_pack、ct.h的g_server_grid_sampling_curve_t、iwdg.h、modbus.h的g_t_modbus_config、bb_update.h、epo_read_config_enable()(system_config.h)、get_eeprom_hardware_read/write_fault_flag()、senergys_query_inv_register、g_t_wireless_info、g_e_linker_info、g_stNetInfo/g_stNetSysInfo/IP_Address、g_t_afci_log_tab。
6. 依赖关系
依赖(本文件 #include,:13-60)
- 网络层:
socket.h、ethernet.h、server.h、ems.h、upd_download.h、ems_download.h、ems_boot_handle.h - 设备固件升级:
bms_download.h、inv_download.h、meter_download.h、evc_download.h、afci_download.h、evc_upgrade.h、meter_upgrade.h、battery_upgrade.h、bb_update.h - 数据与逻辑:
data.h、logic.h、dispatch.h、system_config.h、system_aux.h、fault_warn_log.h、net_event_log.h、flash_data_store.h、sdram.h - 中间件:
meter.h、bms.h、battery_comm.h、inverter.h、generator.h、diesel_generator.h、evc.h、evc_ii.h、ct.h、afci_comm.h、modbus.h、dry_node.h - 人机与硬件:
key.h、lcd.h、board_init.h、pcf8563.h、can0.h、iwdg.h - 通用库:
md5.h、crc.h、math_tools.h
被依赖
net/ems.h被 48 个文件包含(见ems.h.md第 6 节);本文件即该头文件的主要实现者。- 直接调用本文件符号的模块:
user/main.c(创建任务)、net/server.c(set_info_status/clear_info_status/get_info_status/g_stEMSInfo/stUpt/reply_upload_can_log/g_as8SpecialCmdId/s_can_log_ctrl)、midware/ethernt/ethernet.c(socket_abnormal_handle、g_stEMSInfo各 tick 与计数)、logic/logic.c、data/data.c、data/system_config/system_config.c、hmi/key.c、midware/bms/bms.c、midware/inverter/*、midware/meter/meter.c、midware/interface/modbus/modbus.c、data/fault_warn_log/fault_warn_log.c、data/resume_data/flash/flash_data_store.c、hmi/led/app_led.c(读取g_stEMSInfo)。
7. 备注与风险点
snprintf容量参数存在无符号下溢(最高危)。全文件通用写法是snprintf(dst + l_u16Len, l_u16BufLen - l_u16Len, ...),l_u16BufLen、l_u16Len均为u16:一旦某帧累计长度超过l_u16BufLen(如5118),l_u16BufLen - l_u16Len会回绕成几万,snprintf便会越界写 SDRAM。风险最高的是字段最多的几帧:fill_system_run_info(:439)、fill_system_run_info_extend1(:946)、fill_send_config_data(:1676)、fill_send_config_data_extend1(:1931)、fill_system_cell_run_data(:2335)、fill_bms_self_info(:3997)。其中fill_system_cell_run_data的循环上限直接取自 BMS 上行数据g_t_bat_info.t_run_info.u8_cell_temper_number[i]/u8_cell_volt_number[i](:2355、:2370),未做上限校验,BMS 上报异常值即可直接溢出。l_u16Len -= 1去逗号的写法缺少保护,属结构脆弱点。全文件共 51 处l_u16Len -= 1;(已grep -n全量核对),约定是"每段 JSON 都以,结尾,拼接处先减 1 去掉该逗号再补]或}"。该约定在多处依赖"上一段一定输出过内容",而恰恰有几段是条件输出的:fill_system_run_info的PcsPara8..12组(:819-841的if/else if/else在本快照下中间分支被条件编译成空语句块,必然少输出一段)、MID 电表段(:1143)、A/B 簇段(:1212、:1230)、fill_system_cell_run_data中u8_num == 0、fill_send_gen_run_data的告警数组循环(:1611)。此时被减掉的其实是"上一段的尾逗号",因所有段都以逗号结尾而侥幸仍然合法;但这种正确性完全依赖编码约定,没有任何断言或长度校验,后续若新增一段不以逗号结尾的字段就会直接产出非法 JSON。同类去逗号写法还有 :856、:1249、:1616、:1906、:2156、:2285、:2394、:3234、:3613、:3630 等。- CAN 日志模块在本快照中不可编译/不可验证。
ems.h:522将check_can_log_upload_task声明为非static,而ems.c:6849定义为static——"static 声明跟随非 static 声明"在 C 中是错误,ARMCC 会报错。同时CAN_LOG_CTRL_T、CAN_LOG_RAW_ITEM_T、MAX_CAN_LOG_ID_COUNT、MAX_CAN_LOG_COUNT、CAN_LOG_PACK_COUNT_PER_PACK、CAN_LOG_CAPTURE_TIMEOUT_MS、SPECIAL_CMD_CAN_SEND、SPECIAL_CMD_UPLOAD_CAN_LOG_STR全仓(含被忽略文件)只能搜到使用(ems.c:7016/7088/7171/7216/7221、server.c:80/83/3090/3103),找不到任何定义,说明快照缺少对应头文件版本。另外SDRAM_CAN_LOG_BUF_ADDR(hardware/sdram/sdram.h:68)注释标注长度仅 32 KB,而net/server.c:7243按MAX_CAN_LOG_ID_COUNT * MAX_CAN_LOG_COUNT * sizeof(CAN_LOG_RAW_ITEM_T)分配,若这两个常量取"多 ID × 多条"的量级,极可能越界踩到相邻 SDRAM 段。必须向代码维护者确认头文件与容量。 - 编译期与运行期的逆变器类型不一致导致的分支差异。本快照
midware/inverter/inverter.h:172中INVERTER_TYPE == INVERTER_NULL(0):#if (INVERTER_TYPE == INOVANCE_TYPE)段(:4178-4334)整体不编译,g_t_fault_scord_data也无定义,但net/ems.h:502/517/518无条件extern/声明、midware/inverter/inovance/inovance_comm_usart.c会调用 → 声明与定义不匹配;:626-631的老化模式分支里,s32_bms_total_power = inverter_read_bat_power();被#if ((INVERTER_TYPE == INVERTER_MAX) || (INVERTER_TYPE == ALPHAESS_TYPE))编译掉,若运行期get_inverter_type()判定为ALPHAESS_TYPE且ControlMode == AGING_MODE,s32_bms_total_power恒为 0(Pbat字段会误报 0);:819-841的两个PcsPara8..12分支按编译期宏互斥,可编译的只有"非 ALPHAESS/INOVANCE"分支,因此运行期即使接 AlphaESS 逆变器,上报的也是s16_dc_*字段组。
- 注释与实现不符 / 硬编码魔法数字:
:5038注释写"10*1000 * 1ms = 10s 即每10S发一包数据",实际条件SENCOND_60S(60 s)。- 欧盟电池法上传周期被硬编码为
l_u32_upload_interval_s = 10 * 60;(:5202),上面两行保留着"按云端下发周期"的注释方案(:5200-5201),导致ems_get_upload_interval()(:4491)定义后在本文件内没有调用点。 :5242硬编码600000ms 作为混搭信息周期;:4978/:4982的l_u16_send_running_time = 60/300、:5022的>= 300均为硬编码秒数,未使用SENCOND_*常量。:169g_stEMSInfo.time_ticksenddata = get_rtc_tick() - 300 + 60;两个无解释常量叠加。:501/506/511与:1403/1408/1413用abs(功率) < 50把电表相功率置零(0.01 kW 单位下即 0.5 kW),阈值写死。:776-780直接把 OVP1/OVP2/UVP1/UVP2/OFP/UFP 的保护定值写成字符串常量上报,而非读取g_t_inv_info.t_safty_info。:853用一串0,0,0,0,0,0,0,0,0,0补齐APPErrorCode数组长度。
obtain_event_log_data()(:2414)无调用点(全仓仅本文件出现该名字),属死代码;fill_bat_log_ack()(:2903)、fill_bat_log_data_send()(:4348)分别位于#if 0(:2895-2936、:4336-4408),其对应的STATUS_SET_BAT_LOG_CMD/STATUS_SEND_BAT_LOG_DATA两个 case 也在#if 0(:6157-6180)内,但check_idle_process()仍会返回STATUS_SET_BAT_LOG_CMD(:4906-4910,未加条件编译)——一旦g_u8_bat_log_flag被置真,状态机会落到default直接回 IDLE,成为"静默丢弃"的路径。fill_send_fault_record_data()的 s32 通道按 16 位解析:第二段循环(:4282-4286)传sizeof(s16)给s32_channel(8 个通道),fill_channel_data()内部按s16*取值(:4197-4200),拼出的通道值会被拆成两个半字,数据错误/截断。仅 INOVANCE 编译,但属明显 bug。- CAN 日志直接
send()绕过队列(:6241):与task_ethernet的发送路径并发操作同一SOCK_TCP_CLIENT;失败时只调socket_abnormal_handle()并继续按 10 s 节流重试,不重建连接(真正的重连由以太网任务负责)。 - 32 位毫秒计数长期回绕:
u32TickSendBMSSelfInfo(:5205)、u32TickSendMIXBatInfo(:5242)用*1000/600000 参与 32 位毫秒比较,约 49.7 天回绕一次;else if(tick > OSTimeGet())分支只能在下一次判定后被动修正,不是通用处理。 check_idle_process()的"配置回读闸门"语义容易被误改::4601的if((g_u8RequestConfigFlag == FALSE) || (g_u8appConfigFlag == FALSE))实际是"配置未同步完毕才允许业务上报",两个标志同时为真(即STATUS_RESPSONED_SET_CONFIG之后、云端还没回配置的那段时间)会停止所有常规上报(包括心跳与运行数据),仅保留配置回读与升级相关分支。若云端不回配置或回帧被丢,会造成长时间"数据不上报"的现象。clear_info_status()名字与语义相反(:2736为&=而非&= ~),正确用法必须传~(1<<bit)(net/server.c:6770);传错会清掉全部其它事件。- 未声明为
static的"内部函数":get_setup_config_*、全部fill_*、handle_response_*、request_update_*、respond_*、get_*_data、ems_time_event、app_connect_detect、send_*_data、can_log_*、ems_get_upload_interval、obtain_event_log_data全部是外部链接且无头文件声明,一旦与其它文件同名(如fill_channel_data这类通用名)会静默链接到一起,建议收窄作用域。 fill_send_info()中的sprintf/switch分支:INV_UPGRADE_LOG分支的switch(g_t_inv_info.t_debug_log.u32_err_reason)(:2512-2565)没有default分支,未覆盖的错误码会让l_s8_temp保持空串(初始化为{0}),且多处sprintf格式串末尾的逗号处理不一致(ERRNONE~RECVMASTERLASTERR带逗号、其余不带);l_s8_temp[128]与GET_VARIABLE_NAME展开的宏名长度需人工确认不会溢出。handle_response_special_cmd()缩进可疑但语义正确::2800-2801的STATUS_SEND_SET_BAT_BRAND分支中l_pu8Temp = SPECIAL_CMD_SET_BAT_BRAND_STR;缩进与if/else不对齐;另有STATUS_RESPSONED_CAN_SEND分支(:2802-2803)同样只挂了单语句,容易在后续维护时被误改。fill_system_run_info_extend1()的逗号依赖:BatMaxCapacity、AClusterStatus、BClusterStatus三段用"若存在则退逗号"的写法拼装(:1203-1206、:1226、:1245),而函数结尾又无条件l_u16Len -= 1;(:1249)——当 MID 电表段、A/B 簇段全部不成立时,BatMaxCapacity段(无条件输出)末尾的逗号会被减掉再接},结果仍合法;但这完全是靠"每段都以逗号结尾"的约定成立的,建议补上"是否已写入过内容"的标志位。send_debug_run_info_data()的abs(send_debug_run_info_tick - OSTimeGet())(:6652)对u32相减结果取abs(int),超过INT_MAX的差值语义不明确;同函数到期时memset整个g_t_debug_run_info,会连带清掉net/server.c刚下发的使能配置,形成"下发一次只上报一段时间"的行为,与云端预期需对齐。handle_message_queue()的类型参数在几处传错(u8Type仅用于队列/调试记账,不影响发送的字节流,但会让日志与超时判据失真)::5801(扩展域名应答传STATUS_SEND_INFO)、:5824、:5837(上传周期、电芯品牌应答都传STATUS_SEND_INFO)、:6069(STATUS_GET_CONFIG_EXTEND1却传STATUS_SET_CONFIG_EXTEND1)、:6209(AFCI 日志传STATUS_SEND_FAST_FCAS_DATA)。fill_system_grid_ct_sampling_info()的静态分包序号是文件级状态:static u8 l_u8_sampling_code(:881)从 1 递增到 25 后清零并清g_server_grid_sampling_curve_t的发送标志与计数,若中途某包发送失败(入队被丢弃),云端将永久缺少该包,没有补发机制。STATUS_SEND_CAN_LOG_DATA分支在状态机内声明静态变量(:6230static u32 s_last_send_tick = 0;),与check_can_log_upload_task()里的另一个静态s_last_check_tick(:6851)互相独立,两者节流周期(10 s 与 25 ms)需同时成立才会真正发帧,调参时容易顾此失彼。