2026-08-27 调试完成版本

- FC 控制指令域修正:payload 14→18 字节,纵倾/横倾姿态拆分为数据1/数据2
- WebServer 广播改为待发队列模式,修复 mongoose 跨线程调用问题
- build-board.sh 增加板卡组网说明(/23 掩码方案)、pCCU 服务部署与 mission 同步
- 同步 0824 协议文档更新
This commit is contained in:
zjk
2026-08-27 21:51:36 +08:00
parent 54c32a180a
commit c8becd1f6c
13 changed files with 162 additions and 67 deletions
+46 -23
View File
@@ -12,24 +12,45 @@ void registerFcMessages(MessageRegistry& reg) {
//============================================================================
// 0x0001 控制指令域编解码
// 数据域 14 字节,偏移 0~13(对应文档字节 7~20)
// 数据域 18 字节(字节7~24),偏移 0~17;整包 24 字节
//============================================================================
namespace {
// 数据域内偏移(相对数据域起点,offset = 文档字节号 - 7)
enum : size_t {
CO_MODE = 0, // 模式设定 (字节7)
CO_CMD = 1, // 操控指令 (字节8)
CO_OUT_POWER = 2, // 输出功率指令 (字节9)
CO_PITCH1 = 3, // 纵倾姿态数据1 int16 (字节10,11)
CO_PITCH2 = 5, // 纵倾姿态数据2 int16 (字节12,13)
CO_ROLL1 = 7, // 横倾姿态数据1 int16 (字节14,15)
CO_ROLL2 = 9, // 横倾姿态数据2 int16 (字节16,17)
CO_EMER_ALLOW = 11, // 应急允许 (字节18)
CO_DEPTH = 12, // 潜深深度 u16 (字节19,20)
CO_SUPPLY_CMD = 14, // 补给/排放指令 (字节21)
CO_RESERVED5 = 15, // 预留指令5 (字节22)
CO_RESERVED6 = 16, // 预留指令6 (字节23)
CO_HEARTBEAT = 17, // 通信心跳 (字节24)
};
} // namespace
std::vector<uint8_t> FcControlMessage::encode(const void* obj) const {
const FcControlValue* v = static_cast<const FcControlValue*>(obj);
std::vector<uint8_t> payload(payloadLength(), 0);
FieldCodec::putU8(payload, 0, v->mode);
FieldCodec::putU8(payload, 1, v->cmd);
FieldCodec::putU8(payload, 2, v->outputPower);
FieldCodec::putU16(payload, 3, static_cast<uint16_t>(v->pitch)); // 数据1=低位 / 数据2=高位
FieldCodec::putU16(payload, 5, static_cast<uint16_t>(v->roll));
FieldCodec::putU8(payload, 7, v->emergencyAllow);
FieldCodec::putU16(payload, 8, v->depth);
FieldCodec::putU8(payload, 10, v->supplyCmd);
FieldCodec::putU8(payload, 11, v->reservedCmd5);
FieldCodec::putU8(payload, 12, v->reservedCmd6);
FieldCodec::putU8(payload, 13, v->heartbeat);
FieldCodec::putU8(payload, CO_MODE, v->mode);
FieldCodec::putU8(payload, CO_CMD, v->cmd);
FieldCodec::putU8(payload, CO_OUT_POWER, v->outputPower);
FieldCodec::putU16(payload, CO_PITCH1, static_cast<uint16_t>(v->pitch1));
FieldCodec::putU16(payload, CO_PITCH2, static_cast<uint16_t>(v->pitch2));
FieldCodec::putU16(payload, CO_ROLL1, static_cast<uint16_t>(v->roll1));
FieldCodec::putU16(payload, CO_ROLL2, static_cast<uint16_t>(v->roll2));
FieldCodec::putU8(payload, CO_EMER_ALLOW, v->emergencyAllow);
FieldCodec::putU16(payload, CO_DEPTH, v->depth);
FieldCodec::putU8(payload, CO_SUPPLY_CMD, v->supplyCmd);
FieldCodec::putU8(payload, CO_RESERVED5, v->reservedCmd5);
FieldCodec::putU8(payload, CO_RESERVED6, v->reservedCmd6);
FieldCodec::putU8(payload, CO_HEARTBEAT, v->heartbeat);
return Frame::build(id(), payload, checksum());
}
@@ -39,17 +60,19 @@ bool FcControlMessage::decode(const std::vector<uint8_t>& frame, void* obj) cons
FcControlValue* v = static_cast<FcControlValue*>(obj);
size_t o = FRAME_HEADER_LEN; // 数据域起点
v->mode = FieldCodec::getU8(frame, o + 0);
v->cmd = FieldCodec::getU8(frame, o + 1);
v->outputPower = FieldCodec::getU8(frame, o + 2);
v->pitch = static_cast<int16_t>(FieldCodec::getU16(frame, o + 3));
v->roll = static_cast<int16_t>(FieldCodec::getU16(frame, o + 5));
v->emergencyAllow = FieldCodec::getU8(frame, o + 7);
v->depth = FieldCodec::getU16(frame, o + 8);
v->supplyCmd = FieldCodec::getU8(frame, o + 10);
v->reservedCmd5 = FieldCodec::getU8(frame, o + 11);
v->reservedCmd6 = FieldCodec::getU8(frame, o + 12);
v->heartbeat = FieldCodec::getU8(frame, o + 13);
v->mode = FieldCodec::getU8(frame, o + CO_MODE);
v->cmd = FieldCodec::getU8(frame, o + CO_CMD);
v->outputPower = FieldCodec::getU8(frame, o + CO_OUT_POWER);
v->pitch1 = static_cast<int16_t>(FieldCodec::getU16(frame, o + CO_PITCH1));
v->pitch2 = static_cast<int16_t>(FieldCodec::getU16(frame, o + CO_PITCH2));
v->roll1 = static_cast<int16_t>(FieldCodec::getU16(frame, o + CO_ROLL1));
v->roll2 = static_cast<int16_t>(FieldCodec::getU16(frame, o + CO_ROLL2));
v->emergencyAllow = FieldCodec::getU8(frame, o + CO_EMER_ALLOW);
v->depth = FieldCodec::getU16(frame, o + CO_DEPTH);
v->supplyCmd = FieldCodec::getU8(frame, o + CO_SUPPLY_CMD);
v->reservedCmd5 = FieldCodec::getU8(frame, o + CO_RESERVED5);
v->reservedCmd6 = FieldCodec::getU8(frame, o + CO_RESERVED6);
v->heartbeat = FieldCodec::getU8(frame, o + CO_HEARTBEAT);
return true;
}
+12 -9
View File
@@ -28,14 +28,16 @@ struct FcControlValue {
uint8_t mode = 0; // 模式设定 00~03
uint8_t cmd = 0; // 操控指令 00~0B
uint8_t outputPower = 0; // 输出功率指令 0~250,分辨率0.1kW
int16_t pitch = 0; // 纵倾姿态(数据1=低8位 / 数据2=高8位,int16),分辨率0.1°
int16_t roll = 0; // 横倾姿态(数据1=低8位 / 数据2=高8位,int16),分辨率0.1°
uint8_t emergencyAllow = 0; // 应急允许:Bit0 允许降载 / Bit1 允许排气
uint16_t depth = 0; // 潜深深度,单位 m
uint8_t supplyCmd = 0; // 补给/排放指令 00~0B
uint8_t reservedCmd5 = 0; // 预留指令5
uint8_t reservedCmd6 = 0; // 预留指令6
uint8_t heartbeat = 0; // 通信心跳,每次+1
int16_t pitch1 = 0; // 纵倾姿态数据1 int16,分辨率0.1°(字节10,11)
int16_t pitch2 = 0; // 纵倾姿态数据2 int16,分辨率0.1°(字节12,13)
int16_t roll1 = 0; // 横倾姿态数据1 int16,分辨率0.1°(字节14,15)
int16_t roll2 = 0; // 横倾姿态数据2 int16,分辨率0.1°(字节16,17)
uint8_t emergencyAllow = 0; // 应急允许:Bit0 允许降载 / Bit1 允许排气(字节18)
uint16_t depth = 0; // 潜深深度,单位 m(字节19,20)
uint8_t supplyCmd = 0; // 补给/排放指令 00~0B(字节21)
uint8_t reservedCmd5 = 0; // 预留指令5(字节22)
uint8_t reservedCmd6 = 0; // 预留指令6(字节23)
uint8_t heartbeat = 0; // 通信心跳,每次+1(字节24)
};
//--------------------------------------------------------------------------
@@ -132,7 +134,8 @@ public:
uint16_t id() const override { return 0x0001; }
const char* name() const override { return "fc_control"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 14; }
// 1.1.1 控制指令域:数据域 18 字节(字节7~24),整包 24 字节(域字节数 0x0018)
size_t payloadLength() const override { return 18; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
+3 -1
View File
@@ -12,7 +12,9 @@ bool Message::validateFrame(const std::vector<uint8_t>& frame,
FrameHeader h;
if (!Frame::parseHeader(frame, h)) return false;
if (h.id != expectId) return false;
if (h.length != expectTotalLen) return false;
// 协议规定域字节数=整个包长度;联调发现 FC 设备该字段填 120 而非 150,
// 与文档不符但不影响数据域布局,若实际包体足够容纳数据域则放行
if (h.length != expectTotalLen && frame.size() < expectTotalLen) return false;
// 校验和(长度不足由 verify 内部判断)
if (!policy.verify(frame)) return false;