FC协议(0827 docx): - 控制指令域:字节12,13/16,17由纵倾/横倾姿态数据2改为预留(编码恒0、解码忽略) - 状态反馈域:字节147,148由预留12改为剩余发电量(MWh),贯通JSON/网页显示 - PM→CCU链路:姿态字段4xuchar改为2xshort(数据1),结构体大小与线上布局不变 - 同步更新:CCU转发映射、SnapshotBuilder、pCCU网页、fuelcell网页、SQLite列描述、单测/模拟器 - 修正集成测试遗留断言(20B→24B、心跳d[19]→d[23])并增强预留字节校验 CCU地址可配置: - pPowerManger 支持 moos 配置项 ccuhost/ccuport(默认127.0.0.1:7000) - build-board.sh 增加 --ccu-host/--ccu-port 参数 文档:FC协议文档更新为0827版,清理过期协议docx
This commit is contained in:
@@ -52,8 +52,8 @@ static void testFrameLengths() {
|
||||
// 2. FC 控制指令编解码往返
|
||||
static void testFcControlRoundTrip() {
|
||||
FcControlValue v;
|
||||
v.mode = 2; v.cmd = 2; v.outputPower = 100; v.pitch1 = 35; v.pitch2 = -21;
|
||||
v.roll1 = -12; v.roll2 = 48;
|
||||
v.mode = 2; v.cmd = 2; v.outputPower = 100; v.pitch1 = 35;
|
||||
v.roll1 = -12;
|
||||
v.emergencyAllow = 0x03; v.depth = 150;
|
||||
v.supplyCmd = 1; v.reservedCmd5 = 5; v.reservedCmd6 = 6; v.heartbeat = 7;
|
||||
|
||||
@@ -64,11 +64,11 @@ static void testFcControlRoundTrip() {
|
||||
CHECK(frame[0] == 0x40 && frame[1] == 0x40);
|
||||
CHECK(frame[4] == 0x18 && frame[5] == 0x00); // 域字节数 0x0018 小端
|
||||
|
||||
// 字节位置校验(数据域起点=帧头6):姿态各 int16 小端
|
||||
// 字节位置校验(数据域起点=帧头6):姿态数据1 int16 小端,数据2字节预留恒0
|
||||
CHECK(frame[6 + 3] == 0x23 && frame[6 + 4] == 0x00); // 纵倾数据1 = 35
|
||||
CHECK(frame[6 + 5] == 0xEB && frame[6 + 6] == 0xFF); // 纵倾数据2 = -21 (0xFFEB)
|
||||
CHECK(frame[6 + 5] == 0x00 && frame[6 + 6] == 0x00); // 预留(0827协议,原纵倾数据2)
|
||||
CHECK(frame[6 + 7] == 0xF4 && frame[6 + 8] == 0xFF); // 横倾数据1 = -12 (0xFFF4)
|
||||
CHECK(frame[6 + 9] == 0x30 && frame[6 + 10] == 0x00); // 横倾数据2 = 48
|
||||
CHECK(frame[6 + 9] == 0x00 && frame[6 + 10] == 0x00); // 预留(0827协议,原横倾数据2)
|
||||
|
||||
FcControlValue out;
|
||||
CHECK(msg.decode(frame, static_cast<void*>(&out)));
|
||||
@@ -76,9 +76,7 @@ static void testFcControlRoundTrip() {
|
||||
CHECK(out.cmd == 2);
|
||||
CHECK(out.outputPower == 100);
|
||||
CHECK(out.pitch1 == 35);
|
||||
CHECK(out.pitch2 == -21);
|
||||
CHECK(out.roll1 == -12);
|
||||
CHECK(out.roll2 == 48);
|
||||
CHECK(out.emergencyAllow == 0x03);
|
||||
CHECK(out.depth == 150);
|
||||
CHECK(out.supplyCmd == 1);
|
||||
@@ -101,7 +99,7 @@ static void testFcStatusRoundTrip() {
|
||||
v.methanol_total_use = 100;
|
||||
v.cabin_temp1 = 25; v.h2_concentration1 = 500;
|
||||
v.heartbeat = 9; v.emergency_cmd = 0x01;
|
||||
v.reserved[0] = 0xAAAA; v.reserved[11] = 0xBBBB;
|
||||
v.reserved[0] = 0xAAAA; v.remaining_generation = 0xBBBB;
|
||||
|
||||
FcStatusMessage msg;
|
||||
std::vector<uint8_t> frame = msg.encode(v);
|
||||
@@ -125,7 +123,7 @@ static void testFcStatusRoundTrip() {
|
||||
CHECK(out.methanol_total_use == 100);
|
||||
CHECK(out.cabin_temp1 == 25 && out.h2_concentration1 == 500);
|
||||
CHECK(out.heartbeat == 9 && out.emergency_cmd == 0x01);
|
||||
CHECK(out.reserved[0] == 0xAAAA && out.reserved[11] == 0xBBBB);
|
||||
CHECK(out.reserved[0] == 0xAAAA && out.remaining_generation == 0xBBBB);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
校验:
|
||||
1. FC 状态收到并整合,pCCU 周期发送 PM 状态(0x0004, 248B)到 16003
|
||||
2. PM 操控(0x0001)转发为 FC 控制(0x0001, 20B)到 16001
|
||||
2. PM 操控(0x0001)转发为 FC 控制(0x0001, 24B)到 16001
|
||||
3. PM 参数设定(0x0002)触发参数反馈(0x0003, 12B)到 16003
|
||||
4. SQLite 记录了收/发原始帧
|
||||
5. Web 页面可访问
|
||||
@@ -62,6 +62,7 @@ def fc_status_frame():
|
||||
p[17] = 70 # 液氧
|
||||
struct.pack_into("<H", p, 24, 350) # 钯膜温度
|
||||
struct.pack_into("<H", p, 68, 2500) # 舱室温度1
|
||||
struct.pack_into("<H", p, 140, 4321) # 剩余发电量 MWh (字节147,148)
|
||||
p[142] = 5 # 心跳
|
||||
p[143] = 0x01 # 应急指令
|
||||
return frame(0x0002, bytes(p), checksum=False)
|
||||
@@ -74,6 +75,8 @@ def pm_control_frame():
|
||||
p[8] = 2 # 模式 自动
|
||||
p[9] = 2 # 操控 启动
|
||||
p[10] = 120 # 输出功率 0.1kW
|
||||
struct.pack_into("<h", p, 11, 123) # 纵倾姿态数据1 12.3°
|
||||
struct.pack_into("<h", p, 13, -45) # 横倾姿态数据1 -4.5°
|
||||
p[15] = 0x03 # 应急允许
|
||||
struct.pack_into("<H", p, 16, 150) # 潜深
|
||||
p[21] = 0x10 # 仪表锂电启动
|
||||
@@ -173,14 +176,21 @@ def main():
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# 校验 2:PM 操控转发为 FC 控制(20B)
|
||||
fc_ctl = [d for d in fc_ctl_results if len(d) == 20]
|
||||
print(f"[FC 控制转发] 收到 {len(fc_ctl)} 帧 (期望>=1, 每帧20B)")
|
||||
# 校验 2:PM 操控转发为 FC 控制(24B,0827协议:姿态数据1转发,数据2字节预留恒0)
|
||||
fc_ctl = [d for d in fc_ctl_results if len(d) == 24]
|
||||
print(f"[FC 控制转发] 收到 {len(fc_ctl)} 帧 (期望>=1, 每帧24B)")
|
||||
if fc_ctl:
|
||||
d = fc_ctl[0]
|
||||
cmd = d[7]; power = d[8]; hb = d[19]
|
||||
print(f" -> cmd={cmd}, outputPower={power}, heartbeat={hb}")
|
||||
cmd = d[7]; power = d[8]; hb = d[23]
|
||||
pitch = struct.unpack_from("<h", d, 9)[0] # 字节10,11
|
||||
roll = struct.unpack_from("<h", d, 13)[0] # 字节14,15
|
||||
resv1 = struct.unpack_from("<H", d, 11)[0] # 字节12,13 预留
|
||||
resv2 = struct.unpack_from("<H", d, 15)[0] # 字节16,17 预留
|
||||
print(f" -> cmd={cmd}, outputPower={power}, pitch={pitch}, roll={roll}, "
|
||||
f"resv1={resv1}, resv2={resv2}, heartbeat={hb}")
|
||||
ok = ok and (cmd == 2) and (power == 120) and (hb == 7)
|
||||
ok = ok and (pitch == 123) and (roll == -45)
|
||||
ok = ok and (resv1 == 0) and (resv2 == 0)
|
||||
else:
|
||||
ok = False
|
||||
|
||||
|
||||
@@ -120,6 +120,8 @@ class FcSim:
|
||||
struct.pack_into("<H", p, 116, int(900 + 100 * math.sin(self.t / 4)))
|
||||
p[142] = self.heartbeat & 0xFF # 通信心跳 (字节149)
|
||||
p[143] = 0 # 应急指令 (字节150)
|
||||
# 剩余发电量 MWh (字节147-148, 0827协议由预留12改为):随发电缓慢消耗
|
||||
struct.pack_into("<H", p, 140, int(500 - self.gen_time / 360.0))
|
||||
return frame(0x0002, bytes(p), checksum=False)
|
||||
|
||||
def tick(self, dt):
|
||||
@@ -156,7 +158,7 @@ class PmSim:
|
||||
p[8] = 2 # 模式: 自动
|
||||
p[9] = 2 # 操控: 启动
|
||||
p[10] = self.power # 输出功率 (0.1kW)
|
||||
# 纵倾姿态 (数据1=低8位/数据2=高8位, int16, 0.1°) / 横倾姿态
|
||||
# 纵倾姿态数据1 / 横倾姿态数据1 (int16, 0.1°, 0827协议数据2字节预留)
|
||||
pitch = int((3.0 + 1.5 * math.sin(time.time() / 8)) * 10)
|
||||
roll = int((-1.0 + 0.5 * math.cos(time.time() / 6)) * 10)
|
||||
struct.pack_into("<h", p, 11, pitch)
|
||||
|
||||
Reference in New Issue
Block a user