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

ems.c

源文件路径: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_u8DevTypestatic ENUpdateType当前需要应答"强制升级"的设备类型(net/ems.c:113)
s_run_info_extend1_timestatic struct RTC_DateTime扩展运行数据的采集时刻快照(net/ems.c:114)
s_u8Flag1static 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_COUNTCAN 日志模块常量,本快照内无定义(见第 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)staticCAN 日志采集/上报两态轮询(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 起):

偏移长度内容说明
01设备域 = EMS(0x01)固定
11方向域 = REQUEST_COMM(0x01) / RESPONSE_COMM(0x02)上报用 REQUEST,对方主动指令的应答用 RESPONSE
21命令字(ENCmdType)由调用方的 fun/type 参数传入
3-420x00 0x00保留
5-62数据域长度(大端)= 报文总长 − 7sendBuff[5] = (l_u16Len-7)>>8,[6] = (l_u16Len-7)&0xFF(:340-341)
7 …NJSON 明文文本各 fill_* 从 l_u16Len = 7 起用 snprintf 追加
N+1, N+22Modbus 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_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_LOGINfill_login_data()(并 u8LogInCnt++)0x02
STATUS_HEART_BEARTfill_heart_beat_data()0x00
STATUS_SEND_DATA_TO_SERVERfill_system_run_info(CMD_SEND_DAT_SYS_RUN)0x05
STATUS_SEND_DATA_TO_SERVER_EXTEND1fill_system_run_info_extend1(CMD_SEND_DAT_SYS_RUN_EXTEND1)0x16
STATUS_SEND_SECOND_DATAfill_system_second_data()0x10
STATUS_SEND_ENERGY_DATAfill_system_energy_data()0x1D
STATUS_SEND_BMS_MIN_DATAfill_system_cell_run_data()0x55
STATUS_SEND_BMS_SELF_INFOfill_bms_self_info()0xA3
STATUS_SEND_MIXBAT_INFOfill_mix_bat_data()0xA6
STATUS_SEND_GENSET_DATAfill_send_gen_run_data()0x36
STATUS_SEND_BURNIN_DATAfill_burnin_data()0x18
STATUS_SEND_DEBUG_RUN_INFOfill_system_debug_run_data()0x23
STATUS_SEND_UPGRADE_INFOfill_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_DATAfill_system_fcas_data() / `fill_system_fast_fcas_data(0x1B0x19) / fill_system_ffr_data_100ms() / fill_system_dispatch_data()`0x11 / 0x1B / 0x19 / 0x12 / 0x1C
STATUS_SEND_GRID_CT_SAMPLING_DATEfill_system_grid_ct_sampling_info()0x1A
STATUS_SEND_CONFIG / _EXTEND1 / _EXTEND2fill_send_config_data(CMD_SEND_CONFIG_INFO) 等0x04 / 0x15 / 0x61
STATUS_READ_CONFIG / _EXTEND1 / _EXTEND2`fill_read_config_data(0x030x140x60)`0x03 / 0x14 / 0x60
STATUS_SET_CONFIG / STATUS_SET_CONFIG_EXTEND1respond_set_config_data() / respond_set_config_data_extend1()0x42 / 0x4C
STATUS_GET_CONFIG / STATUS_GET_CONFIG_EXTEND1fill_send_config_data(CMD_GET_CONFIG) / fill_send_config_data_extend1(CMD_GET_CONFIG_EXTEND1)0x43 / 0x4D
STATUS_SEND_RUN_INFO / _EXTEND1fill_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(0x470x45)`0x47 / 0x45
STATUS_SEND_SAFETY_CONFIG_INFO / STATUS_SEND_SAFETY_CONFIG`fill_send_safety_config_data(0x4B0x58)`0x4B / 0x58
STATUS_SET_SAFETY_CONFIG_INFO / STATUS_SET_SAFETY_CONFIG`fill_set_safety_config_data(0x4A0x57)`0x4A / 0x57
STATUS_UPDATE_CHECK / STATUS_GET_UPDATE_FILErequest_update_file_info() / request_update_file_content()0x20 / 0x21
STATUS_SEND_INFOfill_send_info()0x09
STATUS_SEND_ERROR_LOG直接取 g_t_error_log_tab.u8_error_log_buff(长度 > 7 才发)——
STATUS_SEND_AFCI_LOG_DATAfill_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_UPDATEhandle_response_force_update()0x08
STATUS_RESPSONED_RESUMEHandle_Response_Resume()(先清 g_u8_resp_resume_flag)0x08
STATUS_SEND_CAN_LOG_DATAfill_send_can_log_data() + 直接 send()0x63
STATUS_SEND_FAULT_RECORDfill_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 阶梯,先命中先返回。三层结构:

  1. 队列忙等(:4529):g_stNetDataSend.u8Type != STATUS_NONE 时只做计数,累计 50 次(NET_DATA_SEND_TRY)后把 u8Type 强制清为 STATUS_NONE 并返回 IDLE(相当于发送超时丢包)。
  2. 建链但未登录(:4547):bIsLogin==FALSE && bIsBuiltSocket==TRUE && APP 未连接 → 每 10 s(SENCOND_10S)返回一次 STATUS_LOGIN。
  3. 已登录(: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()==FALSE10 sSTATUS_SEND_CONFIG(0x04)
2扩展 1 同条件10 sSTATUS_SEND_CONFIG_EXTEND1
3扩展 2 同条件10 sSTATUS_SEND_CONFIG_EXTEND2
4check_need_response_updt_devcie(&s_u8DevType)事件STATUS_RESPSONED_FORCE_UPDATE
5get_flag_modbus_force_update()==FALSE 时:is_goto_update_check_status()5 sSTATUS_UPDATE_CHECK(0x20)
6is_goto_get_update_file_status()(需先取得 g_pUpdateSem,OSSemPend 3 s 超时)100 msSTATUS_GET_UPDATE_FILE(0x21)
7g_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_*
8g_u8_resp_resume_flag / Get_Resume_Status_Current()==RESUME_STS_Sending / ==RESUME_STS_Over事件 / 5 sSTATUS_RESPSONED_RESUME / STATUS_SEND_RESUME_TO_SERVER
9g_u8_bat_log_flag(g_u8_bat_log_send_flag 为空分支,无动作)事件STATUS_SET_BAT_LOG_CMD(对应 case 在 #if 0 内)
10g_u8_can_send_done_flag事件STATUS_RESPSONED_CAN_SEND
11is_can_log_upload_ready()事件STATUS_SEND_CAN_LOG_DATA
12get_info_status() 命中 22 种事件位之一10 sSTATUS_SEND_INFO
13AFCI 日志发送标志 + 回读成功 + 非升级中 + 可强制升级 + 无续传事件STATUS_SEND_AFCI_LOG_DATA
14运行数据:老化模式 60 s,其它模式 300 s60/300 sSTATUS_SEND_DATA_TO_SERVER
15扩展运行数据(同周期,time_ticksenddataextend1)60/300 sSTATUS_SEND_DATA_TO_SERVER_EXTEND1
16油机数据(需 genset_enable_flag)300 sSTATUS_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 sSTATUS_SEND_BURNIN_DATA
20升级信息(u8_send_data_flag 或 g_u8_send_upgrade_info_flag)3 sSTATUS_SEND_UPGRADE_INFO
21调试数据(u64TickSendDebugRunData)u32_debug_run_info_time_intervalSTATUS_SEND_DEBUG_RUN_INFO
22能量数据(get_energy_data_enable())60 sSTATUS_SEND_ENERGY_DATA
23秒级数据(u64TickSecData)10 sSTATUS_SEND_SECOND_DATA
24get_inverter_safty_test_info_send_server_flag()事件STATUS_SEND_SAFETY_INFO
25错误日志(g_t_error_log_tab.u8_error_log_send_flag)10 sSTATUS_SEND_ERROR_LOG
26安规配置上传 / 设置(u8_set_safety_config_flag)10 sSTATUS_SEND_SAFETY_CONFIG_INFO / STATUS_SET_SAFETY_CONFIG_INFO
27云 BMS 单体数据60 sSTATUS_SEND_BMS_MIN_DATA
28电池详细信息(欧盟电池法)硬编码 10 min(:5202)STATUS_SEND_BMS_SELF_INFO
29bms_self_info_clear_flag(电池信息被清空)事件STATUS_SEND_BMS_SELF_INFO
30故障录波(#if INOVANCE,当前不编译)2 sSTATUS_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 上报报文的内容与打包要点

4.6 登录与保活

4.7 升级与断点续传流程

升级(net/upd_download.c 提供状态查询,本文件负责驱动):

  1. 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) 清标志。
  2. load_current_update_data() 载入升级表(g_stEMSInfo.pstCurrUpt / stUpt[],也可由 midware/interface/modbus/modbus.c:8779+ 经 Modbus 直接指定升级目标)。
  3. STATUS_UPDATE_CHECK(5 s 周期)→ 0x20 上报本机掩码与版本信息,云端回帧决定是否需要下载。
  4. 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(),避免无线升级期间被判超时或升级后数据包错乱。
  5. 升级过程中的进度上报由 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 维护):

4.8 调频/调度/FFR 数据采集(get_*_data())

这三个函数每 25 ms 被调用一次,把实时量写入 g_t_freq_dispatch_data 的并行数组,写满一个周期后置对应的 g_u8_send_*_flag,由状态机负责发送:

函数采样周期数组上限采集量
get_fcas_data()(:6313)常规 FCAS1 s(get_rtc_tick() 秒差值)10s16_power、u16_freq(get_dispatch_running_freq())、s32_meter_power、s32_pv_power、s32_pv_meter_power、s32_reactpower、s16_batpower、u8_dispatch_flag
同上,快 FCAS200 ms(OSTimeGet() 差值)50同上,频率取 get_fast_dispatch_running_freq()
同上,极快 FCAS50 ms200同上
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 ms100只采 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,本文件负责采集与打包:

4.10 时间基准与定时语义

本文件混用两套时间基准,改动超时逻辑时必须区分:

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/Extend2Set_Setup_Config_Status() 等(被 data/system_config/system_config.c、hmi/key.c、各厂商逆变器通信模块调用,表示"本地配置已改,需重发")发配置的节流条件(get_setup_config_status()==FALSE 才发)与调度/FCAS/FFR 的上报使能条件
`g_stEMSInfo.time_ticksenddata[extend1gen]`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.u32InfoTypeset_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_dataget_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_infonet/server.c 下发使能/时长;send_debug_run_info_data() 到期清零调试数据上报节流
g_t_upgrade_run_infosend_upgrade_info_data()、net/server.c、各 *_upgrade.c 写错误位0x24 上报与状态迁移
g_au8NetSendPack / g_u16DataSendLen全部 fill_*handle_message_queue()(指针直接入队,发送期间靠 u8lock 保护)
g_t_fault_scord_datamidware/inverter/inovance/inovance_comm_usart.c、fault_scord_init()/set_fault_scord_status()0x56 录波上报(当前不编译)

5. 对外接口

本文件导出的函数(在 net/ems.h 中有声明、供外部调用)

导出函数外部调用方
task_net_appuser/main.c:255(OSTaskCreateExt)
socket_abnormal_handlemidware/ethernt/ethernet.c
set_info_statusdata/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_statuslogic/logic.c、net/server.c
Set_Setup_Config_Statusdata/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_Statusdata/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_Statusdata/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_statusmidware/inverter/inovance/inovance_comm_usart.c、net/server.c(注意:定义在 #if INOVANCE 内)
reply_upload_can_lognet/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 等广泛使用)。

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

6. 依赖关系

依赖(本文件 #include,:13-60)

被依赖

7. 备注与风险点

  1. 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 上报异常值即可直接溢出。
  2. 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 等。
  3. 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 段。必须向代码维护者确认头文件与容量。
  4. 编译期与运行期的逆变器类型不一致导致的分支差异。本快照 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_* 字段组。
  5. 注释与实现不符 / 硬编码魔法数字:
    • :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 硬编码 600000 ms 作为混搭信息周期;:4978/:4982 的 l_u16_send_running_time = 60/300、:5022 的 >= 300 均为硬编码秒数,未使用 SENCOND_* 常量。
    • :169 g_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 数组长度。
  6. 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,成为"静默丢弃"的路径。
  7. fill_send_fault_record_data() 的 s32 通道按 16 位解析:第二段循环(:4282-4286)传 sizeof(s16) 给 s32_channel(8 个通道),fill_channel_data() 内部按 s16* 取值(:4197-4200),拼出的通道值会被拆成两个半字,数据错误/截断。仅 INOVANCE 编译,但属明显 bug。
  8. CAN 日志直接 send() 绕过队列(:6241):与 task_ethernet 的发送路径并发操作同一 SOCK_TCP_CLIENT;失败时只调 socket_abnormal_handle() 并继续按 10 s 节流重试,不重建连接(真正的重连由以太网任务负责)。
  9. 32 位毫秒计数长期回绕:u32TickSendBMSSelfInfo(:5205)、u32TickSendMIXBatInfo(:5242)用 *1000/600000 参与 32 位毫秒比较,约 49.7 天回绕一次;else if(tick > OSTimeGet()) 分支只能在下一次判定后被动修正,不是通用处理。
  10. check_idle_process() 的"配置回读闸门"语义容易被误改::4601 的 if((g_u8RequestConfigFlag == FALSE) || (g_u8appConfigFlag == FALSE)) 实际是"配置未同步完毕才允许业务上报",两个标志同时为真(即 STATUS_RESPSONED_SET_CONFIG 之后、云端还没回配置的那段时间)会停止所有常规上报(包括心跳与运行数据),仅保留配置回读与升级相关分支。若云端不回配置或回帧被丢,会造成长时间"数据不上报"的现象。
  11. clear_info_status() 名字与语义相反(:2736 为 &= 而非 &= ~),正确用法必须传 ~(1<<bit)(net/server.c:6770);传错会清掉全部其它事件。
  12. 未声明为 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 这类通用名)会静默链接到一起,建议收窄作用域。
  13. 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 展开的宏名长度需人工确认不会溢出。
  14. 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)同样只挂了单语句,容易在后续维护时被误改。
  15. fill_system_run_info_extend1() 的逗号依赖:BatMaxCapacity、AClusterStatus、BClusterStatus 三段用"若存在则退逗号"的写法拼装(:1203-1206、:1226、:1245),而函数结尾又无条件 l_u16Len -= 1;(:1249)——当 MID 电表段、A/B 簇段全部不成立时,BatMaxCapacity 段(无条件输出)末尾的逗号会被减掉再接 },结果仍合法;但这完全是靠"每段都以逗号结尾"的约定成立的,建议补上"是否已写入过内容"的标志位。
  16. 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 刚下发的使能配置,形成"下发一次只上报一段时间"的行为,与云端预期需对齐。
  17. 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)。
  18. fill_system_grid_ct_sampling_info() 的静态分包序号是文件级状态:static u8 l_u8_sampling_code(:881)从 1 递增到 25 后清零并清 g_server_grid_sampling_curve_t 的发送标志与计数,若中途某包发送失败(入队被丢弃),云端将永久缺少该包,没有补发机制。
  19. STATUS_SEND_CAN_LOG_DATA 分支在状态机内声明静态变量(:6230 static u32 s_last_send_tick = 0;),与 check_can_log_upload_task() 里的另一个静态 s_last_check_tick(:6851)互相独立,两者节流周期(10 s 与 25 ms)需同时成立才会真正发帧,调参时容易顾此失彼。