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
+1
View File
@@ -137,6 +137,7 @@ add_executable(pccuTest
${CCU_DIR}/protocol/MessageRegistry.cpp
${CCU_DIR}/protocol/FcProtocol.cpp
${CCU_DIR}/protocol/PmProtocol.cpp
${CCU_DIR}/protocol/BatProtocol.cpp
${CCU_DIR}/comm/UdpEndpoint.cpp
${CCU_DIR}/comm/LinkManager.cpp
${CCU_DIR}/store/DbStore.cpp
+248
View File
@@ -15,9 +15,11 @@
#include "../../src/pCCU/protocol/MessageRegistry.h"
#include "../../src/pCCU/protocol/FcProtocol.h"
#include "../../src/pCCU/protocol/PmProtocol.h"
#include "../../src/pCCU/protocol/BatProtocol.h"
#include "../../src/pCCU/comm/LinkManager.h"
#include "../../src/pCCU/comm/FcLinkManager.h"
#include "../../src/pCCU/comm/PmLinkManager.h"
#include "../../src/pCCU/comm/BatLinkManager.h"
#include "../../src/pCCU/store/DbStore.h"
using namespace ccu;
@@ -287,6 +289,247 @@ static void testDbStore() {
std::remove(path.c_str());
}
//--------------------------------------------------------------------------
// 9. 锂电池帧长度必须与协议文档一致(docs/锂电池协议20230324.docx)
static void testBatFrameLengths() {
BatSelfCheckMessage bsc;
BatControlMessage bcm;
BatStatusMessage bsm;
BatPackAlarmMessage bam;
CHECK(bsc.totalLength() == 12); // 自检指令:6+2+4
CHECK(bcm.totalLength() == 27); // 设备控制指令:6+17+4
CHECK(bsm.totalLength() == 90); // 电池组状态:6+80+4
CHECK(bam.totalLength() == 78); // 电池包报警:6+68+4
}
//--------------------------------------------------------------------------
// 10. 锂电池自检/控制指令编解码往返
static void testBatControlRoundTrip() {
// 自检指令
BatSelfCheckValue sc;
sc.selfCheck = BAT_SC_START;
sc.selfCheckReset = BAT_SCR_RESET;
BatSelfCheckMessage scMsg;
std::vector<uint8_t> scFrame = scMsg.encode(sc);
CHECK(scFrame.size() == 12);
CHECK(scFrame[0] == 0x40 && scFrame[1] == 0x40);
CHECK(scFrame[2] == 0x00 && scFrame[3] == 0x00); // id 0x0000 小端
CHECK(scFrame[6] == 0x01 && scFrame[7] == 0x55);
BatSelfCheckValue scOut;
CHECK(scMsg.decode(scFrame, static_cast<void*>(&scOut)));
CHECK(scOut.selfCheck == BAT_SC_START && scOut.selfCheckReset == BAT_SCR_RESET);
// 设备控制指令
BatControlValue v;
v.year = 2026; v.month = 8; v.day = 30; v.hour = 11; v.minute = 20; v.second = 33;
v.ms10 = 4;
v.workCondition = BAT_WC_SEA_TRIAL;
v.busContactor = BAT_CTR_CLOSE;
v.posContactor = BAT_CTR_OPEN;
v.diodeContactor = BAT_CTR_CLOSE;
v.powerKw = 350;
v.reserved = 0x1234;
v.heartbeat = 66;
BatControlMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 27);
CHECK(frame[2] == 0x01 && frame[3] == 0x00); // id 0x0001 小端
CHECK(frame[6 + 8] == BAT_WC_SEA_TRIAL); // 工况
CHECK(frame[6 + 9] == BAT_CTR_CLOSE); // 母线接触器
CHECK(frame[6 + 10] == BAT_CTR_OPEN); // 正极接触器
CHECK(frame[6 + 11] == BAT_CTR_CLOSE); // 二极管接触器
CHECK(frame[6 + 12] == 0x5E && frame[6 + 13] == 0x01); // 350 = 0x015E 小端
CHECK(frame[6 + 14] == 0x34 && frame[6 + 15] == 0x12); // 预留 0x1234 小端
CHECK(frame[6 + 16] == 66); // 心跳
BatControlValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.year == 2026 && out.month == 8 && out.day == 30);
CHECK(out.hour == 11 && out.minute == 20 && out.second == 33 && out.ms10 == 4);
CHECK(out.workCondition == BAT_WC_SEA_TRIAL);
CHECK(out.busContactor == BAT_CTR_CLOSE);
CHECK(out.posContactor == BAT_CTR_OPEN);
CHECK(out.diodeContactor == BAT_CTR_CLOSE);
CHECK(out.powerKw == 350);
CHECK(out.reserved == 0x1234);
CHECK(out.heartbeat == 66);
}
//--------------------------------------------------------------------------
// 11. 锂电池电池组状态(0x0003)编解码往返(关键字段抽查)
static void testBatStatusRoundTrip() {
BatStatusValue v;
v.year = 2026; v.month = 3; v.day = 24; v.hour = 10; v.minute = 0; v.second = 1;
v.ms10 = 9;
v.workCondition = BAT_WC_WORKSHOP;
v.selfCheckState = BAT_SCST_OK;
v.powerCfgState = 0x22;
v.busContactorState = BAT_BUSST_CLOSED;
v.posDiodeState = 0x32; // 二极管闭合完成(3?) 高4位=3 断开完成; 低4位=2 正极闭合完成
v.emergencyState = 0x1C;
v.soc = 855; // 85.5%
v.energy = 1234; // 123.4 kWh
v.maxDischargePower = 5000; // 500.0 kW
v.maxChargePower = 2000;
v.currentAccessPower = 1500; // 150.0 kW
v.packOnlineFlag = 0x00FFFFFF;
v.packAccessFlag = 0x000000FF;
v.relayPosCharge = 0x12;
v.relayPreNeg = 0x11;
v.voltageLoad = 5400; // 54.00 V
v.voltagePack = 5500;
v.current = -200; // -10 A(充电)
v.insulationPos = 50; // 500 kΩ
v.insulationNeg = 60;
v.masterTemp1 = 1; v.masterTemp2 = 2;
v.packVoltageMid = 540; // 54.0 V
v.packVoltageMin = 535;
v.packVoltageMinNo = 3;
v.packVoltageMax = 545;
v.packVoltageMaxNo = 7;
v.packDeltaMax = 200; // 20.0 mV
v.packDeltaMaxNo = 5;
v.packDeltaMaxVoltage = 5450;
v.cellTempAvg = 65; // 25 ℃
v.cellTempMin = 55;
v.cellTempMinNo = 2;
v.cellTempMax = 70;
v.cellTempMaxNo = 4;
v.alarmFlag[0] = 0x42; v.alarmFlag[5] = 0xC1;
v.alarmPosFlag = 0x00FF00AA;
v.chargeStatus = 0x20;
v.reserved = 0x5A5A;
v.heartbeat = 88;
BatStatusMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 90);
CHECK(frame[2] == 0x03 && frame[3] == 0x00); // id 0x0003
CHECK(frame[4] == 0x5A && frame[5] == 0x00); // 域字节数=整包长度 90=0x005A
CHECK(frame[6 + 9] == BAT_SCST_OK); // 自检状态 offset9
CHECK(frame[6 + 11] == BAT_BUSST_CLOSED); // 母线接触器状态 offset11
CHECK(frame[6 + 12] == 0x32); // 正极/二极管 offset12
CHECK(frame[6 + 14] == 0x57 && frame[6 + 15] == 0x03); // SOC 855=0x0357 小端
CHECK(frame[6 + 40] == 0x38 && frame[6 + 41] == 0xFF); // current -200=0xFF38 小端
CHECK(frame[6 + 79] == 88); // 心跳 offset79
BatStatusValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.year == 2026 && out.month == 3 && out.day == 24);
CHECK(out.selfCheckState == BAT_SCST_OK);
CHECK(out.powerCfgState == 0x22);
CHECK(out.busContactorState == BAT_BUSST_CLOSED);
CHECK(out.posDiodeState == 0x32);
CHECK(batPosStateOf(out.posDiodeState) == BAT_POSST_CLOSED);
CHECK(out.emergencyState == 0x1C);
CHECK(out.soc == 855 && out.energy == 1234);
CHECK(out.maxDischargePower == 5000 && out.maxChargePower == 2000);
CHECK(out.currentAccessPower == 1500);
CHECK(out.packOnlineFlag == 0x00FFFFFF && out.packAccessFlag == 0x000000FF);
CHECK(out.relayPosCharge == 0x12 && out.relayPreNeg == 0x11);
CHECK(out.voltageLoad == 5400 && out.voltagePack == 5500);
CHECK(out.current == -200);
CHECK(out.insulationPos == 50 && out.insulationNeg == 60);
CHECK(out.packVoltageMid == 540 && out.packVoltageMin == 535 && out.packVoltageMinNo == 3);
CHECK(out.packVoltageMax == 545 && out.packVoltageMaxNo == 7);
CHECK(out.packDeltaMax == 200 && out.packDeltaMaxNo == 5 && out.packDeltaMaxVoltage == 5450);
CHECK(out.cellTempAvg == 65 && out.cellTempMax == 70 && out.cellTempMaxNo == 4);
CHECK(out.alarmFlag[0] == 0x42 && out.alarmFlag[5] == 0xC1);
CHECK(out.alarmPosFlag == 0x00FF00AA);
CHECK(out.chargeStatus == 0x20 && out.reserved == 0x5A5A);
CHECK(out.heartbeat == 88);
}
//--------------------------------------------------------------------------
// 12. 锂电池电池包报警(0x0004)编解码往返
static void testBatPackAlarmRoundTrip() {
BatPackAlarmValue v;
v.year = 2026; v.month = 3; v.day = 24; v.hour = 9; v.minute = 1; v.second = 2;
v.ms10 = 5;
v.packNo = 12;
v.alarmFlag[0] = 0x45; v.alarmFlag[3] = 0x82;
v.soc = 700; // 70.0%
v.relayPosNeg = 0x12; // 正极接通 + 负极断开
v.voltageLoad = 530; // 53.0 V
v.voltagePack = 528;
v.current = 400; // 20 A(放电)
v.insulationPos = 100;
v.insulationNeg = 110;
v.tempMaxNo = 6; v.tempMax1 = 75;
v.tempMin1 = 50;
v.tempAvg = 62;
v.balanceState = 1;
v.voltMaxNo = 9; v.voltMax1 = 41; // 4.1 mV*10 => 0.1mV 精度
v.voltMin1 = 33;
v.voltMinNo = 11;
v.voltAvg = 38;
BatPackAlarmMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 78);
CHECK(frame[2] == 0x04 && frame[3] == 0x00); // id 0x0004
CHECK(frame[6 + 8] == 12); // 电池包号
CHECK(frame[6 + 9] == 0x45); // 报警标识字1
CHECK(frame[6 + 17] == 0x12); // 正极/负极继电器
CHECK(frame[6 + 66] == 0x26 && frame[6 + 67] == 0x00); // 平均电压 38=0x0026 小端
BatPackAlarmValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.packNo == 12);
CHECK(out.alarmFlag[0] == 0x45 && out.alarmFlag[3] == 0x82);
CHECK(out.soc == 700);
CHECK(out.relayPosNeg == 0x12);
CHECK(out.voltageLoad == 530 && out.voltagePack == 528);
CHECK(out.current == 400);
CHECK(out.insulationPos == 100 && out.insulationNeg == 110);
CHECK(out.tempMaxNo == 6 && out.tempMax1 == 75);
CHECK(out.tempMin1 == 50 && out.tempAvg == 62);
CHECK(out.balanceState == 1);
CHECK(out.voltMaxNo == 9 && out.voltMax1 == 41);
CHECK(out.voltMin1 == 33 && out.voltMinNo == 11);
CHECK(out.voltAvg == 38);
}
//--------------------------------------------------------------------------
// 13. 锂电池链路分发与注册表隔离
static void testBatLinkDispatch() {
MessageRegistry batReg;
registerBatMessages(batReg);
CHECK(batReg.size() == 4);
CHECK(batReg.find(0x0000) != nullptr);
CHECK(batReg.find(0x0001) != nullptr);
CHECK(batReg.find(0x0003) != nullptr);
CHECK(batReg.find(0x0004) != nullptr);
// 锂电池链路收帧:0x0003 状态解码
BatStatusValue v;
v.soc = 999; v.heartbeat = 21;
BatStatusMessage bsm;
std::vector<uint8_t> frame = bsm.encode(v);
BatLinkManager blm(BatRole::Dyn, 0, "192.168.100.137", 7000);
CHECK(blm.linkName() == "bat_dyn");
bool got = false;
blm.setOnMessage([&](Message* m, const std::vector<uint8_t>& f) {
if (m && m->id() == 0x0003) {
BatStatusValue out;
if (m->decode(f, static_cast<void*>(&out))) {
got = (out.soc == 999 && out.heartbeat == 21);
}
}
});
CHECK(blm.handleIncoming(frame));
CHECK(got);
// 校验失败帧拒收
std::vector<uint8_t> bad = frame;
bad[10] ^= 0xFF;
CHECK(!blm.handleIncoming(bad));
}
//--------------------------------------------------------------------------
int main() {
testFrameLengths();
@@ -297,6 +540,11 @@ int main() {
testChecksum();
testLinkDispatch();
testDbStore();
testBatFrameLengths();
testBatControlRoundTrip();
testBatStatusRoundTrip();
testBatPackAlarmRoundTrip();
testBatLinkDispatch();
std::printf("\n==== pccuTest: %d passed, %d failed ====\n", g_pass, g_fail);
return g_fail == 0 ? 0 : 1;
+85 -8
View File
@@ -4,15 +4,21 @@
拓扑:
测试脚本(FC模拟) --16000--> pCCU(fc_local) [FC状态 0x0002]
测试脚本(PM模拟) --16002--> pCCU(pm_local) [PM操控 0x0001, PM参数设定 0x0002]
测试脚本(电池模拟) --17001/17002--> pCCU [电池组状态 0x0003]
pCCU --16001--> 测试脚本 [FC控制 0x0001 转发]
pCCU --16003--> 测试脚本 [PM状态 0x0004, PM参数反馈 0x0003]
pCCU --17003/17004--> 测试脚本 [电池控制指令 0x0001 / 自检指令 0x0000]
校验:
1. FC 状态收到并整合,pCCU 周期发送 PM 状态(0x0004, 248B)到 16003
2. PM 操控(0x0001)转发为 FC 控制(0x0001, 24B)到 16001
3. PM 参数设定(0x0002)触发参数反馈(0x0003, 12B)到 16003
4. SQLite 记录了收/发原始帧
5. Web 页面可访问
4. PM 操控中锂电池启停指令映射为电池控制指令(0x0001, 27B):
动力 0x20 关闭 -> 母线接触器 0x77;仪表 0x10 启动 -> 母线接触器 0x55
5. pCCU 周期(1s)向动力/仪表锂电池下发心跳控制指令
6. 电池组状态(0x0003)收到后落库 SQLite(link=bat_dyn/bat_ins)
7. SQLite 记录了收/发原始帧
8. Web 页面可访问
"""
import socket
import struct
@@ -29,6 +35,10 @@ FC_STATUS_PORT = 16000
FC_CTRL_RECV_PORT = 16001
PM_CTRL_PORT = 16002
PM_STATUS_RECV_PORT = 16003
DYN_BAT_PORT = 17001 # pCCU 监听动力电池状态
INS_BAT_PORT = 17002 # pCCU 监听仪表电池状态
DYN_BAT_RECV_PORT = 17003 # 测试监听(pCCU 发往"动力电池"的目标端口)
INS_BAT_RECV_PORT = 17004 # 测试监听(pCCU 发往"仪表电池"的目标端口)
WEB_PORT = 18081
DB_PATH = "/tmp/pccu_it_test.db"
MOOSDB_EXE = "/usr/local/bin/MOOSDB"
@@ -94,6 +104,26 @@ def pm_paramset_frame():
return frame(0x0002, bytes(p))
def bat_status_frame():
"""电池组状态 0x0003:payload 80B,带 uint32 校验和"""
p = bytearray(80)
struct.pack_into("<H", p, 0, 2026)
p[2], p[3], p[4], p[5], p[6], p[7] = 8, 30, 10, 30, 0, 0
p[8] = 0x10 # 工况 工房调试
p[9] = 0x22 # 自检完成,状态正常
p[10] = 0x22 # 满足功率要求
p[11] = 0x11 # 母线接触器闭合完成
p[12] = 0x22 # 二极管闭合完成(高4位) + 正极闭合完成(低4位)
struct.pack_into("<H", p, 14, 855) # SOC 85.5%
struct.pack_into("<H", p, 16, 1234) # 电量 123.4 kWh
struct.pack_into("<H", p, 18, 5000) # 允许最高放电功率 500 kW
struct.pack_into("<H", p, 22, 1500) # 当前接入功率 150 kW
struct.pack_into("<H", p, 36, 5400) # 负载端电压 54.00V
struct.pack_into("<h", p, 40, -200) # 放电电流 -10A(充电)
p[79] = 33 # 心跳
return frame(0x0003, bytes(p))
def recv_loop(sock, label, results, msg_ids, timeout):
"""持续接收指定 msg_id 的帧,记录到 results"""
sock.settimeout(timeout)
@@ -135,18 +165,27 @@ def main():
print("FAIL: pCCU exited early, code", pccu.returncode)
return 1
# 3. 启动接收监听(FC 控制转发 16001,PM 状态 16003)
# 3. 启动接收监听(FC 控制转发 16001,PM 状态 16003,电池控制 17003/17004)
fc_ctl_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fc_ctl_recv.bind(("127.0.0.1", FC_CTRL_RECV_PORT))
pm_status_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
pm_status_recv.bind(("127.0.0.1", PM_STATUS_RECV_PORT))
dyn_bat_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
dyn_bat_recv.bind(("127.0.0.1", DYN_BAT_RECV_PORT))
ins_bat_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ins_bat_recv.bind(("127.0.0.1", INS_BAT_RECV_PORT))
fc_ctl_results, pm_status_results = [], []
dyn_bat_results, ins_bat_results = [], []
t1 = threading.Thread(target=recv_loop,
args=(fc_ctl_recv, "fc-ctl", fc_ctl_results, {0x0001}, 12))
t2 = threading.Thread(target=recv_loop,
args=(pm_status_recv, "pm-status", pm_status_results, {0x0004, 0x0003}, 12))
t1.start(); t2.start()
t3 = threading.Thread(target=recv_loop,
args=(dyn_bat_recv, "bat-dyn", dyn_bat_results, {0x0000, 0x0001}, 12))
t4 = threading.Thread(target=recv_loop,
args=(ins_bat_recv, "bat-ins", ins_bat_results, {0x0000, 0x0001}, 12))
t1.start(); t2.start(); t3.start(); t4.start()
# 4. 发送 FC 状态(周期模拟)
fc_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -160,7 +199,14 @@ def main():
time.sleep(0.5)
pm_send.sendto(pm_paramset_frame(), ("127.0.0.1", PM_CTRL_PORT))
t1.join(); t2.join()
# 6. 模拟锂电池上发电池组状态(动力/仪表)
bat_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for _ in range(3):
bat_send.sendto(bat_status_frame(), ("127.0.0.1", DYN_BAT_PORT))
bat_send.sendto(bat_status_frame(), ("127.0.0.1", INS_BAT_PORT))
time.sleep(0.5)
t1.join(); t2.join(); t3.join(); t4.join()
ok = True
@@ -204,18 +250,49 @@ def main():
else:
ok = False
# 校验 4:数据库有记录
# 校验 4:锂电池控制指令(27B)。
# PM 操控映射:动力 0x20 关闭 -> 母线接触器 0x77;仪表 0x10 启动 -> 母线接触器 0x55
dyn_ctl = [d for d in dyn_bat_results if len(d) == 27 and d[2] == 0x01]
ins_ctl = [d for d in ins_bat_results if len(d) == 27 and d[2] == 0x01]
print(f"[动力电池控制指令] 收到 {len(dyn_ctl)} 帧 (期望>=5, 每帧27B, 1s心跳)")
if dyn_ctl:
bus = dyn_ctl[-1][6 + 9] # 最后一帧:PM 指令生效后的期望状态
print(f" -> 母线接触器=0x{bus:02X} (期望0x77 关闭)")
ok = ok and (bus == 0x77)
else:
ok = False
print(f"[仪表电池控制指令] 收到 {len(ins_ctl)} 帧 (期望>=5, 每帧27B, 1s心跳)")
if ins_ctl:
bus = ins_ctl[-1][6 + 9]
print(f" -> 母线接触器=0x{bus:02X} (期望0x55 接通)")
ok = ok and (bus == 0x55)
else:
ok = False
# 校验 5:数据库有记录(含锂电池链路收发)
time.sleep(1.0)
db_count = -1
bat_dyn_rx = -1
bat_tx = -1
if os.path.exists(DB_PATH):
import sqlite3
conn = sqlite3.connect(DB_PATH)
db_count = conn.execute("SELECT COUNT(*) FROM comm_log").fetchone()[0]
# 电池组状态接收(link=bat_dyn, 方向=收, msg_id=3)
bat_dyn_rx = conn.execute(
"SELECT COUNT(*) FROM comm_log WHERE link='bat_dyn' AND direction=0 AND msg_id=3"
).fetchone()[0]
# 电池控制指令发送(direction=1, msg_id=1, bat 链路)
bat_tx = conn.execute(
"SELECT COUNT(*) FROM comm_log WHERE link IN ('bat_dyn','bat_ins') AND direction=1 AND msg_id=1"
).fetchone()[0]
conn.close()
print(f"[数据库] comm_log 记录数 = {db_count}")
print(f"[数据库] comm_log 记录数 = {db_count} (期望>=6)")
print(f"[数据库] 动力电池状态接收记录 = {bat_dyn_rx} (期望>=1), 电池控制发送记录 = {bat_tx} (期望>=2)")
ok = ok and (db_count >= 6) # 3条FC状态 + 1条PM操控 + 1条PM参数 + 周期PM状态(至少1)
ok = ok and (bat_dyn_rx >= 1) and (bat_tx >= 2)
# 校验 5:Web 页面
# 校验 6:Web 页面
web_ok = False
try:
resp = urllib.request.urlopen(f"http://127.0.0.1:{WEB_PORT}/", timeout=3)
+8
View File
@@ -16,6 +16,14 @@ ProcessConfig = pCCU
pm_remote_ip = 127.0.0.1
pm_remote_port = 16003 // 控制主机(测试监听此端口收状态报文)
// 锂电池链路(测试端口)
dyn_bat_local_port = 17001 // 监听动力电池状态
dyn_bat_remote_ip = 127.0.0.1
dyn_bat_remote_port = 17003 // 动力电池(测试监听此端口收控制/心跳指令)
ins_bat_local_port = 17002 // 监听仪表电池状态
ins_bat_remote_ip = 127.0.0.1
ins_bat_remote_port = 17004 // 仪表电池(测试监听此端口收控制/心跳指令)
dbpath = /tmp/pccu_it_test.db
logpath = /tmp/pccu_it_test.log
+5 -1
View File
@@ -82,11 +82,15 @@ def main():
print("FAIL: 快照非合法JSON:", text[:200])
return 1
ok = True
for key in ("fc", "pmCmd", "links", "fcStatusCount", "pmControlCount", "logs"):
for key in ("fc", "pmCmd", "links", "fcStatusCount", "pmControlCount", "logs",
"batDyn", "batIns", "batDynAlarm", "batInsAlarm",
"batDynStatusCount", "batInsStatusCount"):
ok = ok and key in data
ok = ok and ("batDyn" in data.get("links", {})) and ("batIns" in data.get("links", {}))
print(f"PASS: 快照 JSON 有效, 分组keys={sorted(data.keys())}")
print(f" fc.fc_status={data['fc']['fc_status']}, fc.fc_mode={data['fc']['fc_mode']}, "
f"heartbeat={data['fc']['heartbeat']}")
print(f" batDyn.soc={data['batDyn'].get('soc')}, batIns.soc={data['batIns'].get('soc')}")
print(f" links={json.dumps(data['links'], ensure_ascii=False)}")
print(f" logs条数={len(data['logs'])}, fcStatusCount={data['fcStatusCount']}")
ws.s.close()