《储能系统中的故障定位》

从"故障现象"到"可验证证据"的工程化闭环(含故障录波),线上/现场的故障排查,最怕两件事:只看现象、却没有证据链;只采集日志、却无法对齐"故障发生时刻"的关键波形。结合你们工程代码里已有的"故障信息采集 + 故障/日志/录波触发 + 波形原始帧落盘(USB)+ Web 导出",下面给出一套可直接落地的故障定位思路:让排查从"猜"变成"证"。

1. 先定义:这次故障你要定位的"对象"是什么?

建议在处理告警/故障时,先把对象分清楚,后续采集与排查才能对齐:

通信类:CAN/BMS/电表/主从 MQTT 通道是否异常(会导致录波数据拿不到)

功能类:充电/放电控制逻辑是否进入保护/停止(策略触发)

设备类:BMS 单体或系统级故障码是否成立(故障信息)

证据类:能不能拿到"故障发生时刻"的故障录波/原始帧(可复盘波形)

在代码里,"对象"的证据分别来自不同模块:例如 BMS 侧故障信息与录波触发/写盘;Web 侧对部分故障日志(如 meterfaultlog)导出。

2. 故障信息怎么拿:先拿"可复用的故障码/状态快照"

在 BMS 侧采集到的原始数据最终会映射为"可上送的故障信息"。例如 `can_infy_batt.c` 中,把运行侧聚合的故障码字段映射到上层 blob:

pBlobIfyBMS->unioBattFault = s_infy_BattRunData[n].unioBattFaultCode;

...

pBlobIfyBMS->emBattSysState = s_infy_BattRunData[n].emBattSysState ;

pBlobIfyBMS->emBattChgState = s_infy_BattRunData[n].emBattChgState ;

```

这一步的意义是:不管录波最终是否成功,至少先有"故障是什么"的快照。故障定位时你要做的第一件事,通常是把"故障码/系统状态/充电状态"与"控制策略触发点"对齐。

3. 故障录波怎么拿:代码里已经实现了"录波触发 + 逐条收取 + 原始帧写盘 + 确认"

工程中,BMS 录波属于"受控的采集流程",核心链路在 `can_infy_batt.c` 里非常清晰。

3.1 先拿到"总记录数":决定是否触发故障/日志/波形采集

`Parse_S2C_RecNum()` 会读取总记录数,包括故障记录、日志记录、波形记录:

if(pFrmReply->byData[1] == 0x00) {

s_infy_BattRunData[nBMSGrpMapIdx].nFaultRecNum = CAN_StringToUShort(pFrmReply->byData + 2);

s_infy_BattRunData[nBMSGrpMapIdx].nLogRecNum = CAN_StringToUShort(pFrmReply->byData + 4);

s_infy_BattRunData[nBMSGrpMapIdx].nWaveRecNum = CAN_StringToUShort(pFrmReply->byData + 6);

}

...

TRACEX("Idx=[%d] Total nFaultRecNum=[%d] nLogRecNum=[%d] nWaveRecNum=[%d] nDODRecNum=[%d]\n", ...);

```

工程化排查关键点:

如果你现场想要录波,但发现系统没有触发录波,先检查 `nWaveRecNum` 是否满足触发条件。

3.2 状态机决定"触发哪类录波":故障 > 日志 > 波形优先级

`YFY_StateMachine4BMSMsg()` 在 `STAT_ON_START` 里选择触发类型(优先故障记录,其次日志记录,最后波形记录),并且用 `MAX_RECSAVE_NUM` 做阈值判断:

case STAT_ON_START:

if(s_infy_BattRunData[nBMSIdx].nFaultRecNum > MAX_RECSAVE_NUM) {

pSTBatt_Rough->stuCanSave.emTrigMsgTp[nBMSIdx] = TP_TRIG_FAULT;

} else if(s_infy_BattRunData[nBMSIdx].nLogRecNum > MAX_RECSAVE_NUM) {

pSTBatt_Rough->stuCanSave.emTrigMsgTp[nBMSIdx] = TP_TRIG_LOG;

} else if(s_infy_BattRunData[nBMSIdx].nWaveRecNum > MAX_RECSAVE_NUM) {

pSTBatt_Rough->stuCanSave.emTrigMsgTp[nBMSIdx] = TP_TRIG_WAVE;

}

break;

```

这直接给出一个非常实用的排查判断法:

"录波没抓到"**通常不是"波形不存在",而是状态机没有进入 `TP_TRIG_WAVE`(阈值/计数/通讯导致读不到记录数)。

3.3 逐条收取 wave,并写入 USB 原始帧,再发送确认

当触发类型为 `TP_TRIG_WAVE`,状态机会不断发送 `CAN_RD_CMD_C2S_WAVE_REC`,直到 `nWaveRecNum` 归零/进入挂起状态。

收包后,`Tigger_IfyBmsFrameWrite2USB()` 对 `_CAN_R_CMD_S2C_WAVEREC` 做了非常明确的动作:**把原始帧写到 USB,并发确认**:

case TP_TRIG_WAVE:

if(pFrmReply->emMsgType == _CAN_R_CMD_S2C_WAVEREC) {

Pending_WriteMsg2USB(pSTBatt_Rough, nBMSGrpMapIdx, pFrmReply->byRawData, 1);

YFY_Send_C2S_Status(pSTBatt_Rough, nBMSGrpMapIdx, CAN_CFM_CMD_C2S_WAVE_REC);

}

break;

```

这也是"故障定位可复盘"的关键设计:

波形/故障录波并不依赖解析细节,**用原始帧保真**,后续再用离线工具解析波形内容。

如果写盘成功但无法解析,问题在离线解析/文件格式;如果写盘都没有,问题多半在录波收取链路或通讯。

3.4 发送波形读取命令(Wave Record 0x0b21yyxx)

波形读取指令打包也在同文件中:

YFY_CAN_StuffMsgID(0x0B, 0x21, frm.byBMSGrpAddr, frm.byEMSAddr, CAN_DATA_AREA_LEN, pbySendBuf);

frm.byData[0] = 0xC0;

...

memcpy(pbySendBuf + 5, frm.byData, CAN_DATA_AREA_LEN);

```

这段能帮助你做"命令是否发出去"的验证(例如抓 CAN 日志对齐 `0x0B/0x21` 对应的读取请求)。

4. 故障录波之外:日志类证据也要纳入定位(比如 AFE 异常时落盘)

代码中有对 AFE 异常状态的落盘行为(示例:`/data/AfeSrStatus.csv`)。当 `bAfeWorkModeAbnormal` 且状态变化时,会把数据写入文件:

fp = fopen("/data/AfeSrStatus.csv", "a");

...

TimeToString(NOW, TIME_CHN_FMT, szTime, sizeof(szTime));

...

fputs(strLine, fp);

```

故障定位建议: 波形是"事发时刻的证据",AFE 落盘是"异常演化的证据"。

当你发现波形抓不到时,至少可以用这种"关键状态变化落盘"去补齐定位链条。

5. Web 侧怎么"导出证据包":让故障录波可交付、可复现

工程里提供了故障/日志相关的导出接口。例如 `api_query_chgrecord.c` 里,对电表故障日志 `meterfaultlog` 做了打包导出:

if(access("/data/meterfaultlog.0", F_OK) != 0) {

Make_Web_OutPut(104, "Can NOT find MeterLog file for export!", NULL, NULL);

return FALSE;

}

system("tar cvf /var/volatile/Meter_Log.tar /data/meter*");

...

UploadBinFile_2Client(METERLOG_EXPORT_LOCALFILE, szRemoteName)

```

这对故障定位的意义是: 你不用只靠"现场口述",而是能把"可落盘证据包"交给分析侧进一步定位根因。

6. 一条可直接照做的排查闭环

把上面代码证据链拼成"定位闭环",建议按下面顺序执行:

  1. 先抓"故障对象快照":从 blob/状态字段确认系统状态、充放电状态、故障码是否成立(例如 `unioBattFault` / `emBattSysState` / `emBattChgState`)。

  2. 再确认"录波是否被触发":查看 `nFaultRecNum/nLogRecNum/nWaveRecNum` 是否满足 `MAX_RECSAVE_NUM` 的触发优先级。

  3. 检查"波形收取是否成功":看 `_CAN_R_CMD_S2C_WAVEREC` 分支是否执行了。 `Pending_WriteMsg2USB` 并发送了 `CAN_CFM_CMD_C2S_WAVE_REC`。

  4. 如果录波写盘成功但分析失败:回到原始帧文件格式/离线解析工具,而不是直接怀疑采集链路。

  5. 若录波/日志缺失:优先排查通讯链路(能否读取记录数、能否收到 waverec 回包)。

  6. 最后导出证据包(Web export 或打包上送),用于跨团队复盘。

7. 典型故障场景怎么套用

现场有故障现象,但系统没有录波文件。

优先检查:`nWaveRecNum` 是否触发 `TP_TRIG_WAVE`(阈值/计数)以及 CAN 录波回包是否收到(`_CAN_R_CMD_S2C_WAVEREC` 分支是否走到写 USB + confirm)。

有故障码,但波形与故障码对不上。

先确认录波原始帧时间是否与故障发生时刻一致(在解析阶段做对齐),再考虑:录波数据是原始帧保真,定位应从"解析对齐"回到"证据解释层"。

电表相关故障反复出现,但定位总是停留在告警表

用 Web export 把 `meterfaultlog` 导出来,再用解析工具结合曲线对齐,减少"只看告警、不看过程"的盲区。

相关推荐
慧一居士3 小时前
Vue项目中使用的首选的依赖库如VueUse等,使用场景和使用示例介绍
前端·vue.js
六义义3 小时前
SpringBoot 超详细全解(入门 + 实战 + 原理 + 面试)
java·spring boot·面试
fengxin_rou3 小时前
详解深浅拷贝:从原理到实现的完整指南
java·后端·浅拷贝·深拷贝
tsyjjOvO3 小时前
【SpringMVC 进阶】拦截器、文件上传、异常处理与 SSM 整合全解析
java·后端·spring
222you3 小时前
JUC读写锁和阻塞队列
java·开发语言·spring
草莓熊Lotso3 小时前
Linux 进程信号深度解析(上):信号的产生与本质(含完整案例)
android·linux·运维·服务器·数据库·c++·mysql
太阳之神aboluo3 小时前
RabbitMQ
java·分布式·spring·rabbitmq·java-rabbitmq
qqty12173 小时前
windows配置永久路由
java