pCCU新增锂电池通信/协调操作 + 电源协调状态机骨架(惰性)

锂电池通信(docs/锂电池协议20230324.docx):
- 协议层 BatProtocol:自检0x0000(2B)/设备控制0x0001(17B)/电池组状态0x0003(80B)/
  电池包报警0x0004(68B),uint32字节和校验、小端
- 链路层 BatLinkManager:动力/仪表锂电池独立UDP链路,本机监听与远端IP端口
  均由.moos配置(dyn_bat_*/ins_bat_*/bat_work_condition)
- SystemData/SnapshotBuilder/网页:电池数据按动力/仪表归类展示(运行状态/接触器
  电气量/电池包电压温度/报警),收发帧落库SQLite,网页新增电池标签页

操作->指令下发(PowerCoordinator):
- 操作接口:自检/上下电(母线接触器55H接通77H断开,含预充)/功率设定(0~500kW)/
  电池切换(低压->高压6步、高压->低压5步,按协议时序)
- 步骤引擎:每电池独立步骤队列,下发->状态反馈确认->超时兜底;周期1s心跳控制帧
- PM操控指令insBatCmd/dynBatCmd/dynBatPower自动映射为电池操作

电源协调状态机骨架(CoordFsm, TinyFSM):
- 事件Tick/PmControl/StepDone + 状态Normal/Switching/Fault,react均为TODO占位
- 当前未接线(无start/dispatch调用,运行行为与接入前一致),CoordFsm.hpp头部附接线指南

测试与部署:
- pccuTest新增电池协议编解码/帧长/链路分发用例(175项通过);集成测试覆盖电池
  心跳、启停映射(0x10->0x55/0x20->0x77)、状态落库;同步pccu_it.moos与ws_check
- h100.moos与pCCU.moos增加锂电池链路配置;归档锂电池协议文档
This commit is contained in:
zjk
2026-08-30 14:40:27 +08:00
parent 2590282aed
commit 7e4d08808a
25 changed files with 2691 additions and 23 deletions
+109 -1
View File
@@ -74,6 +74,8 @@ th{color:var(--dim);font-weight:500;}
<button class="tab active" data-tab="fcStatus">FC状态反馈</button>
<button class="tab" data-tab="pmCmd">PM操控指令</button>
<button class="tab" data-tab="pmParam">PM参数设定</button>
<button class="tab" data-tab="batDyn">动力锂电池</button>
<button class="tab" data-tab="batIns">仪表锂电池</button>
</div>
<div class="tab-group"><span class="tg-label">发送</span>
<button class="tab" data-tab="fcCmd">FC控制指令</button>
@@ -91,7 +93,7 @@ th{color:var(--dim);font-weight:500;}
const $ = id => document.getElementById(id);
const pagesEl = $('pages');
const statusEl = $('status');
const TABS = ['fcStatus','pmCmd','pmParam','fcCmd','pmStatus','pmFb','links','raw'];
const TABS = ['fcStatus','pmCmd','pmParam','batDyn','batIns','fcCmd','pmStatus','pmFb','links','raw'];
let activeTab = 'fcStatus';
function dot(ok){ return ok ? 'dot-ok' : (ok === undefined ? 'dot-warn' : 'dot-bad'); }
@@ -126,6 +128,16 @@ function statusText(s){
return m[s] || ('未知('+hex(s)+')');
}
function flameText(f){ return {0:'无效',1:'火焰报警',2:'探测器故障'}[f] || ('未知('+hex(f)+')'); }
//------------------ 锂电池(0x0003/0x0004) 状态码解析 ------------------
function batWcText(w){ return {0x00:'无效',0x10:'工房调试',0x30:'试验实航',0x50:'科研模式'}[w] || ('未知('+hex(w)+')'); }
function batScText(s){ return {0x00:'无效',0x11:'自检过程中',0x22:'自检完成正常',0x33:'自检完成异常'}[s] || ('未知('+hex(s)+')'); }
function batPwrCfgText(s){ return {0x00:'无效',0x11:'功率计算中',0x22:'满足功率要求',0x33:'不满足功率要求',0x44:'满足要求但无法执行'}[s] || ('未知('+hex(s)+')'); }
function batBusText(s){ return {0x00:'无效',0x01:'执行中',0x11:'闭合完成',0x22:'断开完成',0x33:'闭合故障',0x44:'断开故障'}[s] || ('未知('+hex(s)+')'); }
function batNibText(n){ return {0:'无效',1:'执行中',2:'闭合完成',3:'断开完成',4:'闭合故障',5:'断开故障',6:'负载端无电压'}[n] || ('未知('+hex(n)+')'); }
function batChargeText(s){ return {0x00:'无效',0x10:'电池充电中',0x20:'电池功率输出中'}[s] || ('未知('+hex(s)+')'); }
function batAlarmHex(f){ return f===0 ? '<span class="fc-ok">'+hex(f)+' 无</span>'
: '<span class="fc-bad">'+hex(f)+'</span>'; }
// 带解析的值:hex 码 + 括号解析
function parsed(v, text){ return hex(v)+' ('+text+')'; }
// 故障码高亮:0 显示绿色正常,非 0 显示红色
@@ -291,10 +303,16 @@ function linkCard(s){
'收:'+l.fc.rx+' 发:'+l.fc.tx+' 误:'+l.fc.err);
h+=row('<span><span class="status-dot '+dot(online(l.pm.lastRx))+'"></span>PM 链路</span>',
'收:'+l.pm.rx+' 发:'+l.pm.tx+' 误:'+l.pm.err);
h+=row('<span><span class="status-dot '+dot(online(l.batDyn.lastRx))+'"></span>动力锂电池链路</span>',
'收:'+l.batDyn.rx+' 发:'+l.batDyn.tx+' 误:'+l.batDyn.err);
h+=row('<span><span class="status-dot '+dot(online(l.batIns.lastRx))+'"></span>仪表锂电池链路</span>',
'收:'+l.batIns.rx+' 发:'+l.batIns.tx+' 误:'+l.batIns.err);
h+=row('FC 状态接收计数', s.fcStatusCount);
h+=row('PM 指令接收计数', s.pmControlCount);
h+=row('FC 控制转发计数', s.fcControlCount);
h+=row('PM 状态发送计数', s.pmStatusCount);
h+=row('动力电池状态接收计数', s.batDynStatusCount);
h+=row('仪表电池状态接收计数', s.batInsStatusCount);
return card('链路状态', h);
}
@@ -359,6 +377,89 @@ function pmFbCard(d){
return card('设定结果', h);
}
//------------------ 锂电池状态(0x0003) / 电池包报警(0x0004) 卡片 ------------------
function batRunCard(d){
if(!d) return '';
let h='';
h+=row('工况', parsed(d.workCondition, batWcText(d.workCondition)));
h+=row('电池自检状态', parsed(d.selfCheckState, batScText(d.selfCheckState)));
h+=row('功率配置状态', parsed(d.powerCfgState, batPwrCfgText(d.powerCfgState)));
h+=row('充放电状态', parsed(d.chargeStatus, batChargeText(d.chargeStatus)));
h+=row('电池紧急状态', hex(d.emergencyState));
h+=row('SOC', (d.soc*0.1).toFixed(1)+' %');
h+=row('电池电量', (d.energy*0.1).toFixed(1)+' kWh');
h+=row('允许最高放电功率', (d.maxDischargePower*0.1).toFixed(1)+' kW');
h+=row('允许充电功率', (d.maxChargePower*0.1).toFixed(1)+' kW');
h+=row('当前接入功率', (d.currentAccessPower*0.1).toFixed(1)+' kW');
h+=row('电池包在线标志', hex32(d.packOnlineFlag));
h+=row('电池包接入标志', hex32(d.packAccessFlag));
h+=row('通信心跳', d.heartbeat);
return card('运行状态', h);
}
function batElecCard(d){
if(!d) return '';
let h='';
h+=row('母线接触器状态', parsed(d.busContactorState, batBusText(d.busContactorState)));
h+=row('正极接触器', batNibText(d.posDiodeState & 0x0F));
h+=row('二极管回路', batNibText((d.posDiodeState>>4) & 0x0F));
h+=row('正极+充电继电器', hex(d.relayPosCharge));
h+=row('预充+负极继电器', hex(d.relayPreNeg));
h+=row('负载端电压', (d.voltageLoad*0.01).toFixed(2)+' V');
h+=row('电池端电压', (d.voltagePack*0.01).toFixed(2)+' V');
h+=row('放电电流', (d.current*0.05).toFixed(2)+' A');
h+=row('正端绝缘电阻', d.insulationPos+'0 kΩ');
h+=row('负端绝缘电阻', d.insulationNeg+'0 kΩ');
return card('接触器与电气量', h);
}
function batPackCard(d){
if(!d) return '';
let h='';
h+=row('中值电压', (d.packVoltageMid*0.1).toFixed(1)+' V');
h+=row('最低电压', (d.packVoltageMin*0.1).toFixed(1)+' V (包号'+d.packVoltageMinNo+')');
h+=row('最高电压', (d.packVoltageMax*0.1).toFixed(1)+' V (包号'+d.packVoltageMaxNo+')');
h+=row('单包最大压差', (d.packDeltaMax*0.1).toFixed(1)+' mV (包号'+d.packDeltaMaxNo+')');
h+=row('单体均值温度', (d.cellTempAvg-40)+' ℃');
h+=row('单体最低温度', (d.cellTempMin-40)+' ℃ (包号'+d.cellTempMinNo+')');
h+=row('单体最高温度', (d.cellTempMax-40)+' ℃ (包号'+d.cellTempMaxNo+')');
return card('电池包电压温度', h);
}
function batAlarmWordCard(d){
if(!d) return '';
let h='';
h+=row('报警标识字1', batAlarmHex(d.alarmFlag1));
h+=row('报警标识字2', batAlarmHex(d.alarmFlag2));
h+=row('报警标识字3', batAlarmHex(d.alarmFlag3));
h+=row('报警标识字4', batAlarmHex(d.alarmFlag4));
h+=row('报警标识字5', batAlarmHex(d.alarmFlag5));
h+=row('报警标识字6', batAlarmHex(d.alarmFlag6));
h+=row('报警位置标识字', hex32(d.alarmPosFlag));
return card('电池组报警', h);
}
function batPackAlarmCard(d){
if(!d) return '';
let h='';
h+=row('电池包号', d.packNo);
h+=row('报警标识字1', batAlarmHex(d.alarmFlag1));
h+=row('报警标识字2', batAlarmHex(d.alarmFlag2));
h+=row('SOC', (d.soc*0.1).toFixed(1)+' %');
h+=row('正极+负极继电器', hex(d.relayPosNeg));
h+=row('负载端电压', (d.voltageLoad*0.1).toFixed(1)+' V');
h+=row('电池端电压', (d.voltagePack*0.1).toFixed(1)+' V');
h+=row('放电电流', (d.current*0.05).toFixed(2)+' A');
h+=row('单体最高温度', (d.tempMax1-40)+' ℃ (号'+d.tempMaxNo+')');
h+=row('单体最低温度', (d.tempMin1-40)+' ℃ (号'+d.tempMinNo+')');
h+=row('单体平均温度', (d.tempAvg-40)+' ℃');
h+=row('均衡状态', d.balanceState===1?'有均衡':'无');
h+=row('单体最高电压', (d.voltMax1*0.1).toFixed(1)+' mV (号'+d.voltMaxNo+')');
h+=row('单体最低电压', (d.voltMin1*0.1).toFixed(1)+' mV (号'+d.voltMinNo+')');
h+=row('单体平均电压', (d.voltAvg*0.1).toFixed(1)+' mV');
return card('电池包报警信息', h);
}
function pmStatusCard(d){
if(!d) return '';
let h='';
@@ -450,6 +551,13 @@ function render(snap){
+tankCard(snap.fc)+cabinCard(snap.fc)+'</div>',
pmCmd: pageHeader(0x0001,'PM操控指令','PM→CCU')+'<div class="grid">'+cmdCard(snap.pmCmd)+'</div>',
pmParam: pageHeader(0x0002,'PM参数设定','PM→CCU')+'<div class="grid">'+pmParamCard(snap.pmParam)+'</div>',
// 0x0003/0x0004 锂电池状态反馈(锂电池→CCU):动力 / 仪表
batDyn: pageHeader(0x0003,'动力锂电池状态','动力电池→CCU')+'<div class="grid">'+
batRunCard(snap.batDyn)+batElecCard(snap.batDyn)+batPackCard(snap.batDyn)+
batAlarmWordCard(snap.batDyn)+batPackAlarmCard(snap.batDynAlarm)+'</div>',
batIns: pageHeader(0x0003,'仪表锂电池状态','仪表电池→CCU')+'<div class="grid">'+
batRunCard(snap.batIns)+batElecCard(snap.batIns)+batPackCard(snap.batIns)+
batAlarmWordCard(snap.batIns)+batPackAlarmCard(snap.batInsAlarm)+'</div>',
fcCmd: pageHeader(0x0001,'FC控制指令','CCU→FC')+'<div class="grid">'+fcCmdCard(snap.fcCmd)+'</div>',
// 0x0004 PM 状态报文(CCU→PM):含锂电池/应急电池数据
pmStatus: pageHeader(0x0004,'PM状态报文','CCU→PM')+'<div class="grid">'+