Skip to content

Semantic Bus Pattern

> Aligned with PCR Master Blueprint v1.0 — see Blueprint §1.1(Bus 视角)、§7.1(Bus 裁定)、§3.2(FCC ↔ Bus 契约)。 > 职责:Bus 是 FCC ↔ Devices 之间的语义信道,采用 algebra + interpreter 模式:上层只看 BusMessage(语义),底层 interpreter 决定是否走 1553B / TTE / Semantic-passthrough。 > C-Distillation note:interpreter 的 vtable 在蒸馏后退化为函数指针表;SIL 路径下的 SemanticInterpreter 完全 inline 掉。

> Architectural Note (SIL vs HIL): > 主仿真路径优先用 Pure Delay Queue(Semantic Pass-Through)——不做协议序列化,零开销。 > 重 interpreter(1553B / TTE)仅作为 HIL 与硬件接驳时启用。

1. 核心概念

Bus 是语义解释器,把协议无关的 BusMessage 翻译成具体协议帧。带来三个性质:

  1. 协议无关:FCC / Device step 函数只看 BusMessage,与 1553B / TTE / 内存信道无关
  2. SIL ↔ HIL 平滑迁移:换 interpreter 不动控制逻辑
  3. 可测试:Mock interpreter 用于单元测试,真实 interpreter 用于硬件集成

与 Free Monad 模式的对称性

纯描述解释器执行
FCCFree<FccOp, A>FccInterpreterRWS<FccEnv, ...>
BusBusMessageBusInterpreterFrame<P>

Bus 是 FCC algebraic interpretation 模式在通信层的对称结构。


2. 当前代码实现(与 Blueprint 略有差异)

实际代码位于 src/bus/

cpp
// src/bus/IBus.h
namespace pcr::bus {   // ⚠ namespace 仍为 pcr::bus;Blueprint 用 bus::
                       //    设备 step 函数用 forward decl `namespace bus { class IBus; }`
                       //    待统一为单一 namespace `bus::`

struct BusMessage {
    uint32_t          src;            // 源地址(IMU_1_ADDR / FCC_MAIN_ADDR / …)
    uint32_t          dst;            // 目的地址
    Time              emitted_at;     // 仿真时戳
    BusMessagePayload payload;        // variant 类型,见下
};

class IBus {
public:
    virtual ~IBus() = default;
    virtual void                     publish(BusMessage msg)             = 0;
    virtual std::vector<BusMessage>  poll(uint32_t local_addr)           = 0;
    virtual Time                     now() const                          = 0;
};
}

BusMessagePayload 是 strong-typed variant,覆盖 4 类核心消息:

cpp
// src/bus/BusPayloads.h
using BusMessagePayload = std::variant<
    ImuPayload,    // IMU  -> FCC: { delta_v_body, delta_theta_body }
    GpsPayload,    // GPS  -> FCC: { pos_ecf, vel_ecf }
    ScuPayload,    // FCC  -> SCU: { engine gimbal commands }
    IcuPayload     // FCC  -> ICU: { Cmd { IgniteEngines / ShutdownEngines / FireSeparationBolt } + targets }
>;

> Blueprint §7.1 裁定:"vtable + 强类型 payload variant。删 BusChannel.h + SignalTag。" 当前代码尚有 BusChannel.h / TransparentBus.h 等遗留文件,将在 Wave 1 清理。


3. Algebra:BusMessage(语义层)

BusMessage 是协议无关的语义数据包。代码中已实现为 pcr::bus::BusMessage,4 种 payload 通过 std::variant 标签。

未来扩展的语义消息(如 FCC ↔ FinCtrl、ICU 自身发往 FCC 的离散事件回执)按相同形态添加 payload struct + 加入 variant alternates。


4. 协议帧(蓝图扩展,HIL 阶段实装)

每个物理协议有自己的帧结构。当前 SIL 路径下未实装,作为 HIL roadmap 保留。

4.1 MIL-STD-1553B

cpp
struct Frame1553B {
    struct CommandWord {
        uint16_t rt_address : 5;
        uint16_t tx_rx      : 1;
        uint16_t sub_addr   : 5;
        uint16_t word_count : 5;
    };
    struct StatusWord {
        uint16_t rt_address  : 5;
        uint16_t msg_error   : 1;
        // … 其余位见标准
    };
    CommandWord                command;
    std::vector<uint16_t>      data_words;   // 最大 32 字
    StatusWord                 status;
    std::optional<PhysicalAttrs> physical;   // 仅物理仿真时使用
};

4.2 TTE(Time-Triggered Ethernet)

cpp
struct FrameTTE {
    struct Header {
        uint32_t integration_cycle;
        uint16_t partition_id;
        uint16_t message_id;
        uint64_t sync_timestamp;
    };
    Header                       header;
    std::vector<uint8_t>         payload;
    Time                         scheduled_arrival;
    Time                         actual_arrival;
    std::optional<PhysicalAttrs> physical;
};

4.3 Semantic Pass-Through(SIL 默认)

cpp
struct FrameSemantic {
    BusMessage msg;   // 直接透传,零序列化
};

5. Interpreter 接口

cpp
template <typename Frame>
class BusInterpreter {
public:
    virtual ~BusInterpreter() = default;
    virtual Frame                                 encode(const BusMessage&) const = 0;
    virtual std::expected<BusMessage, BusError>   decode(const Frame&) const      = 0;
    virtual Frame apply_physical_effects(const Frame& f, const PhysicalConfig&) const {
        return f; // 默认 no-op
    }
};

struct BusError {
    enum class Code { CRCFailure, Timeout, InvalidAddress, FrameDropped, ParityError };
    Code        code;
    std::string message;
};

6. 具体 Interpreter

6.1 SemanticInterpreter(SIL 默认)

cpp
class SemanticInterpreter : public BusInterpreter<FrameSemantic> {
public:
    FrameSemantic encode(const BusMessage& m) const override { return {m}; }
    std::expected<BusMessage, BusError> decode(const FrameSemantic& f) const override {
        return f.msg;
    }
};

零开销,是 SIL 默认。

6.2 1553BInterpreter(HIL 用)

cpp
class Bus1553BInterpreter : public BusInterpreter<Frame1553B> {
    std::map<uint32_t /*device addr*/, std::pair<uint8_t, uint8_t> /*RT, Subaddr*/> addr_map_;
    PhysicalConfig physical_cfg_;
public:
    Frame1553B encode(const BusMessage& m) const override {
        Frame1553B fr;
        std::visit([&](auto& p) {
            using P = std::decay_t<decltype(p)>;
            if constexpr (std::is_same_v<P, ImuPayload>) {
                fr.command.rt_address = addr_map_.at(m.src).first;
                fr.command.tx_rx      = 1;  // RT 向 BC 发送
                fr.command.word_count = 8;
                fr.data_words         = pack_imu_1553b(p);
            } else if constexpr (std::is_same_v<P, ScuPayload>) {
                fr.command.tx_rx      = 0;  // BC 写到 RT
                fr.data_words         = pack_scu_1553b(p);
            }
            // … 其余 payload
        }, m.payload);
        return fr;
    }
    std::expected<BusMessage, BusError> decode(const Frame1553B& f) const override {
        if (physical_cfg_.enabled && f.physical && !f.physical->crc_valid) {
            return std::unexpected(BusError{BusError::Code::CRCFailure, "CRC failed"});
        }
        // … 根据 rt_address 派发 unpack
    }
};

6.3 TTEInterpreter(HIL 用)

略,结构类似,关键是 scheduled_arrivalactual_arrival 之间的 jitter 检查。


7. 物理仿真(可选)

启用后,interpreter 在帧上注入时延 / 抖动 / 比特错误:

cpp
struct PhysicalConfig {
    bool enabled = false;

    struct {
        Time base_delay{Time::zero()};
        Time jitter{Time::zero()};
        std::optional<std::function<double()>> distribution;   // e.g. Gaussian
    } latency;

    struct {
        double bit_error_rate = 0.0;
        double drop_rate      = 0.0;
        bool   simulate_crc   = false;
    } errors;
};

apply_physical_effectsencode 后调用:

cpp
auto frame = interpreter_->encode(msg);
frame = interpreter_->apply_physical_effects(frame, physical_cfg_);

8. BusManager / TransparentBus

当前代码用 TransparentBus 作为 IBus 的简单内存实现(一个 FIFO 队列 per address)。它本身不是 interpreter——只是个邮局。Interpreter 模式将在 HIL 阶段引入 BusManager 持有 interpreter_transport_

SIL 路径下,TransparentBus 自身就够用:BusMessage 直接走变体,零编解码。

cpp
// 简化:SIL 路径下 main loop
sim::body_tick(body_env, body) 中:
    avionics::device::ecu::step(body.engines[i].ecu, body.engines[i].mech, *bus, cfg_ecu, dt);
    avionics::device::imu::step(body.imus[i], body.aux, *bus, cfg_imu, dt);
    // … 各设备 step 自己 publish / poll
    fcc::tick(... , bus_msgs_in, fcc_out);  // FCC 通过 BusManager 装解包 FccIn/FccOut Frame

9. FCC 与 Bus 的耦合(Blueprint §3.2)

FCC 不直接依赖 bus::IBus。它的输入是 FccInFrame,输出是 FccOutFrame。 组装 / 拆解发生在 sim::body_tick 内:

text
Bus.poll(fcc.addr) → [BusMessage]
                  → BusManager::decode_to_fcc_in    (variant unpack)
                  → FccInFrame
                  → fcc::tick()
                  → FccOutFrame
                  → BusManager::encode_from_fcc_out (variant pack)
                  → [BusMessage]
                  → Bus.publish(...)

这层装解包是 simulation 的责任,不是 FCC 的责任。FCC 与 Bus 协议的隔离由 simulation 强制保证。


10. 配置(YAML)

yaml
bus:
  protocol: "Semantic"   # "Semantic" | "1553B" | "TTE"

  # SIL 仿真常用:只模拟时延
  physical_simulation:
    enabled: true
    latency:
      base_delay_ms: 0.5
      jitter_ms: 0.1
    errors:
      bit_error_rate: 0.0
      drop_rate: 0.0
      simulate_crc: false

  # 仅 1553B 用得到
  address_map:
    IMU_Primary: { rt: 0x01, subaddr: 0x01 }
    Engine_1:    { rt: 0x10, subaddr: 0x02 }

11. 收益总结

属性实现机制
协议无关FCC / Device step 只见 BusMessage,从未触碰物理协议字段
SIL ↔ HIL 切换换 interpreter,FCC / Device 代码 0 改动
可测试Mock IBus + Mock Interpreter 注入
物理精度可选PhysicalConfig.enabled 切位
类型安全std::variant<...Payload> 比 untagged buffer 更安全

12. 反模式

反模式为什么不行
在 device step 内手动序列化为 1553B 字破坏协议无关性;应交给 interpreter
FCC 内直接调用 IBus::publish违反 §3.2:FCC 只看 FccIn/FccOut Frame
把 BusMessage 的 payload 改为 void* / 裸 buffer失去 variant 类型安全
不同 Device 用不同 IBus 实现Bus 应该是单数;HIL 切换是替换整个 BusManager
在 SIL 路径下也跑 1553BInterpreter浪费算力,违反 §7.12 "PoC 优先简洁"

13. Cross References

  • Bus 在系统中的视角定位 → Blueprint §1.1 "Bus 视角"
  • Bus 与 FCC 的契约 → Blueprint §3.2
  • 当前 BusMessage / payload 定义 → src/bus/{IBus.h, BusPayloads.h}
  • 设备如何使用 IBus → Device_Dual_Face.md(同目录)§3.1
  • FCC Free Monad → 04_FCC/FCC_Algebraic_Interpretation.md
  • DynChannel(FCC↔Plant 边界)→ Blueprint §1.3